diff --git a/src/PowerShellEditorServices.Hosting/Commands/StartEditorServicesCommand.cs b/src/PowerShellEditorServices.Hosting/Commands/StartEditorServicesCommand.cs index 69aa7b97f0..e74ca572cd 100644 --- a/src/PowerShellEditorServices.Hosting/Commands/StartEditorServicesCommand.cs +++ b/src/PowerShellEditorServices.Hosting/Commands/StartEditorServicesCommand.cs @@ -219,7 +219,6 @@ protected override void BeginProcessing() protected override void EndProcessing() { _logger.Log(PsesLogLevel.Diagnostic, "Beginning EndProcessing block"); - try { // First try to remove PSReadLine to decomplicate startup diff --git a/src/PowerShellEditorServices/Logging/HostLoggerAdapter.cs b/src/PowerShellEditorServices/Logging/HostLoggerAdapter.cs index 3290b2b780..0c10980ddf 100644 --- a/src/PowerShellEditorServices/Logging/HostLoggerAdapter.cs +++ b/src/PowerShellEditorServices/Logging/HostLoggerAdapter.cs @@ -9,23 +9,16 @@ namespace Microsoft.PowerShell.EditorServices.Logging /// /// Adapter class to allow logging events sent by the host to be recorded by PSES' logging infrastructure. /// - internal class HostLoggerAdapter : IObserver<(int logLevel, string message)> + internal class HostLoggerAdapter(ILogger logger) : IObserver<(int logLevel, string message)> { - private readonly ILogger _logger; + public void OnError(Exception error) => logger.LogError(error, "Error in host logger"); - /// - /// Create a new host logger adapter. - /// - /// Factory to create logger instances with. - public HostLoggerAdapter(ILoggerFactory loggerFactory) => _logger = loggerFactory.CreateLogger("HostLogs"); + public void OnNext((int logLevel, string message) value) => logger.Log((LogLevel)value.logLevel, value.message); public void OnCompleted() { // Nothing to do; we simply don't send more log messages } - public void OnError(Exception error) => _logger.LogError(error, "Error in host logger"); - - public void OnNext((int logLevel, string message) value) => _logger.Log((LogLevel)value.logLevel, value.message); } } diff --git a/src/PowerShellEditorServices/Server/PsesLanguageServer.cs b/src/PowerShellEditorServices/Server/PsesLanguageServer.cs index ea327603f6..b66d99fa48 100644 --- a/src/PowerShellEditorServices/Server/PsesLanguageServer.cs +++ b/src/PowerShellEditorServices/Server/PsesLanguageServer.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -8,11 +9,13 @@ using Microsoft.Extensions.Logging; using Microsoft.PowerShell.EditorServices.Handlers; using Microsoft.PowerShell.EditorServices.Hosting; +using Microsoft.PowerShell.EditorServices.Logging; using Microsoft.PowerShell.EditorServices.Services; using Microsoft.PowerShell.EditorServices.Services.Extension; using Microsoft.PowerShell.EditorServices.Services.PowerShell.Host; using Newtonsoft.Json.Linq; using OmniSharp.Extensions.JsonRpc; +using OmniSharp.Extensions.LanguageServer.Protocol.General; using OmniSharp.Extensions.LanguageServer.Protocol.Server; using OmniSharp.Extensions.LanguageServer.Server; @@ -26,7 +29,7 @@ namespace Microsoft.PowerShell.EditorServices.Server /// internal class PsesLanguageServer { - internal HostLogger LoggerFactory { get; } + internal HostLogger HostLogger { get; } internal ILanguageServer LanguageServer { get; private set; } private readonly LogLevel _minimumLogLevel; private readonly Stream _inputStream; @@ -34,6 +37,7 @@ internal class PsesLanguageServer private readonly HostStartupInfo _hostDetails; private readonly TaskCompletionSource _serverStart; private PsesInternalHost _psesHost; + private IDisposable hostLoggerSubscription; /// /// Create a new language server instance. @@ -43,18 +47,18 @@ internal class PsesLanguageServer /// cref="EditorServicesServerFactory.CreateLanguageServer"/>. It is essentially a /// singleton. The factory hides the logger. /// - /// Factory to create loggers with. + /// The host logger to hand off for monitoring. /// Protocol transport input stream. /// Protocol transport output stream. /// Host configuration to instantiate the server and services /// with. public PsesLanguageServer( - HostLogger factory, + HostLogger hostLogger, Stream inputStream, Stream outputStream, HostStartupInfo hostStartupInfo) { - LoggerFactory = factory; + HostLogger = hostLogger; _minimumLogLevel = (LogLevel)hostStartupInfo.LogLevel; _inputStream = inputStream; _outputStream = outputStream; @@ -86,9 +90,7 @@ public async Task StartAsync() .ConfigureLogging(builder => builder .ClearProviders() .AddLanguageProtocolLogging() - // TODO: AddHostLogger which registers the host logger provider above as a LoggingProvider (MEL version of a "sink") .SetMinimumLevel(_minimumLogLevel)) - // TODO: Consider replacing all WithHandler with AddSingleton .WithHandler() .WithHandler() .WithHandler() @@ -127,6 +129,11 @@ public async Task StartAsync() .OnInitialize( (languageServer, initializeParams, cancellationToken) => { + // Wire up the HostLogger to the LanguageServer's logger once we are initialized, so that any messages still logged to the HostLogger get sent across the LSP channel. There is no more logging to disk at this point. + hostLoggerSubscription = HostLogger.Subscribe(new HostLoggerAdapter( + languageServer.Services.GetService>() + )); + // Set the workspace path from the parameters. WorkspaceService workspaceService = languageServer.Services.GetService(); if (initializeParams.WorkspaceFolders is not null) @@ -161,7 +168,9 @@ public async Task StartAsync() _psesHost = languageServer.Services.GetService(); return _psesHost.TryStartAsync(hostStartOptions, cancellationToken); - }); + } + ) + .OnShutdown(_ => hostLoggerSubscription.Dispose()); }).ConfigureAwait(false); _serverStart.SetResult(true);