From 463fc60330cfdf3a728845a38f444775b3ca7d36 Mon Sep 17 00:00:00 2001
From: Cheena Malhotra <13396919+cheenamalhotra@users.noreply.github.com>
Date: Fri, 24 Mar 2023 15:07:02 -0700
Subject: [PATCH] Move back 'Shared' content to 'Hosting' assembly (#1967)
---
sqltoolsservice.sln | 11 +-
.../ConnectionProviderOptionsHelper.cs | 2 +-
.../Connection/ConnectionService.cs | 2 +-
.../DataSource/DataSourceFactory.cs | 2 +-
.../DataSource/Kusto/KustoClient.cs | 2 +-
.../Microsoft.Kusto.ServiceLayer.csproj | 1 -
.../Microsoft.SqlTools.Authentication.csproj | 2 +-
.../Microsoft.SqlTools.Hosting.csproj | 3 -
.../Utility/CommonUtils.cs | 66 +++
.../Utility/Logger.cs | 533 ++++++++++++++++++
.../Utility/SqlConstants.cs | 24 +
.../ConnectionProviderOptionsHelper.cs | 2 +-
.../Connection/ConnectionService.cs | 6 +-
.../DacFx/DacFxOperation.cs | 2 +-
.../Microsoft.SqlTools.ServiceLayer.csproj | 1 -
.../SchemaCompare/SchemaCompareUtils.cs | 2 +-
.../Scripting/ScriptingService.cs | 2 +-
.../Utility/FileUtils.cs | 1 +
.../DataSource/DataSourceFactoryTests.cs | 2 +-
.../DataSource/KustoClientTests.cs | 2 +-
...t.SqlTools.ServiceLayer.Test.Common.csproj | 1 -
.../Connection/ConnectionServiceTests.cs | 2 +-
...oft.SqlTools.ServiceLayer.UnitTests.csproj | 1 -
23 files changed, 640 insertions(+), 32 deletions(-)
create mode 100644 src/Microsoft.SqlTools.Hosting/Utility/CommonUtils.cs
create mode 100644 src/Microsoft.SqlTools.Hosting/Utility/Logger.cs
create mode 100644 src/Microsoft.SqlTools.Hosting/Utility/SqlConstants.cs
diff --git a/sqltoolsservice.sln b/sqltoolsservice.sln
index 47941438..1f2c7af6 100644
--- a/sqltoolsservice.sln
+++ b/sqltoolsservice.sln
@@ -96,9 +96,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.SqlTools.Migratio
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.SqlTools.Migration.IntegrationTests", "test\Microsoft.SqlTools.Migration.IntegrationTests\Microsoft.SqlTools.Migration.IntegrationTests.csproj", "{5C7F4DAC-F794-4C21-A031-DCAAFAF3C0A9}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.SqlTools.Authentication", "src\Microsoft.SqlTools.Authentication\Microsoft.SqlTools.Authentication.csproj", "{2A32C3B6-3E9F-4A8E-BF98-59F9AEF6DAAC}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.SqlTools.Shared", "src\Microsoft.SqlTools.Shared\Microsoft.SqlTools.Shared.csproj", "{531EC0E0-F400-42C5-BCFA-6691313B5F3E}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.SqlTools.Authentication", "src\Microsoft.SqlTools.Authentication\Microsoft.SqlTools.Authentication.csproj", "{2A32C3B6-3E9F-4A8E-BF98-59F9AEF6DAAC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -239,12 +237,6 @@ Global
{2A32C3B6-3E9F-4A8E-BF98-59F9AEF6DAAC}.Integration|Any CPU.Build.0 = Debug|Any CPU
{2A32C3B6-3E9F-4A8E-BF98-59F9AEF6DAAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2A32C3B6-3E9F-4A8E-BF98-59F9AEF6DAAC}.Release|Any CPU.Build.0 = Release|Any CPU
- {531EC0E0-F400-42C5-BCFA-6691313B5F3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {531EC0E0-F400-42C5-BCFA-6691313B5F3E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {531EC0E0-F400-42C5-BCFA-6691313B5F3E}.Integration|Any CPU.ActiveCfg = Debug|Any CPU
- {531EC0E0-F400-42C5-BCFA-6691313B5F3E}.Integration|Any CPU.Build.0 = Debug|Any CPU
- {531EC0E0-F400-42C5-BCFA-6691313B5F3E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {531EC0E0-F400-42C5-BCFA-6691313B5F3E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -273,7 +265,6 @@ Global
{22DB0C12-6848-4503-AD1C-DAD6A1D631AE} = {2BBD7364-054F-4693-97CD-1C395E3E84A9}
{5C7F4DAC-F794-4C21-A031-DCAAFAF3C0A9} = {AB9CA2B8-6F70-431C-8A1D-67479D8A7BE4}
{2A32C3B6-3E9F-4A8E-BF98-59F9AEF6DAAC} = {2BBD7364-054F-4693-97CD-1C395E3E84A9}
- {531EC0E0-F400-42C5-BCFA-6691313B5F3E} = {2BBD7364-054F-4693-97CD-1C395E3E84A9}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B31CDF4B-2851-45E5-8C5F-BE97125D9DD8}
diff --git a/src/Microsoft.Kusto.ServiceLayer/Connection/ConnectionProviderOptionsHelper.cs b/src/Microsoft.Kusto.ServiceLayer/Connection/ConnectionProviderOptionsHelper.cs
index de9e4a73..88984946 100644
--- a/src/Microsoft.Kusto.ServiceLayer/Connection/ConnectionProviderOptionsHelper.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/Connection/ConnectionProviderOptionsHelper.cs
@@ -4,7 +4,7 @@
//
using Microsoft.SqlTools.Hosting.Contracts;
-using static Microsoft.SqlTools.Shared.Utility.Constants;
+using static Microsoft.SqlTools.Utility.SqlConstants;
namespace Microsoft.Kusto.ServiceLayer.Connection
{
diff --git a/src/Microsoft.Kusto.ServiceLayer/Connection/ConnectionService.cs b/src/Microsoft.Kusto.ServiceLayer/Connection/ConnectionService.cs
index c7cc33c0..4b368b13 100644
--- a/src/Microsoft.Kusto.ServiceLayer/Connection/ConnectionService.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/Connection/ConnectionService.cs
@@ -19,7 +19,7 @@ using System.Diagnostics;
using Microsoft.Kusto.ServiceLayer.DataSource;
using Microsoft.Kusto.ServiceLayer.DataSource.Metadata;
using Microsoft.SqlTools.ServiceLayer.Connection.ReliableConnection;
-using static Microsoft.SqlTools.Shared.Utility.Constants;
+using static Microsoft.SqlTools.Utility.SqlConstants;
namespace Microsoft.Kusto.ServiceLayer.Connection
{
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceFactory.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceFactory.cs
index 1f140297..76dd52ef 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceFactory.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/DataSourceFactory.cs
@@ -20,7 +20,7 @@ using Microsoft.Kusto.ServiceLayer.LanguageServices;
using Microsoft.Kusto.ServiceLayer.Utility;
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
using CompletionItem = Microsoft.Kusto.ServiceLayer.LanguageServices.Contracts.CompletionItem;
-using static Microsoft.SqlTools.Shared.Utility.Constants;
+using static Microsoft.SqlTools.Utility.SqlConstants;
namespace Microsoft.Kusto.ServiceLayer.DataSource
{
diff --git a/src/Microsoft.Kusto.ServiceLayer/DataSource/Kusto/KustoClient.cs b/src/Microsoft.Kusto.ServiceLayer/DataSource/Kusto/KustoClient.cs
index ac671977..baa23472 100644
--- a/src/Microsoft.Kusto.ServiceLayer/DataSource/Kusto/KustoClient.cs
+++ b/src/Microsoft.Kusto.ServiceLayer/DataSource/Kusto/KustoClient.cs
@@ -21,7 +21,7 @@ using Kusto.Language.Editor;
using Microsoft.Kusto.ServiceLayer.Connection;
using Microsoft.Kusto.ServiceLayer.DataSource.Contracts;
using Microsoft.Kusto.ServiceLayer.Utility;
-using static Microsoft.SqlTools.Shared.Utility.Constants;
+using static Microsoft.SqlTools.Utility.SqlConstants;
namespace Microsoft.Kusto.ServiceLayer.DataSource.Kusto
{
diff --git a/src/Microsoft.Kusto.ServiceLayer/Microsoft.Kusto.ServiceLayer.csproj b/src/Microsoft.Kusto.ServiceLayer/Microsoft.Kusto.ServiceLayer.csproj
index 80501dcf..e646918e 100644
--- a/src/Microsoft.Kusto.ServiceLayer/Microsoft.Kusto.ServiceLayer.csproj
+++ b/src/Microsoft.Kusto.ServiceLayer/Microsoft.Kusto.ServiceLayer.csproj
@@ -51,7 +51,6 @@
-
diff --git a/src/Microsoft.SqlTools.Authentication/Microsoft.SqlTools.Authentication.csproj b/src/Microsoft.SqlTools.Authentication/Microsoft.SqlTools.Authentication.csproj
index 517cd82b..b86e3871 100644
--- a/src/Microsoft.SqlTools.Authentication/Microsoft.SqlTools.Authentication.csproj
+++ b/src/Microsoft.SqlTools.Authentication/Microsoft.SqlTools.Authentication.csproj
@@ -13,6 +13,6 @@
-
+
diff --git a/src/Microsoft.SqlTools.Hosting/Microsoft.SqlTools.Hosting.csproj b/src/Microsoft.SqlTools.Hosting/Microsoft.SqlTools.Hosting.csproj
index 4efcce20..5749dde3 100644
--- a/src/Microsoft.SqlTools.Hosting/Microsoft.SqlTools.Hosting.csproj
+++ b/src/Microsoft.SqlTools.Hosting/Microsoft.SqlTools.Hosting.csproj
@@ -25,7 +25,4 @@
-
-
-
\ No newline at end of file
diff --git a/src/Microsoft.SqlTools.Hosting/Utility/CommonUtils.cs b/src/Microsoft.SqlTools.Hosting/Utility/CommonUtils.cs
new file mode 100644
index 00000000..34e59354
--- /dev/null
+++ b/src/Microsoft.SqlTools.Hosting/Utility/CommonUtils.cs
@@ -0,0 +1,66 @@
+//
+// 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.Runtime.InteropServices;
+
+namespace Microsoft.SqlTools.Hosting.Utility
+{
+ public class CommonUtils
+ {
+ ///
+ /// Builds directory path based on environment settings.
+ ///
+ /// Application directory path
+ /// When called on unsupported platform.
+ public static string BuildAppDirectoryPath()
+ {
+ var homedir = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
+
+ // Windows
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ var appData = Environment.GetEnvironmentVariable("APPDATA");
+ var userProfile = Environment.GetEnvironmentVariable("USERPROFILE");
+ if (appData != null)
+ {
+ return appData;
+ }
+ else if (userProfile != null)
+ {
+ return string.Join(Environment.GetEnvironmentVariable("USERPROFILE"), "AppData", "Roaming");
+ }
+ else
+ {
+ throw new Exception("Not able to find APPDATA or USERPROFILE");
+ }
+ }
+
+ // Mac
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ return string.Join(homedir, "Library", "Application Support");
+ }
+
+ // Linux
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ var xdgConfigHome = Environment.GetEnvironmentVariable("XDG_CONFIG_HOME");
+ if (xdgConfigHome != null)
+ {
+ return xdgConfigHome;
+ }
+ else
+ {
+ return string.Join(homedir, ".config");
+ }
+ }
+ else
+ {
+ throw new Exception("Platform not supported");
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.SqlTools.Hosting/Utility/Logger.cs b/src/Microsoft.SqlTools.Hosting/Utility/Logger.cs
new file mode 100644
index 00000000..2fc61eb3
--- /dev/null
+++ b/src/Microsoft.SqlTools.Hosting/Utility/Logger.cs
@@ -0,0 +1,533 @@
+//
+// 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.Diagnostics;
+using System.Globalization;
+using System.IO;
+using System.Threading;
+
+/**
+ * !!! IMPORTANT: DO NOT MOVE THIS CLASS OUT OF MICROSOFT.SQLTOOLS.HOSTING ASSEMBLY !!!
+ * VSCode Intellicode extension depends on this class from this library, do not alter it's namespace/assembly address.
+ */
+namespace Microsoft.SqlTools.Utility
+{
+ ///
+ /// Ordinal value of each LogEvent value corresponds to a unique event id to be used in trace.
+ /// By convention explicitly specify the integer value so that when this list grows large it is easy to figure out
+ /// enumeration corresponding to a numeric value. We could be reserving ranges of values for specific areas or logEvents.
+ /// Maximum value assignable to LogEvent enum value is 65,535.
+ ///
+ public enum LogEvent : ushort
+ {
+ Default = 0,
+ IoFileSystem = 1,
+ OsSubSystem = 2,
+ }
+
+ ///
+ /// Provides a simple logging interface built on top of .Net tracing frameworks
+ ///
+ public static class Logger
+ {
+ public const SourceLevels defaultTracingLevel = SourceLevels.Critical;
+ public const string defaultTraceSource = "sqltools";
+ private static SourceLevels tracingLevel = defaultTracingLevel;
+ private static string? logFileFullPath;
+
+ public static TraceSource? TraceSource { get; set; }
+
+ public static string LogFileFullPath
+ {
+ get => logFileFullPath!;
+ private set
+ {
+ if (value != null)
+ {
+ //If the log file path has a directory component then ensure that the directory exists.
+ if (!string.IsNullOrEmpty(Path.GetDirectoryName(value)) && !Directory.Exists(Path.GetDirectoryName(value)))
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(value)!);
+ }
+
+ logFileFullPath = value;
+ }
+ ConfigureListener();
+ }
+ }
+
+ private static SqlToolsTraceListener? Listener { get; set; }
+
+ private static void ConfigureLogFile(string logFilePrefix) => LogFileFullPath = GenerateLogFilePath(logFilePrefix);
+
+ ///
+ /// Calling this method will turn on inclusion CallStack in the log for all future traces
+ ///
+ public static void StartCallStack() => Listener!.TraceOutputOptions |= TraceOptions.Callstack;
+
+ ///
+ /// Calling this method will turn off inclusion of CallStack in the log for all future traces
+ ///
+ public static void StopCallStack() => Listener!.TraceOutputOptions &= ~TraceOptions.Callstack;
+
+ ///
+ /// Calls flush on defaultTracingLevel configured listeners.
+ ///
+ public static void Flush()
+ {
+ TraceSource?.Flush();
+ Trace.Flush();
+ }
+
+ public static void Close()
+ {
+ Flush();
+ TraceSource?.Close();
+ Trace.Close();
+ Listener = null; // Since we have closed the listener, set listener to null.
+ }
+ public static SourceLevels TracingLevel
+ {
+ get => tracingLevel;
+ set
+ {
+ if (TraceSource != null)
+ {
+ // configures the source level filter. This alone is not enough for tracing that is done via "Trace" class instead of "TraceSource" object
+ TraceSource.Switch = new SourceSwitch(TraceSource.Name, value.ToString());
+ }
+ // configure the listener level filter
+ tracingLevel = value;
+ Listener!.Filter = new EventTypeFilter(tracingLevel);
+ }
+ }
+
+ public static bool IsPiiEnabled { get; set; } = false;
+
+ public static bool AutoFlush { get; set; } = false;
+
+ ///
+ /// Initializes the Logger for the current process.
+ ///
+ ///
+ /// Optional. Specifies the minimum log message level to write to the log file.
+ ///
+ ///
+ /// Optional. Specifies the log name prefix for the log file name at which log messages will be written.
+ ///
+ /// Optional. Specifies the tracesource name.
+ ///
+ ///
+ /// Optional. Specifies whether the log is flushed after every message
+ ///
+ public static void Initialize(
+ SourceLevels tracingLevel = defaultTracingLevel,
+ bool piiEnabled = false,
+ string? logFilePath = null,
+ string traceSource = defaultTraceSource,
+ bool autoFlush = false)
+ {
+ Logger.tracingLevel = tracingLevel;
+ Logger.IsPiiEnabled = piiEnabled;
+ Logger.AutoFlush = autoFlush;
+ TraceSource = new TraceSource(traceSource, Logger.tracingLevel);
+ if (string.IsNullOrWhiteSpace(logFilePath))
+ {
+ logFilePath = GenerateLogFilePath(traceSource);
+ }
+
+ LogFileFullPath = logFilePath;
+ Write(TraceEventType.Information, $"Initialized the {traceSource} logger. Log file is: {LogFileFullPath}");
+ }
+
+ ///
+ /// Initializes the Logger for the current process.
+ ///
+ ///
+ ///
+ /// Optional. Specifies the minimum log message level to write to the log file.
+ ///
+ ///
+ /// Optional. Specifies the log name prefix for the log file name at which log messages will be written.
+ ///
+ /// Optional. Specifies the tracesource name.
+ ///
+ ///
+ /// Optional. Specifies whether the log is flushed after every message
+ ///
+ public static void Initialize(string tracingLevel, bool piiEnabled, string? logFilePath = null, string traceSource = defaultTraceSource, bool autoFlush = false)
+ {
+ Initialize(Enum.TryParse(tracingLevel, out SourceLevels sourceTracingLevel)
+ ? sourceTracingLevel
+ : defaultTracingLevel
+ , piiEnabled
+ , logFilePath
+ , traceSource
+ , autoFlush);
+ }
+
+ ///
+ /// Configures the LogfilePath for the tracelistener in use for this process.
+ ///
+ ///
+ /// Returns the log file path corresponding to logfilePrefix
+ ///
+ public static string GenerateLogFilePath(string logFilePrefix = defaultTraceSource)
+ {
+ if (string.IsNullOrWhiteSpace(logFilePrefix))
+ {
+ throw new ArgumentOutOfRangeException(nameof(logFilePrefix), $"LogfilePath cannot be configured if argument {nameof(logFilePrefix)} has not been set");
+ }
+ // Create the log directory
+ string? logDir = Path.GetDirectoryName(logFilePrefix);
+ if (!string.IsNullOrWhiteSpace(logDir))
+ {
+ if (!Directory.Exists(logDir))
+ {
+ try
+ {
+ Directory.CreateDirectory(logDir);
+ }
+ catch (Exception ex)
+ {
+ Write(TraceEventType.Error, LogEvent.IoFileSystem, $"Unable to create directory:{logDir}\nException encountered:{ex}");
+ }
+ }
+ }
+
+ // get a unique number to prevent conflicts of two process launching at the same time
+ int uniqueId;
+ try
+ {
+ uniqueId = Environment.ProcessId;
+ }
+ catch (Exception ex)
+ {
+ Write(TraceEventType.Information, LogEvent.OsSubSystem, $"Unable to get process id of current running process\nException encountered:{ex}");
+ // if the pid look up fails for any reason, just use a random number
+ uniqueId = new Random().Next(10000, 99999);
+ }
+
+ string fileName;
+ try
+ {
+ var now = DateTime.Now;
+ fileName = string.Format(CultureInfo.InvariantCulture,
+ "{0}_{1,4:D4}{2,2:D2}{3,2:D2}{4,2:D2}{5,2:D2}{6,2:D2}_{7}.log",
+ logFilePrefix, now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, uniqueId);
+ }
+ catch (Exception)
+ {
+ fileName = string.Format(CultureInfo.InvariantCulture, "{0}_{1}.log", logFilePrefix, uniqueId);
+ }
+
+ // make the log path unique
+ return fileName;
+ }
+
+ private static void ConfigureListener()
+ {
+ if (string.IsNullOrWhiteSpace(LogFileFullPath))
+ {
+ throw new InvalidOperationException("Listeners cannot be configured if LogFileFullPath has not been set");
+ }
+ Listener = new SqlToolsTraceListener(LogFileFullPath)
+ {
+ TraceOutputOptions = TraceOptions.DateTime | TraceOptions.ProcessId | TraceOptions.ThreadId,
+ Filter = new EventTypeFilter(TracingLevel),
+ };
+ TraceSource?.Listeners.Clear();
+ TraceSource?.Listeners.Add(Listener);
+ Trace.Listeners.Clear();
+ Trace.Listeners.Add(Listener);
+ }
+
+ ///
+ /// Writes a message to the log file.
+ ///
+ /// The level at which the message will be written.
+ /// The message text to be written.
+ public static void Write(TraceEventType eventType, string logMessage) => Write(eventType, LogEvent.Default, logMessage);
+
+ ///
+ /// Writes a PII message to the log file with the Verbose event level when PII flag is enabled.
+ ///
+ /// The message text to be written.
+ public static void Pii(string logMessage) {
+ if (IsPiiEnabled) {
+ Write(TraceEventType.Verbose, logMessage);
+ }
+ }
+
+ ///
+ /// Writes a message to the log file with the Verbose event level
+ ///
+ /// The message text to be written.
+ public static void Verbose(string logMessage) => Write(TraceEventType.Verbose, logMessage);
+
+ ///
+ /// Writes a message to the log file with the Information event level
+ ///
+ /// The message text to be written.
+ public static void Information(string logMessage) => Write(TraceEventType.Information, logMessage);
+
+ ///
+ /// Writes a message to the log file with the Warning event level
+ ///
+ /// The message text to be written.
+ public static void Warning(string logMessage) => Write(TraceEventType.Warning, logMessage);
+
+ ///
+ /// Writes a message to the log file with the Error event level
+ ///
+ /// The message text to be written.
+ public static void Error(string logMessage) => Write(TraceEventType.Error, logMessage);
+
+ ///
+ /// Writes an exception to the log file with the Error event level
+ ///
+ ///
+ public static void Error(Exception exception) => Write(TraceEventType.Error, exception.ToString());
+
+ ///
+ /// Writes a message to the log file with the Critical event level
+ ///
+ /// The message text to be written.
+ public static void Critical(string logMessage) => Write(TraceEventType.Critical, logMessage);
+
+ ///
+ /// Writes a message to the log file with accompanying callstack.
+ ///
+ /// The level at which the message will be written.
+ /// The message text to be written.
+ ///
+ /// The callstack logging gets turned on globally and any other log writes that happens in the time window
+ /// while this log write is happening will also get callstack information logged. This is not considered
+ /// and trying to isolate the callstack logging to be turned of for just one call is unnecessarily complex.
+ ///
+ public static void WriteWithCallstack(TraceEventType eventType, string logMessage) => WriteWithCallstack(eventType, LogEvent.Default, logMessage);
+
+ ///
+ /// Writes a message to the log file with accompanying callstack.
+ ///
+ /// The level at which the message will be written.
+ /// The event id enumeration for the log event.
+ /// The message text to be written.
+ ///
+ /// The callstack logging gets turned on globally and any other log writes that happens in the time window
+ /// while this log write is happening will also get callstack information logged. This is not considered
+ /// and trying to isolate the callstack logging to be turned of for just one call is unnecessarily complex.
+ ///
+ public static void WriteWithCallstack(TraceEventType eventType, LogEvent logEvent, string logMessage)
+ {
+ Logger.StartCallStack();
+ Write(eventType, logEvent, logMessage);
+ Logger.StopCallStack();
+ }
+
+ ///
+ /// Writes a message to the log file.
+ ///
+ /// The level at which the message will be written.
+ /// The event id enumeration for the log event.
+ /// The message text to be written.
+ public static void Write(
+ TraceEventType eventType,
+ LogEvent logEvent,
+ string logMessage)
+ {
+ // If logger is initialized then use TraceSource else use Trace
+ if (TraceSource != null)
+ {
+ TraceSource.TraceEvent(eventType, (ushort)logEvent, logMessage);
+ }
+ else
+ {
+ switch (eventType)
+ {
+ case TraceEventType.Critical:
+ case TraceEventType.Error:
+ if (eventType == TraceEventType.Critical)
+ {
+ logMessage = $@"event={eventType}: {logMessage}";
+ }
+
+ Trace.TraceError(logMessage);
+ break;
+ case TraceEventType.Warning:
+ Trace.TraceWarning(logMessage);
+ break;
+ case TraceEventType.Information:
+ case TraceEventType.Resume:
+ case TraceEventType.Start:
+ case TraceEventType.Stop:
+ case TraceEventType.Suspend:
+ case TraceEventType.Transfer:
+ case TraceEventType.Verbose:
+ if (eventType != TraceEventType.Information)
+ {
+ logMessage = $@"event={eventType}: {logMessage}";
+ }
+
+ Trace.TraceInformation(logMessage);
+ break;
+ }
+ }
+ if (AutoFlush)
+ {
+ Flush();
+ }
+ }
+ }
+
+ ///
+ /// This listener has the same behavior as TextWriterTraceListener except it controls how the
+ /// options: TraceOptions.DateTime, TraceOptions.ProcessId and TraceOptions.ThreadId is written to the output stream.
+ /// This listener writes the above options, if turned on, inline with the message
+ /// instead of writing them to indented fields as is the case with TextWriterTraceListener.
+ /// This implementation also lazily initializes the underlying tracelistener
+ ///
+ ///
+ /// Implementation of this is a lazily initialize trace listener that is partly inspired
+ /// by: https://stackoverflow.com/questions/30664527/how-to-stop-streamwriter-to-not-to-create-file-if-nothing-to-write
+ ///
+ internal sealed class SqlToolsTraceListener : TraceListener
+ {
+ Lazy _lazyListener;
+ private TextWriterTraceListener Listener => _lazyListener.Value;
+ private bool IsListenerCreated => _lazyListener.IsValueCreated;
+ public SqlToolsTraceListener(string file, string listenerName = "") : base(listenerName)
+ {
+ // Wrapping around lazy to make sure that we do not create file if the log.Write events are getting filtered out. i.e. the log file actually gets created the first time an actual write to log file happens.
+ _lazyListener = new Lazy(
+ valueFactory: () => new TextWriterTraceListener(new StreamWriter(file, append: true), listenerName),
+ // LazyThreadSafetyMode.PublicationOnly mode ensures that we keep trying to create the listener (especially the file that write) on all future log.write events even if previous attempt(s) have failed
+ mode: LazyThreadSafetyMode.PublicationOnly
+ );
+ }
+ #region forward actual write/close/flush/dispose calls to the underlying listener.
+ public override void Write(string? message) => Listener.Write(message);
+
+ public override void WriteLine(string? message) => Listener.WriteLine(message);
+
+ ///
+ /// Closes the so that it no longer
+ /// receives tracing or debugging output.
+ /// Make sure that we do not Close if the lazy listener never got created.
+ ///
+ public override void Close()
+ {
+ if (IsListenerCreated)
+ {
+ Listener.Close();
+ }
+ }
+
+ ///
+ /// Releases all resources used by the
+ /// No unmanaged resources in this class, and it is sealed.
+ /// No finalizer needed. See http://stackoverflow.com/a/3882819/613130
+ /// We skip disposing if the lazy listener never got created.
+ ///
+ public new void Dispose()
+ {
+ if (IsListenerCreated)
+ {
+ Listener.Dispose();
+ }
+ }
+
+ ///
+ /// Flushes the output buffer for the .
+ /// Make sure that we do not Flush if the lazy listener never got created.
+ ///
+ public override void Flush()
+ {
+ if (IsListenerCreated)
+ {
+ Listener.Flush();
+ }
+ }
+ #endregion
+
+ public override void TraceEvent(TraceEventCache? eventCache, String source, TraceEventType eventType, int id)
+ {
+ TraceEvent(eventCache, source, eventType, id, String.Empty);
+ }
+
+ // All other TraceEvent methods come through this one.
+ public override void TraceEvent(TraceEventCache? eventCache, String source, TraceEventType eventType, int id, string? message)
+ {
+ if (Filter != null && !Filter.ShouldTrace(eventCache, source, eventType, id, message, null, null, null))
+ {
+ return;
+ }
+
+ WriteHeader(eventCache!, source, eventType, id);
+ WriteLine(message!);
+ WriteFooter(eventCache!);
+ }
+
+ public override void TraceEvent(TraceEventCache? eventCache, String source, TraceEventType eventType, int id, string? format, params object?[]? args)
+ {
+ if (Filter != null && !Filter.ShouldTrace(eventCache, source, eventType, id, format, args, null, null))
+ {
+ return;
+ }
+
+ WriteHeader(eventCache!, source, eventType, id);
+ if (args != null)
+ {
+ WriteLine(String.Format(CultureInfo.InvariantCulture, format!, args));
+ }
+ else
+ {
+ WriteLine(format!);
+ }
+ WriteFooter(eventCache!);
+ }
+
+ private void WriteHeader(TraceEventCache eventCache, String source, TraceEventType eventType, int id)
+ => Write(FormatHeader(eventCache, String.Format(CultureInfo.InvariantCulture, "{0} {1}: {2} : ", source, eventType.ToString(), id.ToString(CultureInfo.InvariantCulture))));
+
+ private void WriteFooter(TraceEventCache eventCache)
+ {
+ if (eventCache == null)
+ {
+ return;
+ }
+
+ IndentLevel++;
+ if (TraceOutputOptions.HasFlag(TraceOptions.LogicalOperationStack))
+ {
+ WriteLine("LogicalOperationStack=" + eventCache.LogicalOperationStack);
+ }
+
+ if (TraceOutputOptions.HasFlag(TraceOptions.Callstack))
+ {
+ WriteLine("Callstack=" + eventCache.Callstack);
+ }
+
+ IndentLevel--;
+ }
+
+ private string FormatHeader(TraceEventCache eventCache, string message)
+ {
+ if (eventCache == null)
+ {
+ return message;
+ }
+
+ return $"{(IsEnabled(TraceOptions.DateTime) ? string.Format(CultureInfo.InvariantCulture, "{0} ", eventCache.DateTime.ToLocalTime().ToString("yy-MM-dd H:mm:ss.fffffff", CultureInfo.InvariantCulture)) : string.Empty)}"
+ + $"{(IsEnabled(TraceOptions.ProcessId) ? string.Format(CultureInfo.InvariantCulture, "pid:{0} ", eventCache.ProcessId.ToString(CultureInfo.InvariantCulture)) : string.Empty)}"
+ + $"{(IsEnabled(TraceOptions.ThreadId) ? string.Format(CultureInfo.InvariantCulture, "tid:{0} ", eventCache.ThreadId.ToString(CultureInfo.InvariantCulture)) : string.Empty)}"
+ + message;
+ }
+
+ private bool IsEnabled(TraceOptions opt) => TraceOutputOptions.HasFlag(opt);
+
+ }
+}
diff --git a/src/Microsoft.SqlTools.Hosting/Utility/SqlConstants.cs b/src/Microsoft.SqlTools.Hosting/Utility/SqlConstants.cs
new file mode 100644
index 00000000..8020ac2e
--- /dev/null
+++ b/src/Microsoft.SqlTools.Hosting/Utility/SqlConstants.cs
@@ -0,0 +1,24 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+namespace Microsoft.SqlTools.Utility
+{
+ public class SqlConstants
+ {
+ // Authentication Types
+ public const string Integrated = "Integrated";
+ public const string SqlLogin = "SqlLogin";
+ public const string AzureMFA = "AzureMFA";
+ public const string dstsAuth = "dstsAuth";
+ public const string ActiveDirectoryInteractive = "ActiveDirectoryInteractive";
+ public const string ActiveDirectoryPassword = "ActiveDirectoryPassword";
+
+ // Azure authentication (MSAL) constants
+ public const string ApplicationClientId = "a69788c6-1d43-44ed-9ca3-b83e194da255";
+ public const string AzureTokenFolder = "Azure Accounts";
+ public const string AzureAccountProviderCredentials = "azureAccountProviderCredentials";
+ public const string MsalCacheName = "accessTokenCache";
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionProviderOptionsHelper.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionProviderOptionsHelper.cs
index 1a09551a..ed65e9fb 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionProviderOptionsHelper.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionProviderOptionsHelper.cs
@@ -6,7 +6,7 @@
#nullable disable
using Microsoft.SqlTools.Hosting.Contracts;
-using static Microsoft.SqlTools.Shared.Utility.Constants;
+using static Microsoft.SqlTools.Utility.SqlConstants;
namespace Microsoft.SqlTools.ServiceLayer.Connection
{
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionService.cs b/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionService.cs
index 42a9520e..89dcdd5d 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Connection/ConnectionService.cs
@@ -23,12 +23,12 @@ using Microsoft.SqlTools.ServiceLayer.LanguageServices;
using Microsoft.SqlTools.ServiceLayer.LanguageServices.Contracts;
using Microsoft.SqlTools.ServiceLayer.Utility;
using Microsoft.SqlTools.Utility;
-using static Microsoft.SqlTools.Shared.Utility.Constants;
+using static Microsoft.SqlTools.Utility.SqlConstants;
using System.Diagnostics;
using Microsoft.SqlTools.Authentication.Sql;
using Microsoft.SqlTools.Authentication;
-using Microsoft.SqlTools.Shared.Utility;
using System.IO;
+using Microsoft.SqlTools.Hosting.Utility;
namespace Microsoft.SqlTools.ServiceLayer.Connection
{
@@ -1162,7 +1162,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Connection
var applicationPath = commandOptions.ApplicationPath;
if (string.IsNullOrEmpty(applicationPath))
{
- applicationPath = Utils.BuildAppDirectoryPath();
+ applicationPath = CommonUtils.BuildAppDirectoryPath();
Logger.Warning($"Application Path not received with command options, using default application path as: {applicationPath}");
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxOperation.cs b/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxOperation.cs
index 64f86757..10c01ccd 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxOperation.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxOperation.cs
@@ -8,7 +8,7 @@ using Microsoft.SqlServer.Dac;
using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.TaskServices;
using Microsoft.SqlTools.ServiceLayer.Utility;
-using static Microsoft.SqlTools.Shared.Utility.Constants;
+using static Microsoft.SqlTools.Utility.SqlConstants;
using Microsoft.SqlTools.Utility;
using System;
using System.Diagnostics;
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj b/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj
index 1149eb4e..1733ff8d 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj
+++ b/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj
@@ -58,7 +58,6 @@
-
diff --git a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareUtils.cs b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareUtils.cs
index 707fb8eb..fbbc7880 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareUtils.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareUtils.cs
@@ -17,7 +17,7 @@ using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts;
using Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts;
using Microsoft.SqlTools.ServiceLayer.Utility;
-using static Microsoft.SqlTools.Shared.Utility.Constants;
+using static Microsoft.SqlTools.Utility.SqlConstants;
using Microsoft.SqlTools.Utility;
namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Scripting/ScriptingService.cs b/src/Microsoft.SqlTools.ServiceLayer/Scripting/ScriptingService.cs
index e0c99530..d008ea24 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Scripting/ScriptingService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Scripting/ScriptingService.cs
@@ -17,7 +17,7 @@ using Microsoft.SqlTools.ServiceLayer.Hosting;
using Microsoft.SqlTools.ServiceLayer.Scripting.Contracts;
using Microsoft.SqlTools.Utility;
using Microsoft.SqlTools.ServiceLayer.Utility;
-using static Microsoft.SqlTools.Shared.Utility.Constants;
+using static Microsoft.SqlTools.Utility.SqlConstants;
using System.Linq;
namespace Microsoft.SqlTools.ServiceLayer.Scripting
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Utility/FileUtils.cs b/src/Microsoft.SqlTools.ServiceLayer/Utility/FileUtils.cs
index 0171e819..f5b10fad 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Utility/FileUtils.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/Utility/FileUtils.cs
@@ -6,6 +6,7 @@
#nullable disable
using System;
using System.IO;
+
namespace Microsoft.SqlTools.ServiceLayer.Utility
{
internal static class FileUtilities
diff --git a/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/DataSourceFactoryTests.cs b/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/DataSourceFactoryTests.cs
index c9e7caf4..4d1cd973 100644
--- a/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/DataSourceFactoryTests.cs
+++ b/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/DataSourceFactoryTests.cs
@@ -12,7 +12,7 @@ using Microsoft.Kusto.ServiceLayer.DataSource;
using Microsoft.Kusto.ServiceLayer.DataSource.Intellisense;
using Microsoft.Kusto.ServiceLayer.LanguageServices;
using Microsoft.Kusto.ServiceLayer.Workspace.Contracts;
-using static Microsoft.SqlTools.Shared.Utility.Constants;
+using static Microsoft.SqlTools.Utility.SqlConstants;
using NUnit.Framework;
namespace Microsoft.Kusto.ServiceLayer.UnitTests.DataSource
diff --git a/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/KustoClientTests.cs b/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/KustoClientTests.cs
index 99b8b1e8..1f78f504 100644
--- a/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/KustoClientTests.cs
+++ b/test/Microsoft.Kusto.ServiceLayer.UnitTests/DataSource/KustoClientTests.cs
@@ -8,7 +8,7 @@
using System;
using Microsoft.Kusto.ServiceLayer.DataSource.Contracts;
using Microsoft.Kusto.ServiceLayer.DataSource.Kusto;
-using static Microsoft.SqlTools.Shared.Utility.Constants;
+using static Microsoft.SqlTools.Utility.SqlConstants;
using NUnit.Framework;
namespace Microsoft.Kusto.ServiceLayer.UnitTests.DataSource
diff --git a/test/Microsoft.SqlTools.ServiceLayer.Test.Common/Microsoft.SqlTools.ServiceLayer.Test.Common.csproj b/test/Microsoft.SqlTools.ServiceLayer.Test.Common/Microsoft.SqlTools.ServiceLayer.Test.Common.csproj
index e62da1a9..c5f955ce 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.Test.Common/Microsoft.SqlTools.ServiceLayer.Test.Common.csproj
+++ b/test/Microsoft.SqlTools.ServiceLayer.Test.Common/Microsoft.SqlTools.ServiceLayer.Test.Common.csproj
@@ -25,6 +25,5 @@
-
\ No newline at end of file
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Connection/ConnectionServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Connection/ConnectionServiceTests.cs
index c5669d60..2f03677a 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Connection/ConnectionServiceTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Connection/ConnectionServiceTests.cs
@@ -12,7 +12,7 @@ using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.Connection.Contracts;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using Microsoft.SqlTools.ServiceLayer.UnitTests.Utility;
-using static Microsoft.SqlTools.Shared.Utility.Constants;
+using static Microsoft.SqlTools.Utility.SqlConstants;
using Moq;
using Moq.Protected;
using NUnit.Framework;
diff --git a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Microsoft.SqlTools.ServiceLayer.UnitTests.csproj b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Microsoft.SqlTools.ServiceLayer.UnitTests.csproj
index 9719192d..0f947ee0 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Microsoft.SqlTools.ServiceLayer.UnitTests.csproj
+++ b/test/Microsoft.SqlTools.ServiceLayer.UnitTests/Microsoft.SqlTools.ServiceLayer.UnitTests.csproj
@@ -34,7 +34,6 @@
-