@@ -26,11 +26,15 @@ public void Log<TState>(
26
26
Func < TState , Exception ? , string > formatter
27
27
)
28
28
{
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
+ }
29
33
// Any Omnisharp or trace logs are directly LSP protocol related and we send them to the trace channel
30
34
// 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 .
32
36
// https://github.com/OmniSharp/csharp-language-server-protocol/issues/1390
33
- // if (categoryName.StartsWith("OmniSharp") || logLevel == LogLevel.Trace)
37
+ //
34
38
// {
35
39
// // Everything with omnisharp goes directly to trace
36
40
// string eventMessage = string.Empty;
@@ -47,22 +51,54 @@ public void Log<TState>(
47
51
// };
48
52
// responseRouter.Client.LogTrace(trace);
49
53
// }
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" )
51
68
{
52
- LogMessageParams logMessage = new ( )
69
+ messagePrepend = logLevel switch
53
70
{
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
60
78
} ;
61
- responseRouter . Window . Log ( logMessage ) ;
62
79
}
63
- }
64
80
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
+ }
65
91
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>
66
102
private static string FormatState < TState > ( TState state , Exception ? exception )
67
103
{
68
104
return state switch
@@ -72,29 +108,20 @@ private static string FormatState<TState>(TState state, Exception? exception)
72
108
} ;
73
109
}
74
110
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
78
116
{
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
+ } ;
98
125
}
99
126
100
127
internal class LanguageServerLoggerProvider ( ILanguageServerFacade languageServer ) : ILoggerProvider
0 commit comments