@@ -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
100127internal class LanguageServerLoggerProvider ( ILanguageServerFacade languageServer ) : ILoggerProvider
0 commit comments