File tree

3 files changed

+97
-80
lines changed

3 files changed

+97
-80
lines changed
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,51 @@
1212
namespace Microsoft.PowerShell.EditorServices.Hosting
1313
{
1414
/// <summary>
15-
/// User-facing log level for editor services configuration.
15+
/// Log Level for HostLogger. This is a direct copy of LogLevel from Microsoft.Extensions.Logging, and will map to
16+
/// MEL.LogLevel once MEL is bootstrapped, but we don't want to load any MEL assemblies until the Assembly Load
17+
/// Context is set up.
1618
/// </summary>
17-
/// <remarks>
18-
/// The underlying values of this enum attempt to align to
19-
/// <see cref="Microsoft.Extensions.Logging.LogLevel" />
20-
/// </remarks>
2119
public enum PsesLogLevel
2220
{
23-
Diagnostic = 0,
24-
Verbose = 1,
25-
Normal = 2,
21+
/// <summary>
22+
/// Logs that contain the most detailed messages. These messages may contain sensitive application data.
23+
/// These messages are disabled by default and should never be enabled in a production environment.
24+
/// </summary>
25+
Trace = 0,
26+
27+
/// <summary>
28+
/// Logs that are used for interactive investigation during development. These logs should primarily contain
29+
/// information useful for debugging and have no long-term value.
30+
/// </summary>
31+
Debug = 1,
32+
33+
/// <summary>
34+
/// Logs that track the general flow of the application. These logs should have long-term value.
35+
/// </summary>
36+
Information = 2,
37+
38+
/// <summary>
39+
/// Logs that highlight an abnormal or unexpected event in the application flow, but do not otherwise cause the
40+
/// application execution to stop.
41+
/// </summary>
2642
Warning = 3,
43+
44+
/// <summary>
45+
/// Logs that highlight when the current flow of execution is stopped due to a failure. These should indicate a
46+
/// failure in the current activity, not an application-wide failure.
47+
/// </summary>
2748
Error = 4,
28-
None = 5
49+
50+
/// <summary>
51+
/// Logs that describe an unrecoverable application or system crash, or a catastrophic failure that requires
52+
/// immediate attention.
53+
/// </summary>
54+
Critical = 5,
55+
56+
/// <summary>
57+
/// Not used for writing log messages. Specifies that a logging category should not write any messages.
58+
/// </summary>
59+
None = 6,
2960
}
3061

3162
/// <summary>
@@ -180,15 +211,9 @@ public void LogException(
180211
/// Since it's likely that the process will end when PSES shuts down,
181212
/// there's no good reason to need objects rather than writing directly to the host.
182213
/// </remarks>
183-
internal class PSHostLogger : IObserver<(PsesLogLevel logLevel, string message)>
214+
/// <param name="ui">The PowerShell host user interface object to log output to.</param>
215+
internal class PSHostLogger(PSHostUserInterface ui) : IObserver<(PsesLogLevel logLevel, string message)>
184216
{
185-
private readonly PSHostUserInterface _ui;
186-
187-
/// <summary>
188-
/// Create a new PowerShell host logger.
189-
/// </summary>
190-
/// <param name="ui">The PowerShell host user interface object to log output to.</param>
191-
public PSHostLogger(PSHostUserInterface ui) => _ui = ui;
192217

193218
public void OnCompleted()
194219
{
@@ -200,35 +225,37 @@ public void OnCompleted()
200225

201226
public void OnNext((PsesLogLevel logLevel, string message) value)
202227
{
203-
switch (value.logLevel)
228+
(PsesLogLevel logLevel, string message) = value;
229+
switch (logLevel)
204230
{
205-
case PsesLogLevel.Diagnostic:
206-
_ui.WriteDebugLine(value.message);
207-
return;
208-
209-
case PsesLogLevel.Verbose:
210-
_ui.WriteVerboseLine(value.message);
211-
return;
231+
case PsesLogLevel.Trace:
232+
case PsesLogLevel.Debug:
233+
ui.WriteDebugLine(message);
234+
break;
212235

213-
case PsesLogLevel.Normal:
214-
_ui.WriteLine(value.message);
215-
return;
236+
case PsesLogLevel.Information:
237+
ui.WriteVerboseLine(message);
238+
break;
216239

217240
case PsesLogLevel.Warning:
218-
_ui.WriteWarningLine(value.message);
219-
return;
241+
ui.WriteWarningLine(message);
242+
break;
220243

221244
case PsesLogLevel.Error:
222-
_ui.WriteErrorLine(value.message);
223-
return;
245+
case PsesLogLevel.Critical:
246+
ui.WriteErrorLine(message);
247+
break;
224248

225249
default:
226-
_ui.WriteLine(value.message);
227-
return;
250+
ui.WriteLine(message);
251+
break;
228252
}
229253
}
230254
}
231255

256+
/// <summary>
257+
/// A simple log sink that logs to a stream, typically used to log to a file.
258+
/// </summary>
232259
internal class StreamLogger : IObserver<(PsesLogLevel logLevel, string message)>, IDisposable
233260
{
234261
public static StreamLogger CreateWithNewFile(string path)
@@ -283,9 +310,7 @@ public void OnCompleted()
283310
}
284311

285312
_cancellationSource.Cancel();
286-
287313
_writerThread.Join();
288-
289314
_unsubscriber.Dispose();
290315
_fileWriter.Flush();
291316
_fileWriter.Close();
@@ -298,29 +323,17 @@ public void OnCompleted()
298323

299324
public void OnNext((PsesLogLevel logLevel, string message) value)
300325
{
301-
string message = null;
302-
switch (value.logLevel)
326+
string message = value.logLevel switch
303327
{
304-
case PsesLogLevel.Diagnostic:
305-
message = $"[DBG]: {value.message}";
306-
break;
307-
308-
case PsesLogLevel.Verbose:
309-
message = $"[VRB]: {value.message}";
310-
break;
311-
312-
case PsesLogLevel.Normal:
313-
message = $"[INF]: {value.message}";
314-
break;
315-
316-
case PsesLogLevel.Warning:
317-
message = $"[WRN]: {value.message}";
318-
break;
319-
320-
case PsesLogLevel.Error:
321-
message = $"[ERR]: {value.message}";
322-
break;
323-
}
328+
// String interpolation often considered a logging sin is OK here because our filtering happens before.
329+
PsesLogLevel.Trace => $"[TRC]: {value.message}",
330+
PsesLogLevel.Debug => $"[DBG]: {value.message}",
331+
PsesLogLevel.Information => $"[INF]: {value.message}",
332+
PsesLogLevel.Warning => $"[WRN]: {value.message}",
333+
PsesLogLevel.Error => $"[ERR]: {value.message}",
334+
PsesLogLevel.Critical => $"[CRT]: {value.message}",
335+
_ => value.message,
336+
};
324337

325338
_messageQueue.Add(message);
326339
}
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ internal class HostLoggerAdapter(ILogger logger) : IObserver<(int logLevel, stri
1313
{
1414
public void OnError(Exception error) => logger.LogError(error, "Error in host logger");
1515

16+
/// <summary>
17+
/// Log the message received from the host into MEL.
18+
/// </summary>
1619
public void OnNext((int logLevel, string message) value) => logger.Log((LogLevel)value.logLevel, value.message);
1720

1821
public void OnCompleted()
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
using Microsoft.Extensions.Logging;
1111
using Microsoft.Extensions.Options;
1212
using Newtonsoft.Json;
13-
using OmniSharp.Extensions.LanguageServer.Protocol.Client;
1413
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
1514
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
1615
using OmniSharp.Extensions.LanguageServer.Protocol.Window;
@@ -29,33 +28,35 @@ public void Log<TState>(
2928
{
3029
// Any Omnisharp or trace logs are directly LSP protocol related and we send them to the trace channel
3130
// TODO: Dynamically adjust if SetTrace is reported
32-
if (categoryName.StartsWith("OmniSharp") || logLevel == LogLevel.Trace)
33-
{
34-
// Everything with omnisharp goes directly to trace
35-
string eventMessage = string.Empty;
36-
string exceptionName = exception?.GetType().Name ?? string.Empty;
37-
if (eventId.Name is not null)
38-
{
39-
eventMessage = eventId.Id == 0 ? eventId.Name : $"{eventId.Name} [{eventId.Id}] ";
40-
}
41-
42-
LogTraceParams trace = new()
43-
{
44-
Message = categoryName + ": " + eventMessage + exceptionName,
45-
Verbose = formatter(state, exception)
46-
};
47-
responseRouter.Client.LogTrace(trace);
48-
}
49-
else if (TryGetMessageType(logLevel, out MessageType messageType))
31+
// BUG: There is an omnisharp filter incorrectly filtering this. As a workaround we will use logMessage.
32+
// https://.com/OmniSharp/csharp-language-server-protocol/issues/1390
33+
// if (categoryName.StartsWith("OmniSharp") || logLevel == LogLevel.Trace)
34+
// {
35+
// // Everything with omnisharp goes directly to trace
36+
// string eventMessage = string.Empty;
37+
// string exceptionName = exception?.GetType().Name ?? string.Empty;
38+
// if (eventId.Name is not null)
39+
// {
40+
// eventMessage = eventId.Id == 0 ? eventId.Name : $"{eventId.Name} [{eventId.Id}] ";
41+
// }
42+
43+
// LogTraceParams trace = new()
44+
// {
45+
// Message = categoryName + ": " + eventMessage + exceptionName,
46+
// Verbose = formatter(state, exception)
47+
// };
48+
// responseRouter.Client.LogTrace(trace);
49+
// }
50+
if (TryGetMessageType(logLevel, out MessageType messageType))
5051
{
5152
LogMessageParams logMessage = new()
5253
{
5354
Type = messageType,
5455
// TODO: Add Critical and Debug delineations
5556
Message = categoryName + ": " + formatter(state, exception) +
56-
(exception != null ? " - " + exception : "") + " | " +
57-
//Hopefully this isn't too expensive in the long run
58-
FormatState(state, exception)
57+
(exception != null ? " - " + exception : "") + " | " +
58+
//Hopefully this isn't too expensive in the long run
59+
FormatState(state, exception)
5960
};
6061
responseRouter.Window.Log(logMessage);
6162
}

0 commit comments

Comments
 (0)