mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-01-30 17:24:37 -05:00
This change modifies the logging framework within sqltoolservice. Moves away from custom Logger object to start using .Net tracing framework. It supports for the static Trace and TraceSource way of logging. For all new code it is recommend that we log the log messages using the existing static Logger class, while the code changes will continue to route the older Trace.Write* calls from the process to same log listeners (and thus the log targets) as used by the Logger class. Thus tracing in SMO code that uses Trace.Write* methods gets routed to the same file as the messages from rest of SQLTools Service code. Make changes to start using .Net Frameworks codebase for all logging to unify our logging story. Allows parameter to set tracingLevel filters that controls what kinds of message make it to the log file. Allows a parameter to set a specific log file name so if these gets set by external code (the UI code using the tools service for example) then the external code is aware of the current log file in use. Adding unittests to test out the existing and improved logging capabilities. Sequences of checkins in development branch: * Saving v1 of logging to prepare for code review. Minor cleanup and some end to end testing still remains * Removing local launchSettings.json files * added support for lazy listener to sqltoolsloglistener and removed incorrect changes to comments across files in previous checkin * Converting time to local time when writing entries to the log * move the hosting.v2 to new .net based logging code * removing *.dgml files and addding them to .gitignore * fixing typo of defaultTraceSource * Addressing pull request feedback * Adding a test to verify logging from SMO codebase * propogating changes to v1 sqltools.hosting commandoptions.cs to the v2 version * Fixing comments on start and stop callstack methods and whitespaces * Commenting a test that got uncommented by mistake * addding .gitattributes file as .sql file was observed to be misconstrued as a binary file
212 lines
7.4 KiB
C#
212 lines
7.4 KiB
C#
//
|
|
// Copyright (c) Microsoft. All rights reserved.
|
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
//
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.SqlTools.DataProtocol.Contracts;
|
|
using Microsoft.SqlTools.DataProtocol.Contracts.ServerCapabilities;
|
|
using Microsoft.SqlTools.Hosting.Channels;
|
|
using Microsoft.SqlTools.Hosting.Contracts;
|
|
using Microsoft.SqlTools.Hosting.Contracts.Internal;
|
|
using Microsoft.SqlTools.Hosting.Protocol;
|
|
using Microsoft.SqlTools.Hosting.Utility;
|
|
|
|
namespace Microsoft.SqlTools.Hosting
|
|
{
|
|
public class ServiceHost : IServiceHost
|
|
{
|
|
private const int DefaultShutdownTimeoutSeconds = 120;
|
|
|
|
#region Fields
|
|
|
|
private int? shutdownTimeoutSeconds;
|
|
|
|
internal readonly List<Func<InitializeParameters, IEventSender, Task>> initCallbacks;
|
|
internal readonly List<Func<object, IEventSender, Task>> shutdownCallbacks;
|
|
internal IJsonRpcHost jsonRpcHost;
|
|
|
|
#endregion
|
|
|
|
#region Construction
|
|
|
|
/// <summary>
|
|
/// Base constructor
|
|
/// </summary>
|
|
internal ServiceHost()
|
|
{
|
|
shutdownCallbacks = new List<Func<object, IEventSender, Task>>();
|
|
initCallbacks = new List<Func<InitializeParameters, IEventSender, Task>>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructs a new service host that with ability to provide custom protocol channels
|
|
/// </summary>
|
|
/// <param name="protocolChannel">Channel to use for JSON RPC input/output</param>
|
|
public ServiceHost(ChannelBase protocolChannel)
|
|
: this()
|
|
{
|
|
Validate.IsNotNull(nameof(protocolChannel), protocolChannel);
|
|
jsonRpcHost = new JsonRpcHost(protocolChannel);
|
|
|
|
// Register any request that the service host will handle
|
|
SetEventHandler(ExitNotification.Type, HandleExitNotification, true);
|
|
SetAsyncRequestHandler(ShutdownRequest.Type, HandleShutdownRequest, true);
|
|
SetAsyncRequestHandler(InitializeRequest.Type, HandleInitializeRequest, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructs a new service host intended to be used as a JSON RPC server. StdIn is used
|
|
/// for receiving messages, StdOut is used for sending messages.
|
|
/// </summary>
|
|
/// <returns>Service host as a JSON RPC server over StdI/O</returns>
|
|
public static ServiceHost CreateDefaultServer()
|
|
{
|
|
return new ServiceHost(new StdioServerChannel());
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
public int ShutdownTimeoutSeconds
|
|
{
|
|
get => shutdownTimeoutSeconds ?? DefaultShutdownTimeoutSeconds;
|
|
set => shutdownTimeoutSeconds = value;
|
|
}
|
|
|
|
public InitializeResponse InitializeResponse { get; set; }
|
|
|
|
#endregion
|
|
|
|
#region IServiceHost Implementations
|
|
|
|
public void RegisterShutdownTask(Func<object, IEventSender, Task> shutdownCallback)
|
|
{
|
|
Validate.IsNotNull(nameof(shutdownCallback), shutdownCallback);
|
|
shutdownCallbacks.Add(shutdownCallback);
|
|
}
|
|
|
|
public void RegisterInitializeTask(Func<InitializeParameters, IEventSender, Task> initializeCallback)
|
|
{
|
|
Validate.IsNotNull(nameof(initializeCallback), initializeCallback);
|
|
initCallbacks.Add(initializeCallback);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IJsonRpcHost Implementation
|
|
|
|
public void SendEvent<TParams>(
|
|
EventType<TParams> eventType,
|
|
TParams eventParams)
|
|
{
|
|
jsonRpcHost.SendEvent(eventType, eventParams);
|
|
}
|
|
|
|
public Task<TResult> SendRequest<TParams, TResult>(
|
|
RequestType<TParams, TResult> requestType,
|
|
TParams requestParams)
|
|
{
|
|
return jsonRpcHost.SendRequest(requestType, requestParams);
|
|
}
|
|
|
|
public void SetAsyncEventHandler<TParams>(
|
|
EventType<TParams> eventType,
|
|
Func<TParams, EventContext, Task> eventHandler,
|
|
bool overrideExisting)
|
|
{
|
|
jsonRpcHost.SetAsyncEventHandler(eventType, eventHandler, overrideExisting);
|
|
}
|
|
|
|
public void SetEventHandler<TParams>(
|
|
EventType<TParams> eventType,
|
|
Action<TParams, EventContext> eventHandler,
|
|
bool overrideExisting)
|
|
{
|
|
jsonRpcHost.SetEventHandler(eventType, eventHandler, overrideExisting);
|
|
}
|
|
|
|
public void SetAsyncRequestHandler<TParams, TResult>(
|
|
RequestType<TParams, TResult> requestType,
|
|
Func<TParams, RequestContext<TResult>, Task> requestHandler,
|
|
bool overrideExisting)
|
|
{
|
|
jsonRpcHost.SetAsyncRequestHandler(requestType, requestHandler, overrideExisting);
|
|
}
|
|
|
|
public void SetRequestHandler<TParams, TResult>(
|
|
RequestType<TParams, TResult> requestType,
|
|
Action<TParams, RequestContext<TResult>> requestHandler,
|
|
bool overrideExisting)
|
|
{
|
|
jsonRpcHost.SetRequestHandler(requestType, requestHandler, overrideExisting);
|
|
}
|
|
|
|
public void Start()
|
|
{
|
|
// Start the host
|
|
jsonRpcHost.Start();
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
jsonRpcHost.Stop();
|
|
}
|
|
|
|
public void WaitForExit()
|
|
{
|
|
jsonRpcHost.WaitForExit();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Request Handlers
|
|
|
|
internal async Task HandleInitializeRequest(InitializeParameters initParams, RequestContext<InitializeResponse> requestContext)
|
|
{
|
|
Logger.Write(TraceEventType.Information, "Service host received intialize request");
|
|
|
|
// Call all initialize methods provided by the service components
|
|
IEnumerable<Task> initializeTasks = initCallbacks.Select(t => t(initParams, requestContext));
|
|
|
|
// Respond to initialize once all tasks are completed
|
|
await Task.WhenAll(initializeTasks);
|
|
|
|
if (InitializeResponse == null)
|
|
{
|
|
InitializeResponse = new InitializeResponse
|
|
{
|
|
Capabilities = new ServerCapabilities()
|
|
};
|
|
}
|
|
requestContext.SendResult(InitializeResponse);
|
|
}
|
|
|
|
internal void HandleExitNotification(object exitParams, EventContext eventContext)
|
|
{
|
|
// Stop the server channel
|
|
Stop();
|
|
}
|
|
|
|
internal async Task HandleShutdownRequest(object shutdownParams, RequestContext<object> requestContext)
|
|
{
|
|
Logger.Write(TraceEventType.Information, "Service host received shutdown request");
|
|
|
|
// Call all the shutdown methods provided by the service components
|
|
IEnumerable<Task> shutdownTasks = shutdownCallbacks.Select(t => t(shutdownParams, requestContext));
|
|
|
|
// Shutdown once all tasks are completed, or after the timeout expires, whichever comes first
|
|
TimeSpan shutdownTimeout = TimeSpan.FromSeconds(ShutdownTimeoutSeconds);
|
|
await Task.WhenAny(Task.WhenAll(shutdownTasks), Task.Delay(shutdownTimeout));
|
|
requestContext.SendResult(null);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|