2
2
// Licensed under the MIT License.
3
3
4
4
using System ;
5
- using System . Diagnostics ;
6
5
using System . IO ;
7
6
using Microsoft . Extensions . DependencyInjection ;
8
7
using Microsoft . Extensions . Logging ;
9
- using Microsoft . PowerShell . EditorServices . Logging ;
10
8
using Microsoft . PowerShell . EditorServices . Server ;
11
- using Serilog ;
12
- using Serilog . Events ;
13
9
using OmniSharp . Extensions . LanguageServer . Protocol . Server ;
14
10
using Microsoft . PowerShell . EditorServices . Services . Extension ;
11
+ using OmniSharp . Extensions . LanguageServer . Server ;
15
12
16
- #if DEBUG
17
- using Serilog . Debugging ;
18
- #endif
13
+ // The HostLogger type isn't directly referenced from this assembly, however it uses a common IObservable interface and this alias helps make it more clear the purpose. We can use Microsoft.Extensions.Logging from this point because the ALC should be loaded, but we need to only expose the IObservable to the Hosting assembly so it doesn't try to load MEL before the ALC is ready.
14
+ using HostLogger = System . IObservable < ( int logLevel , string message ) > ;
19
15
20
16
namespace Microsoft . PowerShell . EditorServices . Hosting
21
17
{
22
18
/// <summary>
23
- /// Factory class for hiding dependencies of Editor Services .
19
+ /// Factory for creating the LSP server and debug server instances .
24
20
/// </summary>
25
- /// <remarks>
26
- /// Dependency injection and logging are wrapped by factory methods on this class so that the
27
- /// host assembly can construct the LSP and debug servers without directly depending on <see
28
- /// cref="Microsoft.Extensions.Logging"/> and <see
29
- /// cref="Microsoft.Extensions.DependencyInjection"/>.
30
- /// </remarks>
31
21
internal sealed class EditorServicesServerFactory : IDisposable
32
22
{
23
+ private readonly HostLogger _hostLogger ;
24
+
33
25
/// <summary>
34
- /// Create a new Editor Services factory. This method will instantiate logging.
26
+ /// Creates a loggerfactory for this instance
35
27
/// </summary>
36
- /// <remarks>
37
- /// <para>
38
- /// This can only be called once because it sets global state (the logger) and that call is
39
- /// in <see cref="Hosting.EditorServicesRunner" />.
40
- /// </para>
41
- /// <para>
42
- /// TODO: Why is this a static function wrapping a constructor instead of just a
43
- /// constructor? In the end it returns an instance (albeit a "singleton").
44
- /// </para>
45
- /// </remarks>
46
- /// <param name="logDirectoryPath">The path of the log file to use.</param>
47
- /// <param name="minimumLogLevel">The minimum log level to use.</param>
48
- /// <param name="hostLogger">The host logger?</param>
49
- public static EditorServicesServerFactory Create ( string logDirectoryPath , int minimumLogLevel , IObservable < ( int logLevel , string message ) > hostLogger )
50
- {
51
- // NOTE: Ignore the suggestion to use Environment.ProcessId as it doesn't work for
52
- // .NET 4.6.2 (for Windows PowerShell), and this won't be caught in CI.
53
- int currentPID = Process . GetCurrentProcess ( ) . Id ;
54
- string logPath = Path . Combine ( logDirectoryPath , $ "PowerShellEditorServices-{ currentPID } .log") ;
55
- Log . Logger = new LoggerConfiguration ( )
56
- . Enrich . FromLogContext ( )
57
- . WriteTo . Async ( config => config . File ( logPath ) )
58
- . MinimumLevel . Is ( ( LogEventLevel ) minimumLogLevel )
59
- . CreateLogger ( ) ;
60
-
61
- #if DEBUG
62
- SelfLog . Enable ( msg => Debug . WriteLine ( msg ) ) ;
63
- #endif
64
-
65
- LoggerFactory loggerFactory = new ( ) ;
66
- loggerFactory . AddSerilog ( ) ;
67
-
68
- // Hook up logging from the host so that its recorded in the log file
69
- hostLogger . Subscribe ( new HostLoggerAdapter ( loggerFactory ) ) ;
70
-
71
- return new EditorServicesServerFactory ( loggerFactory ) ;
72
- }
73
-
74
- // TODO: Can we somehow refactor this member so the language and debug servers can be
75
- // instantiated using their constructors instead of tying them to this factory with `Create`
76
- // methods?
77
- private readonly ILoggerFactory _loggerFactory ;
78
-
79
- private EditorServicesServerFactory ( ILoggerFactory loggerFactory ) => _loggerFactory = loggerFactory ;
28
+ /// <param name="hostLogger">The hostLogger that will be provided to the language services for logging handoff</param>
29
+ internal EditorServicesServerFactory ( HostLogger hostLogger ) => _hostLogger = hostLogger ;
80
30
81
31
/// <summary>
82
32
/// Create the LSP server.
@@ -92,7 +42,7 @@ public static EditorServicesServerFactory Create(string logDirectoryPath, int mi
92
42
public PsesLanguageServer CreateLanguageServer (
93
43
Stream inputStream ,
94
44
Stream outputStream ,
95
- HostStartupInfo hostStartupInfo ) => new ( _loggerFactory , inputStream , outputStream , hostStartupInfo ) ;
45
+ HostStartupInfo hostStartupInfo ) => new ( _hostLogger , inputStream , outputStream , hostStartupInfo ) ;
96
46
97
47
/// <summary>
98
48
/// Create the debug server given a language server instance.
@@ -110,7 +60,7 @@ public PsesDebugServer CreateDebugServerWithLanguageServer(
110
60
PsesLanguageServer languageServer )
111
61
{
112
62
return new PsesDebugServer (
113
- _loggerFactory ,
63
+ _hostLogger ,
114
64
inputStream ,
115
65
outputStream ,
116
66
languageServer . LanguageServer . Services ) ;
@@ -132,7 +82,7 @@ public PsesDebugServer RecreateDebugServer(
132
82
PsesDebugServer debugServer )
133
83
{
134
84
return new PsesDebugServer (
135
- _loggerFactory ,
85
+ _hostLogger ,
136
86
inputStream ,
137
87
outputStream ,
138
88
debugServer . ServiceProvider ) ;
@@ -153,7 +103,7 @@ public PsesDebugServer CreateDebugServerForTempSession(
153
103
ServiceProvider serviceProvider = new ServiceCollection ( )
154
104
. AddLogging ( builder => builder
155
105
. ClearProviders ( )
156
- . AddSerilog ( )
106
+ . AddLanguageProtocolLogging ( )
157
107
. SetMinimumLevel ( LogLevel . Trace ) ) // TODO: Why randomly set to trace?
158
108
. AddSingleton < ILanguageServerFacade > ( _ => null )
159
109
// TODO: Why add these for a debug server?!
@@ -171,25 +121,14 @@ public PsesDebugServer CreateDebugServerForTempSession(
171
121
serviceProvider . GetService < ExtensionService > ( ) ;
172
122
173
123
return new PsesDebugServer (
174
- _loggerFactory ,
124
+ _hostLogger ,
175
125
inputStream ,
176
126
outputStream ,
177
127
serviceProvider ,
178
128
isTemp : true ) ;
179
129
}
180
130
181
- /// <summary>
182
- /// TODO: This class probably should not be <see cref="IDisposable"/> as the primary
183
- /// intention of that interface is to provide cleanup of unmanaged resources, which the
184
- /// logger certainly is not. Nor is this class used with a <see langword="using"/>. Instead,
185
- /// this class should call <see cref="Log.CloseAndFlush()"/> in a finalizer. This
186
- /// could potentially even be done with <see
187
- /// cref="SerilogLoggerFactoryExtensions.AddSerilog"</> by passing <c>dispose=true</c>.
188
- /// </summary>
189
- public void Dispose ( )
190
- {
191
- Log . CloseAndFlush ( ) ;
192
- _loggerFactory . Dispose ( ) ;
193
- }
131
+ // TODO: Clean up host logger? Shouldn't matter since we start a new process after shutdown.
132
+ public void Dispose ( ) { }
194
133
}
195
134
}
0 commit comments