Files
sqltoolsservice/src/Microsoft.SqlTools.Hosting/Utility/UtilityServiceHost.cs
ranasaria 09652cccd1 Enhanced Logging for sqltoolsservice (#695)
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
2018-09-24 23:55:59 -07:00

176 lines
6.8 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.Reflection;
using System.Threading.Tasks;
using Microsoft.SqlTools.Hosting;
using Microsoft.SqlTools.Hosting.Contracts;
using Microsoft.SqlTools.Hosting.Protocol;
using Microsoft.SqlTools.Hosting.Protocol.Channel;
namespace Microsoft.SqlTools.Utility
{
/// <summary>
/// SQL Tools Service request handler for any utility services. Provides the entire JSON RPC
/// implementation for sending/receiving JSON requests and dispatching the requests to
/// handlers that are registered prior to startup.
/// </summary>
public sealed class UtilityServiceHost : ServiceHostBase
{
/// <summary>
/// This timeout limits the amount of time that shutdown tasks can take to complete
/// prior to the process shutting down.
/// </summary>
private const int ShutdownTimeoutInSeconds = 120;
#region Singleton Instance Code
/// <summary>
/// Singleton instance of the service host for internal storage
/// </summary>
private static readonly Lazy<UtilityServiceHost> instance = new Lazy<UtilityServiceHost>(() => new UtilityServiceHost());
/// <summary>
/// Current instance of the ServiceHost
/// </summary>
public static UtilityServiceHost Instance
{
get { return instance.Value; }
}
/// <summary>
/// Constructs new instance of ServiceHost using the host and profile details provided.
/// Access is private to ensure only one instance exists at a time.
/// </summary>
private UtilityServiceHost() : base(new StdioServerChannel())
{
// Initialize the shutdown activities
shutdownCallbacks = new List<ShutdownCallback>();
initializeCallbacks = new List<InitializeCallback>();
}
/// <summary>
/// Provide initialization that must occur after the service host is started
/// </summary>
public void InitializeRequestHandlers()
{
// Register the requests that this service host will handle
this.SetRequestHandler(InitializeRequest.Type, this.HandleInitializeRequest);
this.SetRequestHandler(ShutdownRequest.Type, this.HandleShutdownRequest);
this.SetRequestHandler(VersionRequest.Type, HandleVersionRequest);
}
#endregion
#region Member Variables
/// <summary>
/// Delegate definition for the host shutdown event
/// </summary>
/// <param name="shutdownParams"></param>
/// <param name="shutdownRequestContext"></param>
public delegate Task ShutdownCallback(object shutdownParams, RequestContext<object> shutdownRequestContext);
/// <summary>
/// Delegate definition for the host initialization event
/// </summary>
/// <param name="startupParams"></param>
/// <param name="requestContext"></param>
public delegate Task InitializeCallback(InitializeRequest startupParams, RequestContext<InitializeResult> requestContext);
private readonly List<ShutdownCallback> shutdownCallbacks;
private readonly List<InitializeCallback> initializeCallbacks;
private static readonly Version serviceVersion = Assembly.GetEntryAssembly().GetName().Version;
#endregion
#region Public Methods
/// <summary>
/// Adds a new callback to be called when the shutdown request is submitted
/// </summary>
/// <param name="callback">Callback to perform when a shutdown request is submitted</param>
public void RegisterShutdownTask(ShutdownCallback callback)
{
shutdownCallbacks.Add(callback);
}
/// <summary>
/// Add a new method to be called when the initialize request is submitted
/// </summary>
/// <param name="callback">Callback to perform when an initialize request is submitted</param>
public void RegisterInitializeTask(InitializeCallback callback)
{
initializeCallbacks.Add(callback);
}
#endregion
#region Request Handlers
/// <summary>
/// Handles the shutdown event for the Language Server
/// </summary>
private async Task HandleShutdownRequest(object shutdownParams, RequestContext<object> requestContext)
{
Logger.Write(TraceEventType.Information, "Service host is shutting down...");
// Call all the shutdown methods provided by the service components
Task[] shutdownTasks = shutdownCallbacks.Select(t => t(shutdownParams, requestContext)).ToArray();
TimeSpan shutdownTimeout = TimeSpan.FromSeconds(ShutdownTimeoutInSeconds);
// shut down once all tasks are completed, or after the timeout expires, whichever comes first.
await Task.WhenAny(Task.WhenAll(shutdownTasks), Task.Delay(shutdownTimeout)).ContinueWith(t => Environment.Exit(0));
}
/// <summary>
/// Handles the initialization request
/// </summary>
/// <param name="initializeParams"></param>
/// <param name="requestContext"></param>
/// <returns></returns>
internal async Task HandleInitializeRequest(InitializeRequest initializeParams, RequestContext<InitializeResult> requestContext)
{
// Call all tasks that registered on the initialize request
var initializeTasks = initializeCallbacks.Select(t => t(initializeParams, requestContext));
await Task.WhenAll(initializeTasks);
// TODO: Figure out where this needs to go to be agnostic of the language
// Send back what this server can do
await requestContext.SendResult(
new InitializeResult
{
Capabilities = new ServerCapabilities
{
DefinitionProvider = false,
ReferencesProvider = false,
DocumentFormattingProvider = false,
DocumentRangeFormattingProvider = false,
DocumentHighlightProvider = false,
HoverProvider = false
}
});
}
/// <summary>
/// Handles the version request. Sends back the server version as result.
/// </summary>
private static async Task HandleVersionRequest(
object versionRequestParams,
RequestContext<string> requestContext)
{
await requestContext.SendResult(serviceVersion.ToString());
}
#endregion
}
}