Allow logging level to be changed in rzls (#11228)

VS Code has a specific LogOutputChannel which allows the user to easily change the logging level on an output window. To support that rzls also needs to allow dynamic logging level changes. This adds an endpoint and a level provider that changes the logging level based on an lsp message coming in.
This commit is contained in:
Andrew Hall 2024-11-19 17:20:32 -08:00 коммит произвёл GitHub
Родитель 58984fe582
Коммит 7390745dcd
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
9 изменённых файлов: 75 добавлений и 19 удалений

Просмотреть файл

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Threading;
using Microsoft.CodeAnalysis.Razor.Logging;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Hosting.Logging;
internal class LogLevelProvider(LogLevel logLevel)
{
public LogLevel Current { get; set; } = logLevel;
}

Просмотреть файл

@ -0,0 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System.Text.Json.Serialization;
using Microsoft.CodeAnalysis.Razor.Logging;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Hosting.Logging;
/// <summary>
/// Request parameters for updating the log level in the server dynamically.
/// </summary>
/// <param name="LogLevel">the int value of the <see cref="LogLevel"/> enum</param>
internal record class UpdateLogLevelParams([property: JsonPropertyName("logLevel")] int LogLevel);

Просмотреть файл

@ -0,0 +1,24 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root for license information.
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.EndpointContracts;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting.Logging;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.CodeAnalysis.Razor.Protocol;
namespace Microsoft.AspNetCore.Razor.LanguageServer.Hosting.Logging;
[RazorLanguageServerEndpoint(CustomMessageNames.RazorUpdateLogLevelName)]
internal class UpdateLogLevelEndpoint(LogLevelProvider logLevelProvider) : IRazorNotificationHandler<UpdateLogLevelParams>
{
public bool MutatesSolutionState => false;
public Task HandleNotificationAsync(UpdateLogLevelParams request, RazorRequestContext requestContext, CancellationToken cancellationToken)
{
logLevelProvider.Current = (LogLevel)request.LogLevel;
return Task.CompletedTask;
}
}

Просмотреть файл

@ -5,11 +5,11 @@ namespace Microsoft.CodeAnalysis.Razor.Logging;
internal enum LogLevel
{
Trace,
Debug,
Information,
Warning,
Error,
Critical,
None
None = 0,
Trace = 1,
Debug = 2,
Information = 3,
Warning = 4,
Error = 5,
Critical = 6,
}

Просмотреть файл

@ -27,6 +27,7 @@ internal static class CustomMessageNames
// VS Code only
public const string RazorNamedPipeConnectEndpointName = "razor/namedPipeConnect";
public const string RazorUpdateLogLevelName = "razor/updateLogLevel";
// VS Windows and VS Code
public const string RazorUpdateCSharpBufferEndpoint = "razor/updateCSharpBuffer";

Просмотреть файл

@ -2,17 +2,15 @@
// Licensed under the MIT license. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting.Logging;
using Microsoft.CodeAnalysis.Razor.Logging;
namespace Microsoft.AspNetCore.Razor.LanguageServer;
internal class LoggerProvider(LogLevel logLevel, IClientConnection clientConnection) : ILoggerProvider
internal class LoggerProvider(LogLevelProvider logLevelProvider, IClientConnection clientConnection) : ILoggerProvider
{
private readonly LogLevel _logLevel = logLevel;
private readonly IClientConnection _clientConnection = clientConnection;
public ILogger CreateLogger(string categoryName)
{
return new LspLogger(categoryName, _logLevel, _clientConnection);
return new LspLogger(categoryName, logLevelProvider, clientConnection);
}
}

Просмотреть файл

@ -4,6 +4,7 @@
using System;
using System.Threading;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting.Logging;
using Microsoft.CodeAnalysis.Razor.Logging;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Microsoft.VisualStudio.Threading;
@ -13,15 +14,15 @@ namespace Microsoft.AspNetCore.Razor.LanguageServer;
/// <summary>
/// ILogger implementation that logs via the window/logMessage LSP method
/// </summary>
internal class LspLogger(string categoryName, LogLevel logLevel, IClientConnection clientConnection) : ILogger
internal class LspLogger(string categoryName, LogLevelProvider logLevelProvider, IClientConnection clientConnection) : ILogger
{
private readonly LogLevel _logLevel = logLevel;
private LogLevel LogLevel => logLevelProvider.Current;
private readonly string _categoryName = categoryName;
private readonly IClientConnection _clientConnection = clientConnection;
public bool IsEnabled(LogLevel logLevel)
{
return logLevel.IsAtLeast(_logLevel);
return logLevel.IsAtLeast(LogLevel);
}
public void Log(LogLevel logLevel, string message, Exception? exception)

Просмотреть файл

@ -7,6 +7,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting.Logging;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting.NamedPipes;
using Microsoft.AspNetCore.Razor.Telemetry;
using Microsoft.AspNetCore.Razor.Utilities;
@ -83,6 +84,7 @@ public class Program
// Have to create a logger factory to give to the server, but can't create any logger providers until we have
// a server.
var loggerFactory = new LoggerFactory([]);
var logLevelProvider = new LogLevelProvider(logLevel);
using var host = RazorLanguageServerHost.Create(
Console.OpenStandardInput(),
@ -90,15 +92,18 @@ public class Program
loggerFactory,
telemetryContext?.TelemetryReporter ?? NoOpTelemetryReporter.Instance,
featureOptions: languageServerFeatureOptions,
configureServices: static services =>
configureServices: services =>
{
services.AddSingleton<IRazorProjectInfoDriver, NamedPipeBasedRazorProjectInfoDriver>();
services.AddHandler<RazorNamedPipeConnectEndpoint>();
services.AddSingleton(logLevelProvider);
services.AddHandler<UpdateLogLevelEndpoint>();
});
// Now we have a server, and hence a connection, we have somewhere to log
var clientConnection = host.GetRequiredService<IClientConnection>();
var loggerProvider = new LoggerProvider(logLevel, clientConnection);
var loggerProvider = new LoggerProvider(logLevelProvider, clientConnection);
loggerFactory.AddLoggerProvider(loggerProvider);
loggerFactory.GetOrCreateLogger("RZLS").LogInformation($"Razor Language Server started successfully.");

Просмотреть файл

@ -8,8 +8,8 @@ using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.LanguageServer.Extensions;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting.Logging;
using Microsoft.AspNetCore.Razor.LanguageServer.Hosting.NamedPipes;
using Microsoft.AspNetCore.Razor.ProjectSystem;
using Microsoft.AspNetCore.Razor.Telemetry;
@ -124,8 +124,10 @@ public class RazorLanguageServerTest(ITestOutputHelper testOutput) : ToolingTest
{
s.AddSingleton<IRazorProjectInfoDriver, TestProjectInfoDriver>();
// VS Code only handler is added by rzls, but add here for testing purposes
// VS Code only handlers are added by rzls, but add here for testing purposes
s.AddHandler<RazorNamedPipeConnectEndpoint>();
s.AddSingleton(new LogLevelProvider(CodeAnalysis.Razor.Logging.LogLevel.None));
s.AddHandler<UpdateLogLevelEndpoint>();
});
}