Skip to content

Commit 463cb33

Browse files
committed
Wire up loglevels in-band due to LSP bug
1 parent 0556b41 commit 463cb33

File tree

2 files changed

+62
-35
lines changed

2 files changed

+62
-35
lines changed

src/PowerShellEditorServices/Logging/LanguageServerLogger.cs

+61-34
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,15 @@ public void Log<TState>(
2626
Func<TState, Exception?, string> formatter
2727
)
2828
{
29+
if (responseRouter is null)
30+
{
31+
throw new InvalidOperationException("Log received without a valid responseRouter dependency. This is a bug, please report it.");
32+
}
2933
// Any Omnisharp or trace logs are directly LSP protocol related and we send them to the trace channel
3034
// TODO: Dynamically adjust if SetTrace is reported
31-
// BUG: There is an omnisharp filter incorrectly filtering this. As a workaround we will use logMessage.
35+
// BUG: There is an omnisharp filter incorrectly filtering this. As a workaround we will use logMessage for now.
3236
// https://github.com/OmniSharp/csharp-language-server-protocol/issues/1390
33-
// if (categoryName.StartsWith("OmniSharp") || logLevel == LogLevel.Trace)
37+
//
3438
// {
3539
// // Everything with omnisharp goes directly to trace
3640
// string eventMessage = string.Empty;
@@ -47,22 +51,54 @@ public void Log<TState>(
4751
// };
4852
// responseRouter.Client.LogTrace(trace);
4953
// }
50-
if (TryGetMessageType(logLevel, out MessageType messageType))
54+
55+
// Drop all omnisharp messages to trace. This isn't a MEL filter because it's specific only to this provider.
56+
if (categoryName.StartsWith("OmniSharp.", StringComparison.OrdinalIgnoreCase))
57+
{
58+
logLevel = LogLevel.Trace;
59+
}
60+
61+
(MessageType messageType, string messagePrepend) = GetMessageInfo(logLevel);
62+
63+
// The vscode-languageserver-node client doesn't support LogOutputChannel as of 2024-11-24 and also doesn't
64+
// provide a way to middleware the incoming log messages, so our output channel has no idea what the logLevel
65+
// is. As a workaround, we send the severity in-line with the message for the client to parse.
66+
// BUG: https://github.com/microsoft/vscode-languageserver-node/issues/1116
67+
if (responseRouter.Client?.ClientSettings?.ClientInfo?.Name == "Visual Studio Code")
5168
{
52-
LogMessageParams logMessage = new()
69+
messagePrepend = logLevel switch
5370
{
54-
Type = messageType,
55-
// TODO: Add Critical and Debug delineations
56-
Message = categoryName + ": " + formatter(state, exception) +
57-
(exception != null ? " - " + exception : "") + " | " +
58-
//Hopefully this isn't too expensive in the long run
59-
FormatState(state, exception)
71+
LogLevel.Critical => "<Error> CRITICAL: ",
72+
LogLevel.Error => "<Error>",
73+
LogLevel.Warning => "<Warning>",
74+
LogLevel.Information => "<Info>",
75+
LogLevel.Debug => "<Debug>",
76+
LogLevel.Trace => "<Trace>",
77+
_ => string.Empty
6078
};
61-
responseRouter.Window.Log(logMessage);
6279
}
63-
}
6480

81+
LogMessageParams logMessage = new()
82+
{
83+
Type = messageType,
84+
Message = messagePrepend + categoryName + ": " + formatter(state, exception) +
85+
(exception != null ? " - " + exception : "") + " | " +
86+
//Hopefully this isn't too expensive in the long run
87+
FormatState(state, exception)
88+
};
89+
responseRouter.Window.Log(logMessage);
90+
}
6591

92+
/// <summary>
93+
/// Formats the state object into a string for logging.
94+
/// </summary>
95+
/// <remarks>
96+
/// This is copied from Omnisharp, we can probably do better.
97+
/// </remarks>
98+
/// <typeparam name="TState"></typeparam>
99+
/// <param name="state"></param>
100+
/// <param name="exception"></param>
101+
/// <returns></returns>
66102
private static string FormatState<TState>(TState state, Exception? exception)
67103
{
68104
return state switch
@@ -72,29 +108,20 @@ private static string FormatState<TState>(TState state, Exception? exception)
72108
};
73109
}
74110

75-
private static bool TryGetMessageType(LogLevel logLevel, out MessageType messageType)
76-
{
77-
switch (logLevel)
111+
/// <summary>
112+
/// Maps MEL log levels to LSP message types
113+
/// </summary>
114+
private static (MessageType messageType, string messagePrepend) GetMessageInfo(LogLevel logLevel)
115+
=> logLevel switch
78116
{
79-
case LogLevel.Critical:
80-
case LogLevel.Error:
81-
messageType = MessageType.Error;
82-
return true;
83-
case LogLevel.Warning:
84-
messageType = MessageType.Warning;
85-
return true;
86-
case LogLevel.Information:
87-
messageType = MessageType.Info;
88-
return true;
89-
case LogLevel.Debug:
90-
case LogLevel.Trace:
91-
messageType = MessageType.Log;
92-
return true;
93-
}
94-
95-
messageType = MessageType.Log;
96-
return false;
97-
}
117+
LogLevel.Critical => (MessageType.Error, "Critical: "),
118+
LogLevel.Error => (MessageType.Error, string.Empty),
119+
LogLevel.Warning => (MessageType.Warning, string.Empty),
120+
LogLevel.Information => (MessageType.Info, string.Empty),
121+
LogLevel.Debug => (MessageType.Log, string.Empty),
122+
LogLevel.Trace => (MessageType.Log, "Trace: "),
123+
_ => throw new ArgumentOutOfRangeException(nameof(logLevel), logLevel, null)
124+
};
98125
}
99126

100127
internal class LanguageServerLoggerProvider(ILanguageServerFacade languageServer) : ILoggerProvider

src/PowerShellEditorServices/Server/PsesLanguageServer.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public async Task StartAsync()
8989
})
9090
.ConfigureLogging(builder => builder
9191
.ClearProviders()
92-
.AddLanguageProtocolLogging()
92+
.AddPsesLanguageServerLogging()
9393
.SetMinimumLevel(_minimumLogLevel))
9494
.WithHandler<PsesWorkspaceSymbolsHandler>()
9595
.WithHandler<PsesTextDocumentHandler>()

0 commit comments

Comments
 (0)