Skip to content

Commit 71253d2

Browse files
JustinGroteandyleejordan
authored andcommitted
Add basic logger
1 parent 38dd8f0 commit 71253d2

File tree

1 file changed

+118
-11
lines changed

1 file changed

+118
-11
lines changed
Lines changed: 118 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,120 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
3+
#nullable enable
34

5+
using System;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using System.Reactive.Disposables;
49
using Microsoft.Extensions.DependencyInjection;
510
using Microsoft.Extensions.Logging;
611
using Microsoft.Extensions.Options;
12+
using Newtonsoft.Json;
13+
using OmniSharp.Extensions.LanguageServer.Protocol.Client;
14+
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
15+
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
16+
using OmniSharp.Extensions.LanguageServer.Protocol.Window;
717

818
namespace Microsoft.PowerShell.EditorServices.Logging;
9-
internal class DynamicLogLevelOptions(
10-
LogLevel initialLevel,
11-
IOptionsMonitor<LoggerFilterOptions> optionsMonitor) : IConfigureOptions<LoggerFilterOptions>
19+
20+
internal class LanguageServerLogger(ILanguageServerFacade responseRouter, string categoryName) : ILogger
1221
{
13-
private LogLevel _currentLevel = initialLevel;
14-
private readonly IOptionsMonitor<LoggerFilterOptions> _optionsMonitor = optionsMonitor;
22+
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => Disposable.Empty;
23+
public bool IsEnabled(LogLevel logLevel) => true;
1524

16-
public void Configure(LoggerFilterOptions options) => options.MinLevel = _currentLevel;
25+
public void Log<TState>(
26+
LogLevel logLevel, EventId eventId, TState state, Exception? exception,
27+
Func<TState, Exception?, string> formatter
28+
)
29+
{
30+
// Any Omnisharp or trace logs are directly LSP protocol related and we send them to the trace channel
31+
// 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+
}
1741

18-
public void SetLogLevel(LogLevel level)
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))
50+
{
51+
LogMessageParams logMessage = new()
52+
{
53+
Type = messageType,
54+
// TODO: Add Critical and Debug delineations
55+
Message = categoryName + ": " + formatter(state, exception) +
56+
(exception != null ? " - " + exception : "") + " | " +
57+
//Hopefully this isn't too expensive in the long run
58+
FormatState(state, exception)
59+
};
60+
responseRouter.Window.Log(logMessage);
61+
}
62+
}
63+
64+
65+
private static string FormatState<TState>(TState state, Exception? exception)
1966
{
20-
_currentLevel = level;
21-
// Trigger reload of options to apply new log level
22-
_optionsMonitor.CurrentValue.MinLevel = level;
67+
return state switch
68+
{
69+
IEnumerable<KeyValuePair<string, object>> dict => string.Join(" ", dict.Where(z => z.Key != "{OriginalFormat}").Select(z => $"{z.Key}='{z.Value}'")),
70+
_ => JsonConvert.SerializeObject(state).Replace("\"", "'")
71+
};
2372
}
73+
74+
private static bool TryGetMessageType(LogLevel logLevel, out MessageType messageType)
75+
{
76+
switch (logLevel)
77+
{
78+
case LogLevel.Critical:
79+
case LogLevel.Error:
80+
messageType = MessageType.Error;
81+
return true;
82+
case LogLevel.Warning:
83+
messageType = MessageType.Warning;
84+
return true;
85+
case LogLevel.Information:
86+
messageType = MessageType.Info;
87+
return true;
88+
case LogLevel.Debug:
89+
case LogLevel.Trace:
90+
messageType = MessageType.Log;
91+
return true;
92+
}
93+
94+
messageType = MessageType.Log;
95+
return false;
96+
}
97+
}
98+
99+
internal class LanguageServerLoggerProvider(ILanguageServerFacade languageServer) : ILoggerProvider
100+
{
101+
public ILogger CreateLogger(string categoryName) => new LanguageServerLogger(languageServer, categoryName);
102+
103+
public void Dispose() { }
24104
}
25105

26-
public static class LoggingBuilderExtensions
106+
107+
public static class LanguageServerLoggerExtensions
27108
{
109+
/// <summary>
110+
/// Adds a custom logger provider for PSES LSP, that provides more granular categorization than the default Omnisharp logger, such as separating Omnisharp and PSES messages to different channels.
111+
/// </summary>
112+
public static ILoggingBuilder AddPsesLanguageServerLogging(this ILoggingBuilder builder)
113+
{
114+
builder.Services.AddSingleton<ILoggerProvider, LanguageServerLoggerProvider>();
115+
return builder;
116+
}
117+
28118
public static ILoggingBuilder AddLspClientConfigurableMinimumLevel(
29119
this ILoggingBuilder builder,
30120
LogLevel initialLevel = LogLevel.Trace
@@ -38,7 +128,24 @@ public static ILoggingBuilder AddLspClientConfigurableMinimumLevel(
38128
});
39129
builder.Services.AddSingleton<IConfigureOptions<LoggerFilterOptions>>(sp =>
40130
sp.GetRequiredService<DynamicLogLevelOptions>());
131+
41132
return builder;
42133
}
43134
}
44135

136+
internal class DynamicLogLevelOptions(
137+
LogLevel initialLevel,
138+
IOptionsMonitor<LoggerFilterOptions> optionsMonitor) : IConfigureOptions<LoggerFilterOptions>
139+
{
140+
private LogLevel _currentLevel = initialLevel;
141+
private readonly IOptionsMonitor<LoggerFilterOptions> _optionsMonitor = optionsMonitor;
142+
143+
public void Configure(LoggerFilterOptions options) => options.MinLevel = _currentLevel;
144+
145+
public void SetLogLevel(LogLevel level)
146+
{
147+
_currentLevel = level;
148+
// Trigger reload of options to apply new log level
149+
_optionsMonitor.CurrentValue.MinLevel = level;
150+
}
151+
}

0 commit comments

Comments
 (0)