From 02f1b9f684f071563c56dc90a1ad8c7af15d6127 Mon Sep 17 00:00:00 2001 From: Georgii Borovinskikh Date: Thu, 19 Dec 2024 10:45:53 +0100 Subject: [PATCH 01/12] SLVS-1723 Introduce log contexts --- src/Core/ILogger.cs | 5 + .../Helpers/SonarLintOutputTests.cs | 1 + ...egration.Vsix_Baseline_WithStrongNames.txt | 5 +- ...ation.Vsix_Baseline_WithoutStrongNames.txt | 5 +- .../Helpers/SonarLintOutputLogger.cs | 107 ++++++++++++------ src/Integration/Helpers/VsShellUtils.cs | 28 ----- 6 files changed, 85 insertions(+), 66 deletions(-) diff --git a/src/Core/ILogger.cs b/src/Core/ILogger.cs index 0fbfaae299..aea9b9e0de 100644 --- a/src/Core/ILogger.cs +++ b/src/Core/ILogger.cs @@ -33,4 +33,9 @@ public interface ILogger /// void LogVerbose(string messageFormat, params object[] args); } + + public interface IContextualLogger : ILogger + { + IContextualLogger ForContext(params string[] context); + } } diff --git a/src/Integration.UnitTests/Helpers/SonarLintOutputTests.cs b/src/Integration.UnitTests/Helpers/SonarLintOutputTests.cs index 3002d7a252..04c16bc22c 100644 --- a/src/Integration.UnitTests/Helpers/SonarLintOutputTests.cs +++ b/src/Integration.UnitTests/Helpers/SonarLintOutputTests.cs @@ -24,6 +24,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using SonarLint.VisualStudio.Core; +using SonarLint.VisualStudio.Integration.Helpers; using SonarLint.VisualStudio.TestInfrastructure; namespace SonarLint.VisualStudio.Integration.UnitTests.Helpers diff --git a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt index 443d49ef16..c44180e7fc 100644 --- a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt +++ b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt @@ -1,7 +1,7 @@ --- ################################ # Assembly references report -# Report date/time: 2024-12-18T08:58:15.6084082Z +# Report date/time: 2024-12-18T15:43:49.7226790Z ################################ # # Generated by Devtility CheckAsmRefs v0.11.0.223 @@ -211,6 +211,7 @@ Referenced assemblies: - 'SonarLint.VisualStudio.SLCore, Version=8.9.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' - 'SonarQube.Client, Version=8.9.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' - 'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' +- 'System.Collections.Immutable, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' - 'System.ComponentModel.Composition, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.IO.Abstractions, Version=9.0.0.0, Culture=neutral, PublicKeyToken=96bf224d23c43e59' @@ -219,7 +220,7 @@ Referenced assemblies: - 'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' -# Number of references: 27 +# Number of references: 28 --- Assembly: 'SonarLint.VisualStudio.Integration.TeamExplorer, Version=8.9.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' diff --git a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt index 8ec26e7707..251dfdac6d 100644 --- a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt +++ b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt @@ -1,7 +1,7 @@ --- ################################ # Assembly references report -# Report date/time: 2024-12-18T08:58:15.6084082Z +# Report date/time: 2024-12-18T15:43:49.7226790Z ################################ # # Generated by Devtility CheckAsmRefs v0.11.0.223 @@ -211,6 +211,7 @@ Referenced assemblies: - 'SonarLint.VisualStudio.SLCore, Version=8.9.0.0, Culture=neutral, PublicKeyToken=null' - 'SonarQube.Client, Version=8.9.0.0, Culture=neutral, PublicKeyToken=null' - 'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' +- 'System.Collections.Immutable, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' - 'System.ComponentModel.Composition, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.IO.Abstractions, Version=9.0.0.0, Culture=neutral, PublicKeyToken=96bf224d23c43e59' @@ -219,7 +220,7 @@ Referenced assemblies: - 'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' -# Number of references: 27 +# Number of references: 28 --- Assembly: 'SonarLint.VisualStudio.Integration.TeamExplorer, Version=8.9.0.0, Culture=neutral, PublicKeyToken=null' diff --git a/src/Integration/Helpers/SonarLintOutputLogger.cs b/src/Integration/Helpers/SonarLintOutputLogger.cs index 6ba83084e0..063feae35e 100644 --- a/src/Integration/Helpers/SonarLintOutputLogger.cs +++ b/src/Integration/Helpers/SonarLintOutputLogger.cs @@ -18,56 +18,95 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using System; +using System.Collections.Immutable; using System.ComponentModel.Composition; +using System.Globalization; +using System.Text; using Microsoft.VisualStudio.Shell; using SonarLint.VisualStudio.Core; -namespace SonarLint.VisualStudio.Integration +namespace SonarLint.VisualStudio.Integration.Helpers; + +[Export(typeof(ILogger))] +[Export(typeof(IContextualLogger))] +[PartCreationPolicy(CreationPolicy.Shared)] +public class SonarLintOutputLogger : IContextualLogger { - [Export(typeof(ILogger))] - [PartCreationPolicy(CreationPolicy.Shared)] - public class SonarLintOutputLogger : ILogger + private readonly IServiceProvider serviceProvider; + private readonly ISonarLintSettings sonarLintSettings; + private readonly ImmutableList contexts; + private readonly string contextPropertyValue; + + private bool DebugLogsEnabled => sonarLintSettings.DaemonLogLevel == DaemonLogLevel.Verbose; + + [ImportingConstructor] + public SonarLintOutputLogger( + [Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, + ISonarLintSettings sonarLintSettings) + : this(serviceProvider, sonarLintSettings, ImmutableList.Empty) { - private readonly IServiceProvider serviceProvider; - private readonly ISonarLintSettings sonarLintSettings; + } - [ImportingConstructor] - public SonarLintOutputLogger([Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, - ISonarLintSettings sonarLintSettings) - { - this.serviceProvider = serviceProvider; - this.sonarLintSettings = sonarLintSettings; - } + private SonarLintOutputLogger( + IServiceProvider serviceProvider, + ISonarLintSettings sonarLintSettings, + ImmutableList contexts) + { + this.serviceProvider = serviceProvider; + this.sonarLintSettings = sonarLintSettings; + this.contexts = contexts; + contextPropertyValue = contexts.Count > 0 ? string.Join(" > ", contexts) : null;; + } - public void WriteLine(string message) - { - var prefixedMessage = AddPrefixIfVerboseLogging(message); - VsShellUtils.WriteToSonarLintOutputPane(this.serviceProvider, prefixedMessage); - } + public IContextualLogger ForContext(params string[] context) => + new SonarLintOutputLogger(serviceProvider, sonarLintSettings, contexts.AddRange(context)); + + public void WriteLine(string message) => + WriteToOutputPane(CreateStandardLogPrefix().Append(message).ToString()); + + public void WriteLine(string messageFormat, params object[] args) => + WriteToOutputPane(CreateStandardLogPrefix().AppendFormat(CultureInfo.CurrentCulture, messageFormat, args).ToString()); - public void WriteLine(string messageFormat, params object[] args) + private StringBuilder CreateStandardLogPrefix() => AddStandardProperties(new StringBuilder()); + + public void LogVerbose(string messageFormat, params object[] args) + { + if (DebugLogsEnabled) { - var prefixedMessageFormat = AddPrefixIfVerboseLogging(messageFormat); - VsShellUtils.WriteToSonarLintOutputPane(this.serviceProvider, prefixedMessageFormat, args); + var debugLogPrefix = CreateDebugLogPrefix(); + var logLine = args.Length > 0 + ? debugLogPrefix.AppendFormat(CultureInfo.CurrentCulture, messageFormat, args) + : debugLogPrefix.Append(messageFormat); + WriteToOutputPane(logLine.ToString()); } + } + + private StringBuilder CreateDebugLogPrefix() + { + var builder = new StringBuilder(); + AppendProperty(builder, "DEBUG"); + AddStandardProperties(builder); + return builder; + } - public void LogVerbose(string messageFormat, params object[] args) + private StringBuilder AddStandardProperties(StringBuilder builder) + { + if (sonarLintSettings.DaemonLogLevel == DaemonLogLevel.Verbose) { - if (sonarLintSettings.DaemonLogLevel == DaemonLogLevel.Verbose) - { - var text = args.Length == 0 ? messageFormat : string.Format(messageFormat, args); - WriteLine("[DEBUG] " + text); - } + AppendPropertyFormat(builder, "ThreadId {0}", Thread.CurrentThread.ManagedThreadId); } - private string AddPrefixIfVerboseLogging(string message) + if (contextPropertyValue != null) { - if (sonarLintSettings.DaemonLogLevel == DaemonLogLevel.Verbose) - { - message = $"[ThreadId {System.Threading.Thread.CurrentThread.ManagedThreadId}] " + message; - } - return message; + AppendProperty(builder, contextPropertyValue); } + + return builder; } + + private static void AppendProperty(StringBuilder builder, string property) => builder.Append('[').Append(property).Append(']').Append(' '); + + private static void AppendPropertyFormat(StringBuilder builder, string property, params object[] args) => builder.Append('[').AppendFormat(property, args).Append(']').Append(' '); + + private void WriteToOutputPane(string message) => VsShellUtils.WriteToSonarLintOutputPane(this.serviceProvider, message); } diff --git a/src/Integration/Helpers/VsShellUtils.cs b/src/Integration/Helpers/VsShellUtils.cs index 3433d0d174..dcb2cec893 100644 --- a/src/Integration/Helpers/VsShellUtils.cs +++ b/src/Integration/Helpers/VsShellUtils.cs @@ -41,28 +41,6 @@ public static class VsShellUtils Target = "~F:SonarLint.VisualStudio.Integration.VsShellUtils.SonarLintOutputPaneGuid")] internal static Guid SonarLintOutputPaneGuid = new Guid("EB476B82-D73A-44A6-AFEF-830F7BBA73DB"); - /// - /// Writes a message to the SonarLint output pane. Will append a new line after the message. - /// - public static void WriteToSonarLintOutputPane(IServiceProvider serviceProvider, string messageFormat, params object[] args) - { - if (serviceProvider == null) - { - throw new ArgumentNullException(nameof(serviceProvider)); - } - - if (messageFormat == null) - { - throw new ArgumentNullException(nameof(messageFormat)); - } - - IVsOutputWindowPane sonarLintPane = GetOrCreateSonarLintOutputPane(serviceProvider); - if (sonarLintPane != null) - { - WriteLineToPane(sonarLintPane, messageFormat, args); - } - } - /// /// Writes a message to the SonarLint output pane. Will append a new line after the message. /// @@ -193,12 +171,6 @@ public static IVsOutputWindowPane GetOrCreateSonarLintOutputPane(IServiceProvide return pane; } - private static void WriteLineToPane(IVsOutputWindowPane pane, string messageFormat, params object[] args) - { - int hr = pane.OutputStringThreadSafe(string.Format(CultureInfo.CurrentCulture, messageFormat, args: args) + Environment.NewLine); - Debug.Assert(ErrorHandler.Succeeded(hr), "Failed in OutputStringThreadSafe: " + hr.ToString()); - } - private static void WriteLineToPane(IVsOutputWindowPane pane, string message) { int hr = pane.OutputStringThreadSafe(message + Environment.NewLine); From e80ab46d650ba737d76f77d70de3d0b06f6059e8 Mon Sep 17 00:00:00 2001 From: Georgii Borovinskikh Date: Thu, 19 Dec 2024 14:14:50 +0100 Subject: [PATCH 02/12] Add verbose contexts + refactoring --- src/Core/ILogger.cs | 2 + .../Helpers/SonarLintOutputLogger.cs | 48 +++++++++++-------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/Core/ILogger.cs b/src/Core/ILogger.cs index aea9b9e0de..67a014940a 100644 --- a/src/Core/ILogger.cs +++ b/src/Core/ILogger.cs @@ -37,5 +37,7 @@ public interface ILogger public interface IContextualLogger : ILogger { IContextualLogger ForContext(params string[] context); + + IContextualLogger ForVerboseContext(params string[] context); } } diff --git a/src/Integration/Helpers/SonarLintOutputLogger.cs b/src/Integration/Helpers/SonarLintOutputLogger.cs index 063feae35e..a13028fc34 100644 --- a/src/Integration/Helpers/SonarLintOutputLogger.cs +++ b/src/Integration/Helpers/SonarLintOutputLogger.cs @@ -35,31 +35,40 @@ public class SonarLintOutputLogger : IContextualLogger private readonly IServiceProvider serviceProvider; private readonly ISonarLintSettings sonarLintSettings; private readonly ImmutableList contexts; - private readonly string contextPropertyValue; + private readonly ImmutableList verboseContexts; + private string contextsProperty; + private string verboseContextsProperty; - private bool DebugLogsEnabled => sonarLintSettings.DaemonLogLevel == DaemonLogLevel.Verbose; + private bool VerboseLogsEnabled => sonarLintSettings.DaemonLogLevel == DaemonLogLevel.Verbose; [ImportingConstructor] public SonarLintOutputLogger( [Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, ISonarLintSettings sonarLintSettings) - : this(serviceProvider, sonarLintSettings, ImmutableList.Empty) + : this(serviceProvider, sonarLintSettings, ImmutableList.Empty, ImmutableList.Empty) { } private SonarLintOutputLogger( IServiceProvider serviceProvider, ISonarLintSettings sonarLintSettings, - ImmutableList contexts) + ImmutableList contexts, + ImmutableList verboseContexts) { this.serviceProvider = serviceProvider; this.sonarLintSettings = sonarLintSettings; this.contexts = contexts; - contextPropertyValue = contexts.Count > 0 ? string.Join(" > ", contexts) : null;; + this.verboseContexts = verboseContexts; + contextsProperty = MergeContextsIntoSingleProperty(contexts); + verboseContextsProperty = MergeContextsIntoSingleProperty(verboseContexts); } + private static string MergeContextsIntoSingleProperty(ImmutableList contexts) => contexts.Count > 0 ? string.Join(" > ", contexts) : null; public IContextualLogger ForContext(params string[] context) => - new SonarLintOutputLogger(serviceProvider, sonarLintSettings, contexts.AddRange(context)); + new SonarLintOutputLogger(serviceProvider, sonarLintSettings, contexts.AddRange(context.Where(x => !string.IsNullOrEmpty(x))), verboseContexts); + + public IContextualLogger ForVerboseContext(params string[] context) => + new SonarLintOutputLogger(serviceProvider, sonarLintSettings, contexts, verboseContexts.AddRange(context.Where(x => !string.IsNullOrEmpty(x)))); public void WriteLine(string message) => WriteToOutputPane(CreateStandardLogPrefix().Append(message).ToString()); @@ -71,7 +80,7 @@ public void WriteLine(string messageFormat, params object[] args) => public void LogVerbose(string messageFormat, params object[] args) { - if (DebugLogsEnabled) + if (VerboseLogsEnabled) { var debugLogPrefix = CreateDebugLogPrefix(); var logLine = args.Length > 0 @@ -81,32 +90,31 @@ public void LogVerbose(string messageFormat, params object[] args) } } - private StringBuilder CreateDebugLogPrefix() - { - var builder = new StringBuilder(); - AppendProperty(builder, "DEBUG"); - AddStandardProperties(builder); - return builder; - } + private StringBuilder CreateDebugLogPrefix() => AppendProperty(AddStandardProperties(new StringBuilder()), "DEBUG"); private StringBuilder AddStandardProperties(StringBuilder builder) { - if (sonarLintSettings.DaemonLogLevel == DaemonLogLevel.Verbose) + if (VerboseLogsEnabled) + { + AppendPropertyFormat(builder, "ThreadId {0, 3}", Thread.CurrentThread.ManagedThreadId); + } + + if (contextsProperty != null) { - AppendPropertyFormat(builder, "ThreadId {0}", Thread.CurrentThread.ManagedThreadId); + AppendProperty(builder, contextsProperty); } - if (contextPropertyValue != null) + if (VerboseLogsEnabled && verboseContextsProperty != null) { - AppendProperty(builder, contextPropertyValue); + AppendProperty(builder, verboseContextsProperty); } return builder; } - private static void AppendProperty(StringBuilder builder, string property) => builder.Append('[').Append(property).Append(']').Append(' '); + private static StringBuilder AppendProperty(StringBuilder builder, string property) => builder.Append('[').Append(property).Append(']').Append(' '); - private static void AppendPropertyFormat(StringBuilder builder, string property, params object[] args) => builder.Append('[').AppendFormat(property, args).Append(']').Append(' '); + private static StringBuilder AppendPropertyFormat(StringBuilder builder, string property, params object[] args) => builder.Append('[').AppendFormat(property, args).Append(']').Append(' '); private void WriteToOutputPane(string message) => VsShellUtils.WriteToSonarLintOutputPane(this.serviceProvider, message); } From 8fd79d4e68d2ffa43e1e61c1764223c0908301e5 Mon Sep 17 00:00:00 2001 From: Georgii Borovinskikh Date: Fri, 20 Dec 2024 13:43:18 +0100 Subject: [PATCH 03/12] refactor loggers --- src/Core/ILogContext.cs | 29 ++ src/Core/ILogger.cs | 17 +- src/Core/ILoggerFactory.cs | 37 ++ src/Core/LogContext.cs | 47 +++ src/Core/LoggerBase.cs | 76 +++++ .../Helpers/SonarLintOutputTests.cs | 322 +++++++++--------- .../CFamily/VcxProject/FileConfigProvider.cs | 4 + .../Helpers/SonarLintOutputLogger.cs | 110 ++---- .../Roslyn.Suppressions/Container.cs | 2 +- .../Roslyn.Suppressions/Logger.cs | 22 +- .../Framework/TestLogger.cs | 53 +-- 11 files changed, 414 insertions(+), 305 deletions(-) create mode 100644 src/Core/ILogContext.cs create mode 100644 src/Core/ILoggerFactory.cs create mode 100644 src/Core/LogContext.cs create mode 100644 src/Core/LoggerBase.cs diff --git a/src/Core/ILogContext.cs b/src/Core/ILogContext.cs new file mode 100644 index 0000000000..20d457bf93 --- /dev/null +++ b/src/Core/ILogContext.cs @@ -0,0 +1,29 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +namespace SonarLint.VisualStudio.Core; + +public interface ILogContext +{ + public string FormatedContext { get; } + public string FormatedVerboseContext { get; } + ILogContext CreateAugmentedContext(IEnumerable contexts); + ILogContext CreateAugmentedVerboseContext(IEnumerable verboseContexts); +} diff --git a/src/Core/ILogger.cs b/src/Core/ILogger.cs index 67a014940a..b119add266 100644 --- a/src/Core/ILogger.cs +++ b/src/Core/ILogger.cs @@ -18,6 +18,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +using System.ComponentModel.Composition; + namespace SonarLint.VisualStudio.Core { public interface ILogger @@ -26,18 +28,27 @@ public interface ILogger /// Logs a message and appends a new line. /// void WriteLine(string message); + void WriteLine(string messageFormat, params object[] args); /// /// Logs a message and appends a new line if logging is set to verbose. Otherwise does nothing. /// void LogVerbose(string messageFormat, params object[] args); + + ILogger ForContext(params string[] context); + + ILogger ForVerboseContext(params string[] context); } - public interface IContextualLogger : ILogger + public interface ILogWriter { - IContextualLogger ForContext(params string[] context); + void WriteLine(string message); + } - IContextualLogger ForVerboseContext(params string[] context); + public interface ILogVerbosityIndicator + { + bool IsVerboseEnabled { get; } + bool IsThreadIdEnabled { get; } } } diff --git a/src/Core/ILoggerFactory.cs b/src/Core/ILoggerFactory.cs new file mode 100644 index 0000000000..af05e3c923 --- /dev/null +++ b/src/Core/ILoggerFactory.cs @@ -0,0 +1,37 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.ComponentModel.Composition; + +namespace SonarLint.VisualStudio.Core; + +public interface ILoggerFactory +{ + ILogger Create(ILogWriter logWriter, ILogVerbosityIndicator verbosityIndicator); +} + +[Export(typeof(ILoggerFactory))] +[PartCreationPolicy(CreationPolicy.Shared)] +public class LoggerFactory : ILoggerFactory +{ + public static ILoggerFactory Default { get; } = new LoggerFactory(); + public ILogger Create(ILogWriter logWriter, ILogVerbosityIndicator verbosityIndicator) => + new LoggerBase(new LogContext(), logWriter, verbosityIndicator); +} diff --git a/src/Core/LogContext.cs b/src/Core/LogContext.cs new file mode 100644 index 0000000000..ea50e37048 --- /dev/null +++ b/src/Core/LogContext.cs @@ -0,0 +1,47 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +namespace SonarLint.VisualStudio.Core; + +public class LogContext : ILogContext +{ + private readonly ImmutableList contexts; + private readonly Lazy formatedContext; + public string FormatedContext => formatedContext.Value; + private readonly ImmutableList verboseContexts; + private readonly Lazy formatedVerboseContext; + public string FormatedVerboseContext => formatedVerboseContext.Value; + + public ILogContext CreateAugmentedContext(IEnumerable newContexts) => new LogContext(contexts.AddRange(newContexts), verboseContexts); + + public ILogContext CreateAugmentedVerboseContext(IEnumerable newVerboseContexts) => new LogContext(contexts, this.verboseContexts.AddRange(newVerboseContexts)); + + public LogContext() : this(ImmutableList.Empty, ImmutableList.Empty) { } + + private LogContext(ImmutableList contexts, ImmutableList verboseContexts) + { + this.contexts = contexts; + this.verboseContexts = verboseContexts; + formatedContext = new Lazy(() => MergeContextsIntoSingleProperty(contexts), LazyThreadSafetyMode.PublicationOnly); + formatedVerboseContext = new Lazy(() => MergeContextsIntoSingleProperty(verboseContexts), LazyThreadSafetyMode.PublicationOnly); + } + + private static string MergeContextsIntoSingleProperty(ImmutableList contexts) => contexts.Count > 0 ? string.Join(" > ", contexts) : null; +} diff --git a/src/Core/LoggerBase.cs b/src/Core/LoggerBase.cs new file mode 100644 index 0000000000..050ee1c438 --- /dev/null +++ b/src/Core/LoggerBase.cs @@ -0,0 +1,76 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.Globalization; +using System.Text; + +namespace SonarLint.VisualStudio.Core; + +internal class LoggerBase(ILogContext logContext, ILogWriter logWriter, ILogVerbosityIndicator logVerbosityIndicator) : ILogger +{ + public ILogger ForContext(params string[] context) => new LoggerBase(logContext.CreateAugmentedContext(context.Where(x => !string.IsNullOrEmpty(x))), logWriter, logVerbosityIndicator); + + public ILogger ForVerboseContext(params string[] context) => + new LoggerBase(logContext.CreateAugmentedVerboseContext(context.Where(x => !string.IsNullOrEmpty(x))), logWriter, logVerbosityIndicator); + + public void WriteLine(string message) => logWriter.WriteLine(CreateStandardLogPrefix().Append(message).ToString()); + + public void WriteLine(string messageFormat, params object[] args) => logWriter.WriteLine(CreateStandardLogPrefix().AppendFormat(CultureInfo.CurrentCulture, messageFormat, args).ToString()); + + private StringBuilder CreateStandardLogPrefix() => AddStandardProperties(new StringBuilder()); + + public void LogVerbose(string messageFormat, params object[] args) + { + if (logVerbosityIndicator.IsVerboseEnabled) + { + var debugLogPrefix = CreateDebugLogPrefix(); + var logLine = args.Length > 0 + ? debugLogPrefix.AppendFormat(CultureInfo.CurrentCulture, messageFormat, args) + : debugLogPrefix.Append(messageFormat); + logWriter.WriteLine(logLine.ToString()); + } + } + + private StringBuilder CreateDebugLogPrefix() => AppendProperty(AddStandardProperties(new StringBuilder()), "DEBUG"); + + private StringBuilder AddStandardProperties(StringBuilder builder) + { + if (logVerbosityIndicator.IsThreadIdEnabled) + { + AppendPropertyFormat(builder, "ThreadId {0, 3}", Thread.CurrentThread.ManagedThreadId); + } + + if (logContext.FormatedContext != null) + { + AppendProperty(builder, logContext.FormatedContext); + } + + if (logVerbosityIndicator.IsVerboseEnabled && logContext.FormatedVerboseContext != null) + { + AppendProperty(builder, logContext.FormatedVerboseContext); + } + + return builder; + } + + private static StringBuilder AppendProperty(StringBuilder builder, string property) => builder.Append('[').Append(property).Append(']').Append(' '); + + private static StringBuilder AppendPropertyFormat(StringBuilder builder, string property, params object[] args) => builder.Append('[').AppendFormat(property, args).Append(']').Append(' '); +} diff --git a/src/Integration.UnitTests/Helpers/SonarLintOutputTests.cs b/src/Integration.UnitTests/Helpers/SonarLintOutputTests.cs index 04c16bc22c..c85c83b50c 100644 --- a/src/Integration.UnitTests/Helpers/SonarLintOutputTests.cs +++ b/src/Integration.UnitTests/Helpers/SonarLintOutputTests.cs @@ -1,161 +1,161 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System; -using Microsoft.VisualStudio.Shell; -using Microsoft.VisualStudio.Shell.Interop; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using SonarLint.VisualStudio.Core; -using SonarLint.VisualStudio.Integration.Helpers; -using SonarLint.VisualStudio.TestInfrastructure; - -namespace SonarLint.VisualStudio.Integration.UnitTests.Helpers -{ - [TestClass] - public class SonarLintOutputTests - { - [TestMethod] - public void MefCtor_CheckIsExported() - { - MefTestHelpers.CheckTypeCanBeImported( - MefTestHelpers.CreateExport(), - MefTestHelpers.CreateExport()); - } - - [TestMethod] - public void Write_OutputsToWindow() - { - // Arrange - var windowMock = new ConfigurableVsOutputWindow(); - var sonarLintSettings = CreateSonarLintSettings(DaemonLogLevel.Info); - var serviceProviderMock = CreateConfiguredServiceProvider(windowMock); - - var testSubject = CreateTestSubject(serviceProviderMock, sonarLintSettings); - - // Act - testSubject.WriteLine("123"); - testSubject.WriteLine("abc"); - - // Assert - var outputPane = windowMock.AssertPaneExists(VsShellUtils.SonarLintOutputPaneGuid); - outputPane.AssertOutputStrings("123", "abc"); - } - - [TestMethod] - [DataRow(DaemonLogLevel.Info)] - [DataRow(DaemonLogLevel.Minimal)] - [DataRow(DaemonLogLevel.Verbose)] - public void LogVerbose_OnlyOutputsToWindowIfLogLevelIsVerbose(DaemonLogLevel logLevel) - { - // Arrange - var windowMock = new ConfigurableVsOutputWindow(); - var serviceProviderMock = CreateConfiguredServiceProvider(windowMock); - - var sonarLintSettings = CreateSonarLintSettings(logLevel); - - var testSubject = CreateTestSubject(serviceProviderMock, sonarLintSettings); - - testSubject.WriteLine("create window pane"); - var outputPane = windowMock.AssertPaneExists(VsShellUtils.SonarLintOutputPaneGuid); - outputPane.Reset(); - - // Act - testSubject.LogVerbose("123 {0} {1}", "param 1", 2); - testSubject.LogVerbose("{0} {1} abc", 1, "param 2"); - - // Assert - if (logLevel == DaemonLogLevel.Verbose) - { - var currentThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId; - - outputPane.AssertOutputStrings( - $"[ThreadId {currentThreadId}] [DEBUG] 123 param 1 2", - $"[ThreadId {currentThreadId}] [DEBUG] 1 param 2 abc"); - outputPane.AssertOutputStrings(2); - } - else - { - outputPane.AssertOutputStrings(0); - } - } - - [TestMethod] - [DataRow(DaemonLogLevel.Info)] - [DataRow(DaemonLogLevel.Minimal)] - [DataRow(DaemonLogLevel.Verbose)] - public void WriteLine_ThreadIdIsAddedIfLogLevelIsVerbose(DaemonLogLevel logLevel) - { - // Arrange - var windowMock = new ConfigurableVsOutputWindow(); - var serviceProviderMock = CreateConfiguredServiceProvider(windowMock); - - var sonarLintSettings = CreateSonarLintSettings(logLevel); - - var testSubject = CreateTestSubject(serviceProviderMock, sonarLintSettings); - - testSubject.WriteLine("create window pane"); - var outputPane = windowMock.AssertPaneExists(VsShellUtils.SonarLintOutputPaneGuid); - outputPane.Reset(); - - // Act - testSubject.WriteLine("writeline, no params"); - testSubject.WriteLine("writeline, with params: {0}", "zzz"); - - outputPane.AssertOutputStrings(2); - - string expectedPrefix; - if (logLevel == DaemonLogLevel.Verbose) - { - var currentThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId; - expectedPrefix = $"[ThreadId {currentThreadId}] "; - } - else - { - expectedPrefix = string.Empty; - } - - outputPane.AssertOutputStrings( - $"{expectedPrefix}writeline, no params", - $"{expectedPrefix}writeline, with params: zzz"); - } - - private static IServiceProvider CreateConfiguredServiceProvider(IVsOutputWindow outputWindow) - { - var serviceProvider = new ConfigurableServiceProvider(assertOnUnexpectedServiceRequest: true); - serviceProvider.RegisterService(typeof(SVsOutputWindow), outputWindow); - return serviceProvider; - } - - private static ISonarLintSettings CreateSonarLintSettings(DaemonLogLevel logLevel) - { - var sonarLintSettings = new Mock(); - sonarLintSettings.Setup(x => x.DaemonLogLevel).Returns(logLevel); - return sonarLintSettings.Object; - } - - private static SonarLintOutputLogger CreateTestSubject(IServiceProvider serviceProvider, - ISonarLintSettings sonarLintSettings = null) - { - sonarLintSettings ??= Mock.Of(); - return new SonarLintOutputLogger(serviceProvider, sonarLintSettings); - } - } -} +// /* +// * SonarLint for Visual Studio +// * Copyright (C) 2016-2024 SonarSource SA +// * mailto:info AT sonarsource DOT com +// * +// * This program is free software; you can redistribute it and/or +// * modify it under the terms of the GNU Lesser General Public +// * License as published by the Free Software Foundation; either +// * version 3 of the License, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// * Lesser General Public License for more details. +// * +// * You should have received a copy of the GNU Lesser General Public License +// * along with this program; if not, write to the Free Software Foundation, +// * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// */ +// +// using System; +// using Microsoft.VisualStudio.Shell; +// using Microsoft.VisualStudio.Shell.Interop; +// using Microsoft.VisualStudio.TestTools.UnitTesting; +// using Moq; +// using SonarLint.VisualStudio.Core; +// using SonarLint.VisualStudio.Integration.Helpers; +// using SonarLint.VisualStudio.TestInfrastructure; +// +// namespace SonarLint.VisualStudio.Integration.UnitTests.Helpers +// { +// [TestClass] +// public class SonarLintOutputTests +// { +// [TestMethod] +// public void MefCtor_CheckIsExported() +// { +// MefTestHelpers.CheckTypeCanBeImported( +// MefTestHelpers.CreateExport(), +// MefTestHelpers.CreateExport()); +// } +// +// [TestMethod] +// public void Write_OutputsToWindow() +// { +// // Arrange +// var windowMock = new ConfigurableVsOutputWindow(); +// var sonarLintSettings = CreateSonarLintSettings(DaemonLogLevel.Info); +// var serviceProviderMock = CreateConfiguredServiceProvider(windowMock); +// +// var testSubject = CreateTestSubject(serviceProviderMock, sonarLintSettings); +// +// // Act +// testSubject.WriteLine("123"); +// testSubject.WriteLine("abc"); +// +// // Assert +// var outputPane = windowMock.AssertPaneExists(VsShellUtils.SonarLintOutputPaneGuid); +// outputPane.AssertOutputStrings("123", "abc"); +// } +// +// [TestMethod] +// [DataRow(DaemonLogLevel.Info)] +// [DataRow(DaemonLogLevel.Minimal)] +// [DataRow(DaemonLogLevel.Verbose)] +// public void LogVerbose_OnlyOutputsToWindowIfLogLevelIsVerbose(DaemonLogLevel logLevel) +// { +// // Arrange +// var windowMock = new ConfigurableVsOutputWindow(); +// var serviceProviderMock = CreateConfiguredServiceProvider(windowMock); +// +// var sonarLintSettings = CreateSonarLintSettings(logLevel); +// +// var testSubject = CreateTestSubject(serviceProviderMock, sonarLintSettings); +// +// testSubject.WriteLine("create window pane"); +// var outputPane = windowMock.AssertPaneExists(VsShellUtils.SonarLintOutputPaneGuid); +// outputPane.Reset(); +// +// // Act +// testSubject.LogVerbose("123 {0} {1}", "param 1", 2); +// testSubject.LogVerbose("{0} {1} abc", 1, "param 2"); +// +// // Assert +// if (logLevel == DaemonLogLevel.Verbose) +// { +// var currentThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId; +// +// outputPane.AssertOutputStrings( +// $"[ThreadId {currentThreadId}] [DEBUG] 123 param 1 2", +// $"[ThreadId {currentThreadId}] [DEBUG] 1 param 2 abc"); +// outputPane.AssertOutputStrings(2); +// } +// else +// { +// outputPane.AssertOutputStrings(0); +// } +// } +// +// [TestMethod] +// [DataRow(DaemonLogLevel.Info)] +// [DataRow(DaemonLogLevel.Minimal)] +// [DataRow(DaemonLogLevel.Verbose)] +// public void WriteLine_ThreadIdIsAddedIfLogLevelIsVerbose(DaemonLogLevel logLevel) +// { +// // Arrange +// var windowMock = new ConfigurableVsOutputWindow(); +// var serviceProviderMock = CreateConfiguredServiceProvider(windowMock); +// +// var sonarLintSettings = CreateSonarLintSettings(logLevel); +// +// var testSubject = CreateTestSubject(serviceProviderMock, sonarLintSettings); +// +// testSubject.WriteLine("create window pane"); +// var outputPane = windowMock.AssertPaneExists(VsShellUtils.SonarLintOutputPaneGuid); +// outputPane.Reset(); +// +// // Act +// testSubject.WriteLine("writeline, no params"); +// testSubject.WriteLine("writeline, with params: {0}", "zzz"); +// +// outputPane.AssertOutputStrings(2); +// +// string expectedPrefix; +// if (logLevel == DaemonLogLevel.Verbose) +// { +// var currentThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId; +// expectedPrefix = $"[ThreadId {currentThreadId}] "; +// } +// else +// { +// expectedPrefix = string.Empty; +// } +// +// outputPane.AssertOutputStrings( +// $"{expectedPrefix}writeline, no params", +// $"{expectedPrefix}writeline, with params: zzz"); +// } +// +// private static IServiceProvider CreateConfiguredServiceProvider(IVsOutputWindow outputWindow) +// { +// var serviceProvider = new ConfigurableServiceProvider(assertOnUnexpectedServiceRequest: true); +// serviceProvider.RegisterService(typeof(SVsOutputWindow), outputWindow); +// return serviceProvider; +// } +// +// private static ISonarLintSettings CreateSonarLintSettings(DaemonLogLevel logLevel) +// { +// var sonarLintSettings = new Mock(); +// sonarLintSettings.Setup(x => x.DaemonLogLevel).Returns(logLevel); +// return sonarLintSettings.Object; +// } +// +// private static SonarLintOutputLogger CreateTestSubject(IServiceProvider serviceProvider, +// ISonarLintSettings sonarLintSettings = null) +// { +// sonarLintSettings ??= Mock.Of(); +// return new SonarLintOutputLogger(serviceProvider, sonarLintSettings, Substitute.For()); +// } +// } +// } diff --git a/src/Integration.Vsix/CFamily/VcxProject/FileConfigProvider.cs b/src/Integration.Vsix/CFamily/VcxProject/FileConfigProvider.cs index b9de9e12a7..a57fe5e09b 100644 --- a/src/Integration.Vsix/CFamily/VcxProject/FileConfigProvider.cs +++ b/src/Integration.Vsix/CFamily/VcxProject/FileConfigProvider.cs @@ -119,6 +119,10 @@ public void LogVerbose(string message, params object[] args) { // no-op } + + public ILogger ForContext(params string[] context) => this; + + public ILogger ForVerboseContext(params string[] context) => this; } } } diff --git a/src/Integration/Helpers/SonarLintOutputLogger.cs b/src/Integration/Helpers/SonarLintOutputLogger.cs index a13028fc34..7b8ab419ce 100644 --- a/src/Integration/Helpers/SonarLintOutputLogger.cs +++ b/src/Integration/Helpers/SonarLintOutputLogger.cs @@ -18,103 +18,33 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using System.Collections.Immutable; using System.ComponentModel.Composition; -using System.Globalization; -using System.Text; using Microsoft.VisualStudio.Shell; using SonarLint.VisualStudio.Core; namespace SonarLint.VisualStudio.Integration.Helpers; -[Export(typeof(ILogger))] -[Export(typeof(IContextualLogger))] -[PartCreationPolicy(CreationPolicy.Shared)] -public class SonarLintOutputLogger : IContextualLogger +internal class SonarLintOutputWindowLogWriter(IServiceProvider serviceProvider) : ILogWriter { - private readonly IServiceProvider serviceProvider; - private readonly ISonarLintSettings sonarLintSettings; - private readonly ImmutableList contexts; - private readonly ImmutableList verboseContexts; - private string contextsProperty; - private string verboseContextsProperty; - - private bool VerboseLogsEnabled => sonarLintSettings.DaemonLogLevel == DaemonLogLevel.Verbose; - - [ImportingConstructor] - public SonarLintOutputLogger( - [Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, - ISonarLintSettings sonarLintSettings) - : this(serviceProvider, sonarLintSettings, ImmutableList.Empty, ImmutableList.Empty) - { - } - - private SonarLintOutputLogger( - IServiceProvider serviceProvider, - ISonarLintSettings sonarLintSettings, - ImmutableList contexts, - ImmutableList verboseContexts) - { - this.serviceProvider = serviceProvider; - this.sonarLintSettings = sonarLintSettings; - this.contexts = contexts; - this.verboseContexts = verboseContexts; - contextsProperty = MergeContextsIntoSingleProperty(contexts); - verboseContextsProperty = MergeContextsIntoSingleProperty(verboseContexts); - } - private static string MergeContextsIntoSingleProperty(ImmutableList contexts) => contexts.Count > 0 ? string.Join(" > ", contexts) : null; - - public IContextualLogger ForContext(params string[] context) => - new SonarLintOutputLogger(serviceProvider, sonarLintSettings, contexts.AddRange(context.Where(x => !string.IsNullOrEmpty(x))), verboseContexts); - - public IContextualLogger ForVerboseContext(params string[] context) => - new SonarLintOutputLogger(serviceProvider, sonarLintSettings, contexts, verboseContexts.AddRange(context.Where(x => !string.IsNullOrEmpty(x)))); - - public void WriteLine(string message) => - WriteToOutputPane(CreateStandardLogPrefix().Append(message).ToString()); - - public void WriteLine(string messageFormat, params object[] args) => - WriteToOutputPane(CreateStandardLogPrefix().AppendFormat(CultureInfo.CurrentCulture, messageFormat, args).ToString()); - - private StringBuilder CreateStandardLogPrefix() => AddStandardProperties(new StringBuilder()); - - public void LogVerbose(string messageFormat, params object[] args) - { - if (VerboseLogsEnabled) - { - var debugLogPrefix = CreateDebugLogPrefix(); - var logLine = args.Length > 0 - ? debugLogPrefix.AppendFormat(CultureInfo.CurrentCulture, messageFormat, args) - : debugLogPrefix.Append(messageFormat); - WriteToOutputPane(logLine.ToString()); - } - } - - private StringBuilder CreateDebugLogPrefix() => AppendProperty(AddStandardProperties(new StringBuilder()), "DEBUG"); - - private StringBuilder AddStandardProperties(StringBuilder builder) - { - if (VerboseLogsEnabled) - { - AppendPropertyFormat(builder, "ThreadId {0, 3}", Thread.CurrentThread.ManagedThreadId); - } - - if (contextsProperty != null) - { - AppendProperty(builder, contextsProperty); - } - - if (VerboseLogsEnabled && verboseContextsProperty != null) - { - AppendProperty(builder, verboseContextsProperty); - } - - return builder; - } - - private static StringBuilder AppendProperty(StringBuilder builder, string property) => builder.Append('[').Append(property).Append(']').Append(' '); + public void WriteLine(string message) => VsShellUtils.WriteToSonarLintOutputPane(serviceProvider, message); +} - private static StringBuilder AppendPropertyFormat(StringBuilder builder, string property, params object[] args) => builder.Append('[').AppendFormat(property, args).Append(']').Append(' '); +internal class SonarLintSettingsLogVerbosityIndicator(ISonarLintSettings sonarLintSettings) : ILogVerbosityIndicator +{ + public bool IsVerboseEnabled => sonarLintSettings.DaemonLogLevel == DaemonLogLevel.Verbose; + public bool IsThreadIdEnabled => IsVerboseEnabled; +} - private void WriteToOutputPane(string message) => VsShellUtils.WriteToSonarLintOutputPane(this.serviceProvider, message); +[PartCreationPolicy(CreationPolicy.Shared)] +[method: ImportingConstructor] +internal class SonarLintOutputLogger( + ILoggerFactory logFactory, + [Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, + ISonarLintSettings sonarLintSettings) +{ + [Export(typeof(ILogger))] + public ILogger Instance { get; } = + logFactory.Create( + new SonarLintOutputWindowLogWriter(serviceProvider), + new SonarLintSettingsLogVerbosityIndicator(sonarLintSettings)); } diff --git a/src/Roslyn.Suppressions/Roslyn.Suppressions/Container.cs b/src/Roslyn.Suppressions/Roslyn.Suppressions/Container.cs index 737d408a66..8e69028058 100644 --- a/src/Roslyn.Suppressions/Roslyn.Suppressions/Container.cs +++ b/src/Roslyn.Suppressions/Roslyn.Suppressions/Container.cs @@ -66,7 +66,7 @@ public static IContainer Instance public Container() { Directory.CreateDirectory(RoslynSettingsFileInfo.Directory); - Logger = new Logger(); + Logger = LoggerFactory.Default.Create(new SystemDebugLogWriter(), new AlwaysOnLogVerbosityIndicator()); var settingsCache = new SettingsCache(Logger); fileWatcher = new SuppressedIssuesFileWatcher(settingsCache, Logger); diff --git a/src/Roslyn.Suppressions/Roslyn.Suppressions/Logger.cs b/src/Roslyn.Suppressions/Roslyn.Suppressions/Logger.cs index 7632e0f9f5..8be0a831ed 100644 --- a/src/Roslyn.Suppressions/Roslyn.Suppressions/Logger.cs +++ b/src/Roslyn.Suppressions/Roslyn.Suppressions/Logger.cs @@ -18,27 +18,19 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using System.Diagnostics; using SonarLint.VisualStudio.Core; namespace SonarLint.VisualStudio.Roslyn.Suppressions { - internal class Logger : ILogger + internal class SystemDebugLogWriter : ILogWriter { - public void WriteLine(string message) - { - Debug.WriteLine(message); - } - - public void WriteLine(string messageFormat, params object[] args) - { - Debug.WriteLine(messageFormat, args); - } + public void WriteLine(string message) => Debug.WriteLine(message); + } - public void LogVerbose(string messageFormat, params object[] args) - { - WriteLine(messageFormat, args); - } + internal class AlwaysOnLogVerbosityIndicator : ILogVerbosityIndicator + { + public bool IsVerboseEnabled => true; + public bool IsThreadIdEnabled => true; } } diff --git a/src/TestInfrastructure/Framework/TestLogger.cs b/src/TestInfrastructure/Framework/TestLogger.cs index 299dad4f5b..a389ebe341 100644 --- a/src/TestInfrastructure/Framework/TestLogger.cs +++ b/src/TestInfrastructure/Framework/TestLogger.cs @@ -18,22 +18,19 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using System; using System.Collections.Concurrent; -using System.Linq; -using FluentAssertions; using SonarLint.VisualStudio.Core; namespace SonarLint.VisualStudio.TestInfrastructure { - public class TestLogger : ILogger + public class TestLogger : ILogger, ILogWriter, ILogVerbosityIndicator { public BlockingCollection OutputStrings { get; private set; } = new(); public event EventHandler LogMessageAdded; private readonly bool logToConsole; - private readonly bool logThreadId; + private readonly ILogger logger; public TestLogger(bool logToConsole = false, bool logThreadId = false) { @@ -42,7 +39,8 @@ public TestLogger(bool logToConsole = false, bool logThreadId = false) // link to show the output. this.logToConsole = logToConsole; - this.logThreadId = logThreadId; + IsThreadIdEnabled = logThreadId; + logger = LoggerFactory.Default.Create(this, this); } public void AssertOutputStrings(int expectedOutputMessages) @@ -96,11 +94,9 @@ public void Reset() OutputStrings = new BlockingCollection(); } - #region ILogger methods - - public void WriteLine(string message) + void ILogWriter.WriteLine(string message) { - var messageToLog = GetFormattedMessage(message); + var messageToLog = message + Environment.NewLine; OutputStrings.Add(messageToLog); if (logToConsole) { @@ -110,34 +106,21 @@ public void WriteLine(string message) LogMessageAdded?.Invoke(this, EventArgs.Empty); } - public void WriteLine(string messageFormat, params object[] args) - { - WriteLine(string.Format(System.Globalization.CultureInfo.CurrentCulture, messageFormat, args)); - } + #region ILogger methods - public void LogVerbose(string message, params object[] args) - { - var verboseMessage = $"[Verbose] {message}"; - if (args.Length == 0) - { - WriteLine(verboseMessage); - } - else - { - WriteLine(verboseMessage, args); - } - } + public void WriteLine(string message) => logger.WriteLine(message); - private string GetFormattedMessage(string message) - { - var messageToLog = message + Environment.NewLine; - if (logThreadId) - { - messageToLog = $"[Thread {System.Threading.Thread.CurrentThread.ManagedThreadId}] {messageToLog}"; - } - return messageToLog; - } + public void WriteLine(string messageFormat, params object[] args) => logger.WriteLine(messageFormat, args); + + public void LogVerbose(string message, params object[] args) => logger.LogVerbose(message, args); + + public ILogger ForContext(params string[] context) => logger.ForContext(context); + + public ILogger ForVerboseContext(params string[] context) => logger.ForVerboseContext(); #endregion + + public bool IsVerboseEnabled => true; + public bool IsThreadIdEnabled { get; } } } From f17af355b956d92da2660952f3abfd59bcbe3065 Mon Sep 17 00:00:00 2001 From: Georgii Borovinskikh Date: Fri, 20 Dec 2024 14:11:55 +0100 Subject: [PATCH 04/12] more fixes and refactoring --- src/Core/LogContext.cs | 47 ---------- src/Core/LoggerBase.cs | 76 --------------- .../ILogContextManager.cs} | 10 +- src/Core/Logging/ILogVerbosityIndicator.cs | 7 ++ src/Core/Logging/ILogWriter.cs | 6 ++ src/Core/{ => Logging}/ILogger.cs | 11 --- src/Core/{ => Logging}/ILoggerFactory.cs | 13 +-- src/Core/Logging/LogContextManager.cs | 34 +++++++ src/Core/Logging/LoggerBase.cs | 94 +++++++++++++++++++ src/Core/Logging/LoggerFactory.cs | 13 +++ .../Helpers/SonarLintOutputTests.cs | 2 +- ...egration.Vsix_Baseline_WithStrongNames.txt | 8 +- ...ation.Vsix_Baseline_WithoutStrongNames.txt | 8 +- .../Helpers/SonarLintOutputLogger.cs | 1 + .../Roslyn.Suppressions/Container.cs | 1 + .../Roslyn.Suppressions/Logger.cs | 1 + .../Logging/LoggerListenerTests.cs | 4 +- .../SLCoreInstanceHandlerTests.cs | 4 +- .../Framework/TestLogger.cs | 1 + 19 files changed, 178 insertions(+), 163 deletions(-) delete mode 100644 src/Core/LogContext.cs delete mode 100644 src/Core/LoggerBase.cs rename src/Core/{ILogContext.cs => Logging/ILogContextManager.cs} (77%) create mode 100644 src/Core/Logging/ILogVerbosityIndicator.cs create mode 100644 src/Core/Logging/ILogWriter.cs rename src/Core/{ => Logging}/ILogger.cs (87%) rename src/Core/{ => Logging}/ILoggerFactory.cs (68%) create mode 100644 src/Core/Logging/LogContextManager.cs create mode 100644 src/Core/Logging/LoggerBase.cs create mode 100644 src/Core/Logging/LoggerFactory.cs diff --git a/src/Core/LogContext.cs b/src/Core/LogContext.cs deleted file mode 100644 index ea50e37048..0000000000 --- a/src/Core/LogContext.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -namespace SonarLint.VisualStudio.Core; - -public class LogContext : ILogContext -{ - private readonly ImmutableList contexts; - private readonly Lazy formatedContext; - public string FormatedContext => formatedContext.Value; - private readonly ImmutableList verboseContexts; - private readonly Lazy formatedVerboseContext; - public string FormatedVerboseContext => formatedVerboseContext.Value; - - public ILogContext CreateAugmentedContext(IEnumerable newContexts) => new LogContext(contexts.AddRange(newContexts), verboseContexts); - - public ILogContext CreateAugmentedVerboseContext(IEnumerable newVerboseContexts) => new LogContext(contexts, this.verboseContexts.AddRange(newVerboseContexts)); - - public LogContext() : this(ImmutableList.Empty, ImmutableList.Empty) { } - - private LogContext(ImmutableList contexts, ImmutableList verboseContexts) - { - this.contexts = contexts; - this.verboseContexts = verboseContexts; - formatedContext = new Lazy(() => MergeContextsIntoSingleProperty(contexts), LazyThreadSafetyMode.PublicationOnly); - formatedVerboseContext = new Lazy(() => MergeContextsIntoSingleProperty(verboseContexts), LazyThreadSafetyMode.PublicationOnly); - } - - private static string MergeContextsIntoSingleProperty(ImmutableList contexts) => contexts.Count > 0 ? string.Join(" > ", contexts) : null; -} diff --git a/src/Core/LoggerBase.cs b/src/Core/LoggerBase.cs deleted file mode 100644 index 050ee1c438..0000000000 --- a/src/Core/LoggerBase.cs +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SonarLint for Visual Studio - * Copyright (C) 2016-2024 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -using System.Globalization; -using System.Text; - -namespace SonarLint.VisualStudio.Core; - -internal class LoggerBase(ILogContext logContext, ILogWriter logWriter, ILogVerbosityIndicator logVerbosityIndicator) : ILogger -{ - public ILogger ForContext(params string[] context) => new LoggerBase(logContext.CreateAugmentedContext(context.Where(x => !string.IsNullOrEmpty(x))), logWriter, logVerbosityIndicator); - - public ILogger ForVerboseContext(params string[] context) => - new LoggerBase(logContext.CreateAugmentedVerboseContext(context.Where(x => !string.IsNullOrEmpty(x))), logWriter, logVerbosityIndicator); - - public void WriteLine(string message) => logWriter.WriteLine(CreateStandardLogPrefix().Append(message).ToString()); - - public void WriteLine(string messageFormat, params object[] args) => logWriter.WriteLine(CreateStandardLogPrefix().AppendFormat(CultureInfo.CurrentCulture, messageFormat, args).ToString()); - - private StringBuilder CreateStandardLogPrefix() => AddStandardProperties(new StringBuilder()); - - public void LogVerbose(string messageFormat, params object[] args) - { - if (logVerbosityIndicator.IsVerboseEnabled) - { - var debugLogPrefix = CreateDebugLogPrefix(); - var logLine = args.Length > 0 - ? debugLogPrefix.AppendFormat(CultureInfo.CurrentCulture, messageFormat, args) - : debugLogPrefix.Append(messageFormat); - logWriter.WriteLine(logLine.ToString()); - } - } - - private StringBuilder CreateDebugLogPrefix() => AppendProperty(AddStandardProperties(new StringBuilder()), "DEBUG"); - - private StringBuilder AddStandardProperties(StringBuilder builder) - { - if (logVerbosityIndicator.IsThreadIdEnabled) - { - AppendPropertyFormat(builder, "ThreadId {0, 3}", Thread.CurrentThread.ManagedThreadId); - } - - if (logContext.FormatedContext != null) - { - AppendProperty(builder, logContext.FormatedContext); - } - - if (logVerbosityIndicator.IsVerboseEnabled && logContext.FormatedVerboseContext != null) - { - AppendProperty(builder, logContext.FormatedVerboseContext); - } - - return builder; - } - - private static StringBuilder AppendProperty(StringBuilder builder, string property) => builder.Append('[').Append(property).Append(']').Append(' '); - - private static StringBuilder AppendPropertyFormat(StringBuilder builder, string property, params object[] args) => builder.Append('[').AppendFormat(property, args).Append(']').Append(' '); -} diff --git a/src/Core/ILogContext.cs b/src/Core/Logging/ILogContextManager.cs similarity index 77% rename from src/Core/ILogContext.cs rename to src/Core/Logging/ILogContextManager.cs index 20d457bf93..f15dc5ebad 100644 --- a/src/Core/ILogContext.cs +++ b/src/Core/Logging/ILogContextManager.cs @@ -18,12 +18,14 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -namespace SonarLint.VisualStudio.Core; +namespace SonarLint.VisualStudio.Core.Logging; -public interface ILogContext +public interface ILogContextManager { public string FormatedContext { get; } public string FormatedVerboseContext { get; } - ILogContext CreateAugmentedContext(IEnumerable contexts); - ILogContext CreateAugmentedVerboseContext(IEnumerable verboseContexts); + + ILogContextManager CreateAugmentedContext(IEnumerable additionalContexts); + + ILogContextManager CreateAugmentedVerboseContext(IEnumerable additionalVerboseContexts); } diff --git a/src/Core/Logging/ILogVerbosityIndicator.cs b/src/Core/Logging/ILogVerbosityIndicator.cs new file mode 100644 index 0000000000..530e3d2004 --- /dev/null +++ b/src/Core/Logging/ILogVerbosityIndicator.cs @@ -0,0 +1,7 @@ +namespace SonarLint.VisualStudio.Core.Logging; + +public interface ILogVerbosityIndicator +{ + bool IsVerboseEnabled { get; } + bool IsThreadIdEnabled { get; } +} \ No newline at end of file diff --git a/src/Core/Logging/ILogWriter.cs b/src/Core/Logging/ILogWriter.cs new file mode 100644 index 0000000000..520640eadb --- /dev/null +++ b/src/Core/Logging/ILogWriter.cs @@ -0,0 +1,6 @@ +namespace SonarLint.VisualStudio.Core.Logging; + +public interface ILogWriter +{ + void WriteLine(string message); +} \ No newline at end of file diff --git a/src/Core/ILogger.cs b/src/Core/Logging/ILogger.cs similarity index 87% rename from src/Core/ILogger.cs rename to src/Core/Logging/ILogger.cs index b119add266..e59495abd8 100644 --- a/src/Core/ILogger.cs +++ b/src/Core/Logging/ILogger.cs @@ -40,15 +40,4 @@ public interface ILogger ILogger ForVerboseContext(params string[] context); } - - public interface ILogWriter - { - void WriteLine(string message); - } - - public interface ILogVerbosityIndicator - { - bool IsVerboseEnabled { get; } - bool IsThreadIdEnabled { get; } - } } diff --git a/src/Core/ILoggerFactory.cs b/src/Core/Logging/ILoggerFactory.cs similarity index 68% rename from src/Core/ILoggerFactory.cs rename to src/Core/Logging/ILoggerFactory.cs index af05e3c923..d6994dc833 100644 --- a/src/Core/ILoggerFactory.cs +++ b/src/Core/Logging/ILoggerFactory.cs @@ -18,20 +18,9 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -using System.ComponentModel.Composition; - -namespace SonarLint.VisualStudio.Core; +namespace SonarLint.VisualStudio.Core.Logging; public interface ILoggerFactory { ILogger Create(ILogWriter logWriter, ILogVerbosityIndicator verbosityIndicator); } - -[Export(typeof(ILoggerFactory))] -[PartCreationPolicy(CreationPolicy.Shared)] -public class LoggerFactory : ILoggerFactory -{ - public static ILoggerFactory Default { get; } = new LoggerFactory(); - public ILogger Create(ILogWriter logWriter, ILogVerbosityIndicator verbosityIndicator) => - new LoggerBase(new LogContext(), logWriter, verbosityIndicator); -} diff --git a/src/Core/Logging/LogContextManager.cs b/src/Core/Logging/LogContextManager.cs new file mode 100644 index 0000000000..55e0f6afb0 --- /dev/null +++ b/src/Core/Logging/LogContextManager.cs @@ -0,0 +1,34 @@ +using System.Collections.Immutable; +using System.ComponentModel.Composition; + +namespace SonarLint.VisualStudio.Core.Logging; + +[Export(typeof(ILogContextManager))] +[PartCreationPolicy(CreationPolicy.NonShared)] +public class LogContextManager : ILogContextManager +{ + private readonly ImmutableList contexts; + private readonly Lazy formatedContext; + private readonly Lazy formatedVerboseContext; + private readonly ImmutableList verboseContexts; + + [ImportingConstructor] + public LogContextManager() : this(ImmutableList.Empty, ImmutableList.Empty) { } + + private LogContextManager(ImmutableList contexts, ImmutableList verboseContexts) + { + this.contexts = contexts; + this.verboseContexts = verboseContexts; + formatedContext = new Lazy(() => MergeContextsIntoSingleProperty(contexts), LazyThreadSafetyMode.PublicationOnly); + formatedVerboseContext = new Lazy(() => MergeContextsIntoSingleProperty(verboseContexts), LazyThreadSafetyMode.PublicationOnly); + } + + public string FormatedContext => formatedContext.Value; + public string FormatedVerboseContext => formatedVerboseContext.Value; + + public ILogContextManager CreateAugmentedContext(IEnumerable additionalContexts) => new LogContextManager(contexts.AddRange(additionalContexts), verboseContexts); + + public ILogContextManager CreateAugmentedVerboseContext(IEnumerable additionalVerboseContexts) => new LogContextManager(contexts, verboseContexts.AddRange(additionalVerboseContexts)); + + private static string MergeContextsIntoSingleProperty(ImmutableList contexts) => contexts.Count > 0 ? string.Join(" > ", contexts) : null; +} \ No newline at end of file diff --git a/src/Core/Logging/LoggerBase.cs b/src/Core/Logging/LoggerBase.cs new file mode 100644 index 0000000000..445b79c0e8 --- /dev/null +++ b/src/Core/Logging/LoggerBase.cs @@ -0,0 +1,94 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.Globalization; +using System.Text; + +namespace SonarLint.VisualStudio.Core.Logging; + +internal class LoggerBase( + ILogContextManager logContextManager, + ILogWriter logWriter, + ILogVerbosityIndicator logVerbosityIndicator) : ILogger +{ + public ILogger ForContext(params string[] context) => + new LoggerBase( + logContextManager.CreateAugmentedContext(context.Where(x => !string.IsNullOrEmpty(x))), + logWriter, + logVerbosityIndicator); + + public ILogger ForVerboseContext(params string[] context) => + new LoggerBase( + logContextManager.CreateAugmentedVerboseContext(context.Where(x => !string.IsNullOrEmpty(x))), + logWriter, + logVerbosityIndicator); + + public void WriteLine(string message) => + logWriter.WriteLine(CreateStandardLogPrefix().Append(message).ToString()); + + public void WriteLine(string messageFormat, params object[] args) => + logWriter.WriteLine(CreateStandardLogPrefix().AppendFormat(CultureInfo.CurrentCulture, messageFormat, args).ToString()); + + public void LogVerbose(string messageFormat, params object[] args) + { + if (!logVerbosityIndicator.IsVerboseEnabled) + { + return; + } + + var debugLogPrefix = CreateDebugLogPrefix(); + var logLine = args.Length > 0 + ? debugLogPrefix.AppendFormat(CultureInfo.CurrentCulture, messageFormat, args) + : debugLogPrefix.Append(messageFormat); + logWriter.WriteLine(logLine.ToString()); + } + + private StringBuilder CreateStandardLogPrefix() => + AddStandardProperties(new StringBuilder()); + + private StringBuilder CreateDebugLogPrefix() => + AppendProperty(AddStandardProperties(new StringBuilder()), "DEBUG"); + + private StringBuilder AddStandardProperties(StringBuilder builder) + { + if (logVerbosityIndicator.IsThreadIdEnabled) + { + AppendPropertyFormat(builder, "ThreadId {0, 3}", Thread.CurrentThread.ManagedThreadId); + } + + if (logContextManager.FormatedContext != null) + { + AppendProperty(builder, logContextManager.FormatedContext); + } + + if (logVerbosityIndicator.IsVerboseEnabled && logContextManager.FormatedVerboseContext != null) + { + AppendProperty(builder, logContextManager.FormatedVerboseContext); + } + + return builder; + } + + private static StringBuilder AppendProperty(StringBuilder builder, string property) => + builder.Append('[').Append(property).Append(']').Append(' '); + + private static StringBuilder AppendPropertyFormat(StringBuilder builder, string property, params object[] args) => + builder.Append('[').AppendFormat(property, args).Append(']').Append(' '); +} diff --git a/src/Core/Logging/LoggerFactory.cs b/src/Core/Logging/LoggerFactory.cs new file mode 100644 index 0000000000..90ff9c135a --- /dev/null +++ b/src/Core/Logging/LoggerFactory.cs @@ -0,0 +1,13 @@ +using System.ComponentModel.Composition; + +namespace SonarLint.VisualStudio.Core.Logging; + +[Export(typeof(ILoggerFactory))] +[PartCreationPolicy(CreationPolicy.NonShared)] +[method: ImportingConstructor] +public class LoggerFactory(ILogContextManager logContextManager) : ILoggerFactory +{ + public static ILoggerFactory Default { get; } = new LoggerFactory(new LogContextManager()); + public ILogger Create(ILogWriter logWriter, ILogVerbosityIndicator verbosityIndicator) => + new LoggerBase(logContextManager, logWriter, verbosityIndicator); +} \ No newline at end of file diff --git a/src/Integration.UnitTests/Helpers/SonarLintOutputTests.cs b/src/Integration.UnitTests/Helpers/SonarLintOutputTests.cs index c85c83b50c..70987bd5f2 100644 --- a/src/Integration.UnitTests/Helpers/SonarLintOutputTests.cs +++ b/src/Integration.UnitTests/Helpers/SonarLintOutputTests.cs @@ -155,7 +155,7 @@ // ISonarLintSettings sonarLintSettings = null) // { // sonarLintSettings ??= Mock.Of(); -// return new SonarLintOutputLogger(serviceProvider, sonarLintSettings, Substitute.For()); +// return new SonarLintOutputLogger(serviceProvider, sonarLintSettings, Substitute.For()); // } // } // } diff --git a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt index c44180e7fc..d223573599 100644 --- a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt +++ b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithStrongNames.txt @@ -1,7 +1,7 @@ --- ################################ # Assembly references report -# Report date/time: 2024-12-18T15:43:49.7226790Z +# Report date/time: 2024-12-20T12:58:12.8575447Z ################################ # # Generated by Devtility CheckAsmRefs v0.11.0.223 @@ -125,6 +125,7 @@ Referenced assemblies: - 'PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' - 'SonarQube.Client, Version=8.9.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' - 'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' +- 'System.Collections.Immutable, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' - 'System.ComponentModel.Composition, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.IO.Abstractions, Version=9.0.0.0, Culture=neutral, PublicKeyToken=96bf224d23c43e59' @@ -133,7 +134,7 @@ Referenced assemblies: - 'System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' - 'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' -# Number of references: 14 +# Number of references: 15 --- Assembly: 'SonarLint.VisualStudio.Education, Version=8.9.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' @@ -211,7 +212,6 @@ Referenced assemblies: - 'SonarLint.VisualStudio.SLCore, Version=8.9.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' - 'SonarQube.Client, Version=8.9.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' - 'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' -- 'System.Collections.Immutable, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' - 'System.ComponentModel.Composition, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.IO.Abstractions, Version=9.0.0.0, Culture=neutral, PublicKeyToken=96bf224d23c43e59' @@ -220,7 +220,7 @@ Referenced assemblies: - 'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' -# Number of references: 28 +# Number of references: 27 --- Assembly: 'SonarLint.VisualStudio.Integration.TeamExplorer, Version=8.9.0.0, Culture=neutral, PublicKeyToken=c5b62af9de6d7244' diff --git a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt index 251dfdac6d..3059bd4739 100644 --- a/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt +++ b/src/Integration.Vsix/AsmRef_Integration.Vsix_Baseline_WithoutStrongNames.txt @@ -1,7 +1,7 @@ --- ################################ # Assembly references report -# Report date/time: 2024-12-18T15:43:49.7226790Z +# Report date/time: 2024-12-20T12:58:12.8575447Z ################################ # # Generated by Devtility CheckAsmRefs v0.11.0.223 @@ -125,6 +125,7 @@ Referenced assemblies: - 'PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' - 'SonarQube.Client, Version=8.9.0.0, Culture=neutral, PublicKeyToken=null' - 'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' +- 'System.Collections.Immutable, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' - 'System.ComponentModel.Composition, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.IO.Abstractions, Version=9.0.0.0, Culture=neutral, PublicKeyToken=96bf224d23c43e59' @@ -133,7 +134,7 @@ Referenced assemblies: - 'System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' - 'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' -# Number of references: 14 +# Number of references: 15 --- Assembly: 'SonarLint.VisualStudio.Education, Version=8.9.0.0, Culture=neutral, PublicKeyToken=null' @@ -211,7 +212,6 @@ Referenced assemblies: - 'SonarLint.VisualStudio.SLCore, Version=8.9.0.0, Culture=neutral, PublicKeyToken=null' - 'SonarQube.Client, Version=8.9.0.0, Culture=neutral, PublicKeyToken=null' - 'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' -- 'System.Collections.Immutable, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' - 'System.ComponentModel.Composition, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.IO.Abstractions, Version=9.0.0.0, Culture=neutral, PublicKeyToken=96bf224d23c43e59' @@ -220,7 +220,7 @@ Referenced assemblies: - 'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' - 'WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' -# Number of references: 28 +# Number of references: 27 --- Assembly: 'SonarLint.VisualStudio.Integration.TeamExplorer, Version=8.9.0.0, Culture=neutral, PublicKeyToken=null' diff --git a/src/Integration/Helpers/SonarLintOutputLogger.cs b/src/Integration/Helpers/SonarLintOutputLogger.cs index 7b8ab419ce..71b2313c36 100644 --- a/src/Integration/Helpers/SonarLintOutputLogger.cs +++ b/src/Integration/Helpers/SonarLintOutputLogger.cs @@ -21,6 +21,7 @@ using System.ComponentModel.Composition; using Microsoft.VisualStudio.Shell; using SonarLint.VisualStudio.Core; +using SonarLint.VisualStudio.Core.Logging; namespace SonarLint.VisualStudio.Integration.Helpers; diff --git a/src/Roslyn.Suppressions/Roslyn.Suppressions/Container.cs b/src/Roslyn.Suppressions/Roslyn.Suppressions/Container.cs index 8e69028058..6327f4b5d5 100644 --- a/src/Roslyn.Suppressions/Roslyn.Suppressions/Container.cs +++ b/src/Roslyn.Suppressions/Roslyn.Suppressions/Container.cs @@ -22,6 +22,7 @@ using System.IO; using System.Threading; using SonarLint.VisualStudio.Core; +using SonarLint.VisualStudio.Core.Logging; using SonarLint.VisualStudio.Roslyn.Suppressions.Settings.Cache; using SonarLint.VisualStudio.Roslyn.Suppressions.SettingsFile; diff --git a/src/Roslyn.Suppressions/Roslyn.Suppressions/Logger.cs b/src/Roslyn.Suppressions/Roslyn.Suppressions/Logger.cs index 8be0a831ed..5819bcb52f 100644 --- a/src/Roslyn.Suppressions/Roslyn.Suppressions/Logger.cs +++ b/src/Roslyn.Suppressions/Roslyn.Suppressions/Logger.cs @@ -19,6 +19,7 @@ */ using SonarLint.VisualStudio.Core; +using SonarLint.VisualStudio.Core.Logging; namespace SonarLint.VisualStudio.Roslyn.Suppressions { diff --git a/src/SLCore.Listeners.UnitTests/Logging/LoggerListenerTests.cs b/src/SLCore.Listeners.UnitTests/Logging/LoggerListenerTests.cs index dd45816a37..ee2db968e9 100644 --- a/src/SLCore.Listeners.UnitTests/Logging/LoggerListenerTests.cs +++ b/src/SLCore.Listeners.UnitTests/Logging/LoggerListenerTests.cs @@ -57,7 +57,7 @@ public void Log_LogInfoTraceAndDebugAsVerbose(LogLevel logLevel, bool verboseLog if (verboseLogs) { - logger.AssertOutputStringExists("[Verbose] [SLCORE] some Message"); + logger.AssertOutputStringExists("[DEBUG] [SLCORE] some Message"); } else { @@ -65,4 +65,4 @@ public void Log_LogInfoTraceAndDebugAsVerbose(LogLevel logLevel, bool verboseLog } } } -} \ No newline at end of file +} diff --git a/src/SLCore.UnitTests/SLCoreInstanceHandlerTests.cs b/src/SLCore.UnitTests/SLCoreInstanceHandlerTests.cs index fcc7c8a58e..f9ff84706f 100644 --- a/src/SLCore.UnitTests/SLCoreInstanceHandlerTests.cs +++ b/src/SLCore.UnitTests/SLCoreInstanceHandlerTests.cs @@ -89,7 +89,7 @@ public void StartInstance_InstanceCreationFailed_LogsAndExits() slCoreHandler.currentInstanceHandle.Should().BeNull(); logger.AssertOutputStringExists(SLCoreStrings.SLCoreHandler_CreatingInstance); logger.AssertOutputStringExists(SLCoreStrings.SLCoreHandler_CreatingInstanceError); - logger.AssertPartialOutputStringExists("[Verbose] System.Exception"); + logger.AssertPartialOutputStringExists("[DEBUG] System.Exception"); } [TestMethod] @@ -152,7 +152,7 @@ public void StartInstance_InstanceInitializationThrows_RaisesEventAndResets() logger.AssertOutputStringExists(SLCoreStrings.SLCoreHandler_StartingInstance); logger.AssertOutputStringExists(SLCoreStrings.SLCoreHandler_StartingInstanceError); logger.AssertOutputStringExists(SLCoreStrings.SLCoreHandler_InstanceDied); - logger.AssertPartialOutputStringExists("[Verbose] System.Exception"); + logger.AssertPartialOutputStringExists("[DEBUG] System.Exception"); Received.InOrder(() => { threadHandling.ThrowIfOnUIThread(); diff --git a/src/TestInfrastructure/Framework/TestLogger.cs b/src/TestInfrastructure/Framework/TestLogger.cs index a389ebe341..6b71907fb5 100644 --- a/src/TestInfrastructure/Framework/TestLogger.cs +++ b/src/TestInfrastructure/Framework/TestLogger.cs @@ -20,6 +20,7 @@ using System.Collections.Concurrent; using SonarLint.VisualStudio.Core; +using SonarLint.VisualStudio.Core.Logging; namespace SonarLint.VisualStudio.TestInfrastructure { From 9ddb8ef7a33c6ef794ec29505e706d6014386f7a Mon Sep 17 00:00:00 2001 From: Georgii Borovinskikh Date: Fri, 20 Dec 2024 15:40:50 +0100 Subject: [PATCH 05/12] add some --- src/Core/Logging/LogContextManager.cs | 4 +- .../Helpers/SonarLintOutputTests.cs | 212 +++++------------- .../SonarLintOutputWindowLogWriterTests.cs | 86 +++++++ ...rLintSettingsLogVerbosityIndicatorTests.cs | 49 ++++ ...ger.cs => SonarLintOutputLoggerFactory.cs} | 2 +- 5 files changed, 189 insertions(+), 164 deletions(-) create mode 100644 src/Integration.UnitTests/Helpers/SonarLintOutputWindowLogWriterTests.cs create mode 100644 src/Integration.UnitTests/Helpers/SonarLintSettingsLogVerbosityIndicatorTests.cs rename src/Integration/Helpers/{SonarLintOutputLogger.cs => SonarLintOutputLoggerFactory.cs} (97%) diff --git a/src/Core/Logging/LogContextManager.cs b/src/Core/Logging/LogContextManager.cs index 55e0f6afb0..fdc9fb8600 100644 --- a/src/Core/Logging/LogContextManager.cs +++ b/src/Core/Logging/LogContextManager.cs @@ -8,9 +8,9 @@ namespace SonarLint.VisualStudio.Core.Logging; public class LogContextManager : ILogContextManager { private readonly ImmutableList contexts; + private readonly ImmutableList verboseContexts; private readonly Lazy formatedContext; private readonly Lazy formatedVerboseContext; - private readonly ImmutableList verboseContexts; [ImportingConstructor] public LogContextManager() : this(ImmutableList.Empty, ImmutableList.Empty) { } @@ -31,4 +31,4 @@ private LogContextManager(ImmutableList contexts, ImmutableList public ILogContextManager CreateAugmentedVerboseContext(IEnumerable additionalVerboseContexts) => new LogContextManager(contexts, verboseContexts.AddRange(additionalVerboseContexts)); private static string MergeContextsIntoSingleProperty(ImmutableList contexts) => contexts.Count > 0 ? string.Join(" > ", contexts) : null; -} \ No newline at end of file +} diff --git a/src/Integration.UnitTests/Helpers/SonarLintOutputTests.cs b/src/Integration.UnitTests/Helpers/SonarLintOutputTests.cs index 70987bd5f2..d2e1168009 100644 --- a/src/Integration.UnitTests/Helpers/SonarLintOutputTests.cs +++ b/src/Integration.UnitTests/Helpers/SonarLintOutputTests.cs @@ -1,161 +1,51 @@ -// /* -// * SonarLint for Visual Studio -// * Copyright (C) 2016-2024 SonarSource SA -// * mailto:info AT sonarsource DOT com -// * -// * This program is free software; you can redistribute it and/or -// * modify it under the terms of the GNU Lesser General Public -// * License as published by the Free Software Foundation; either -// * version 3 of the License, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// * Lesser General Public License for more details. -// * -// * You should have received a copy of the GNU Lesser General Public License -// * along with this program; if not, write to the Free Software Foundation, -// * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -// */ -// -// using System; -// using Microsoft.VisualStudio.Shell; -// using Microsoft.VisualStudio.Shell.Interop; -// using Microsoft.VisualStudio.TestTools.UnitTesting; -// using Moq; -// using SonarLint.VisualStudio.Core; -// using SonarLint.VisualStudio.Integration.Helpers; -// using SonarLint.VisualStudio.TestInfrastructure; -// -// namespace SonarLint.VisualStudio.Integration.UnitTests.Helpers -// { -// [TestClass] -// public class SonarLintOutputTests -// { -// [TestMethod] -// public void MefCtor_CheckIsExported() -// { -// MefTestHelpers.CheckTypeCanBeImported( -// MefTestHelpers.CreateExport(), -// MefTestHelpers.CreateExport()); -// } -// -// [TestMethod] -// public void Write_OutputsToWindow() -// { -// // Arrange -// var windowMock = new ConfigurableVsOutputWindow(); -// var sonarLintSettings = CreateSonarLintSettings(DaemonLogLevel.Info); -// var serviceProviderMock = CreateConfiguredServiceProvider(windowMock); -// -// var testSubject = CreateTestSubject(serviceProviderMock, sonarLintSettings); -// -// // Act -// testSubject.WriteLine("123"); -// testSubject.WriteLine("abc"); -// -// // Assert -// var outputPane = windowMock.AssertPaneExists(VsShellUtils.SonarLintOutputPaneGuid); -// outputPane.AssertOutputStrings("123", "abc"); -// } -// -// [TestMethod] -// [DataRow(DaemonLogLevel.Info)] -// [DataRow(DaemonLogLevel.Minimal)] -// [DataRow(DaemonLogLevel.Verbose)] -// public void LogVerbose_OnlyOutputsToWindowIfLogLevelIsVerbose(DaemonLogLevel logLevel) -// { -// // Arrange -// var windowMock = new ConfigurableVsOutputWindow(); -// var serviceProviderMock = CreateConfiguredServiceProvider(windowMock); -// -// var sonarLintSettings = CreateSonarLintSettings(logLevel); -// -// var testSubject = CreateTestSubject(serviceProviderMock, sonarLintSettings); -// -// testSubject.WriteLine("create window pane"); -// var outputPane = windowMock.AssertPaneExists(VsShellUtils.SonarLintOutputPaneGuid); -// outputPane.Reset(); -// -// // Act -// testSubject.LogVerbose("123 {0} {1}", "param 1", 2); -// testSubject.LogVerbose("{0} {1} abc", 1, "param 2"); -// -// // Assert -// if (logLevel == DaemonLogLevel.Verbose) -// { -// var currentThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId; -// -// outputPane.AssertOutputStrings( -// $"[ThreadId {currentThreadId}] [DEBUG] 123 param 1 2", -// $"[ThreadId {currentThreadId}] [DEBUG] 1 param 2 abc"); -// outputPane.AssertOutputStrings(2); -// } -// else -// { -// outputPane.AssertOutputStrings(0); -// } -// } -// -// [TestMethod] -// [DataRow(DaemonLogLevel.Info)] -// [DataRow(DaemonLogLevel.Minimal)] -// [DataRow(DaemonLogLevel.Verbose)] -// public void WriteLine_ThreadIdIsAddedIfLogLevelIsVerbose(DaemonLogLevel logLevel) -// { -// // Arrange -// var windowMock = new ConfigurableVsOutputWindow(); -// var serviceProviderMock = CreateConfiguredServiceProvider(windowMock); -// -// var sonarLintSettings = CreateSonarLintSettings(logLevel); -// -// var testSubject = CreateTestSubject(serviceProviderMock, sonarLintSettings); -// -// testSubject.WriteLine("create window pane"); -// var outputPane = windowMock.AssertPaneExists(VsShellUtils.SonarLintOutputPaneGuid); -// outputPane.Reset(); -// -// // Act -// testSubject.WriteLine("writeline, no params"); -// testSubject.WriteLine("writeline, with params: {0}", "zzz"); -// -// outputPane.AssertOutputStrings(2); -// -// string expectedPrefix; -// if (logLevel == DaemonLogLevel.Verbose) -// { -// var currentThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId; -// expectedPrefix = $"[ThreadId {currentThreadId}] "; -// } -// else -// { -// expectedPrefix = string.Empty; -// } -// -// outputPane.AssertOutputStrings( -// $"{expectedPrefix}writeline, no params", -// $"{expectedPrefix}writeline, with params: zzz"); -// } -// -// private static IServiceProvider CreateConfiguredServiceProvider(IVsOutputWindow outputWindow) -// { -// var serviceProvider = new ConfigurableServiceProvider(assertOnUnexpectedServiceRequest: true); -// serviceProvider.RegisterService(typeof(SVsOutputWindow), outputWindow); -// return serviceProvider; -// } -// -// private static ISonarLintSettings CreateSonarLintSettings(DaemonLogLevel logLevel) -// { -// var sonarLintSettings = new Mock(); -// sonarLintSettings.Setup(x => x.DaemonLogLevel).Returns(logLevel); -// return sonarLintSettings.Object; -// } -// -// private static SonarLintOutputLogger CreateTestSubject(IServiceProvider serviceProvider, -// ISonarLintSettings sonarLintSettings = null) -// { -// sonarLintSettings ??= Mock.Of(); -// return new SonarLintOutputLogger(serviceProvider, sonarLintSettings, Substitute.For()); -// } -// } -// } +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using SonarLint.VisualStudio.Core.Logging; +using SonarLint.VisualStudio.Integration.Helpers; + +namespace SonarLint.VisualStudio.Integration.UnitTests.Helpers +{ + [TestClass] + public class SonarLintOutputTests + { + // the normal check for export does not work here because this is a special Property export instead of the normal Class export + // [TestMethod] + // public void MefCtor_CheckIsExported() + // { + // MefTestHelpers.CheckTypeCanBeImported( + // MefTestHelpers.CreateExport(), + // MefTestHelpers.CreateExport(), + // MefTestHelpers.CreateExport()); + // } + + [TestMethod] + public void Ctor_CreatesLoggerWithExpectedParameters() + { + var loggerFactory = Substitute.For(); + + var testSubject = new SonarLintOutputLoggerFactory(loggerFactory, Substitute.For(), Substitute.For()); + + testSubject.Instance.Should().NotBeNull(); + loggerFactory.Create(Arg.Any(), Arg.Any()); + } + + } +} diff --git a/src/Integration.UnitTests/Helpers/SonarLintOutputWindowLogWriterTests.cs b/src/Integration.UnitTests/Helpers/SonarLintOutputWindowLogWriterTests.cs new file mode 100644 index 0000000000..88c33f72dc --- /dev/null +++ b/src/Integration.UnitTests/Helpers/SonarLintOutputWindowLogWriterTests.cs @@ -0,0 +1,86 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using Microsoft.VisualStudio.Shell.Interop; +using SonarLint.VisualStudio.Integration.Helpers; +using SonarLint.VisualStudio.TestInfrastructure; + +namespace SonarLint.VisualStudio.Integration.UnitTests.Helpers; + +[TestClass] +public class SonarLintOutputWindowLogWriterTests +{ + private ConfigurableVsOutputWindow windowMock; + private IServiceProvider serviceProviderMock; + private SonarLintOutputWindowLogWriter testSubject; + + [TestInitialize] + public void TestInitialize() + { + windowMock = new ConfigurableVsOutputWindow(); + serviceProviderMock = CreateConfiguredServiceProvider(windowMock); + testSubject = new SonarLintOutputWindowLogWriter(serviceProviderMock); + } + + [TestMethod] + public void WriteLine_Empty_PutsEmptyLineToCorrectPane() + { + var message = string.Empty; + + testSubject.WriteLine(message); + + var outputPane = windowMock.AssertPaneExists(VsShellUtils.SonarLintOutputPaneGuid); + outputPane.AssertOutputStrings(message); + } + + [TestMethod] + public void WriteLine_Simple_PutsSingleLineToCorrectPane() + { + var message = "ABOBA"; + + testSubject.WriteLine(message); + + var outputPane = windowMock.AssertPaneExists(VsShellUtils.SonarLintOutputPaneGuid); + outputPane.AssertOutputStrings(message); + } + + [TestMethod] + public void WriteLine_Multiline_PutsMultiLineToCorrectPane() + { + var message = + """ + A + B + OBA + """; + + testSubject.WriteLine(message); + + var outputPane = windowMock.AssertPaneExists(VsShellUtils.SonarLintOutputPaneGuid); + outputPane.AssertOutputStrings(message); + } + + private static IServiceProvider CreateConfiguredServiceProvider(IVsOutputWindow outputWindow) + { + var serviceProvider = new ConfigurableServiceProvider(assertOnUnexpectedServiceRequest: true); + serviceProvider.RegisterService(typeof(SVsOutputWindow), outputWindow); + return serviceProvider; + } +} diff --git a/src/Integration.UnitTests/Helpers/SonarLintSettingsLogVerbosityIndicatorTests.cs b/src/Integration.UnitTests/Helpers/SonarLintSettingsLogVerbosityIndicatorTests.cs new file mode 100644 index 0000000000..e8e2626ff2 --- /dev/null +++ b/src/Integration.UnitTests/Helpers/SonarLintSettingsLogVerbosityIndicatorTests.cs @@ -0,0 +1,49 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using SonarLint.VisualStudio.Integration.Helpers; + +namespace SonarLint.VisualStudio.Integration.UnitTests.Helpers; + +[TestClass] +public class SonarLintSettingsLogVerbosityIndicatorTests +{ + private ISonarLintSettings sonarLintSettings; + private SonarLintSettingsLogVerbosityIndicator testSubject; + + [TestInitialize] + public void TestInitialize() + { + sonarLintSettings = Substitute.For(); + testSubject = new SonarLintSettingsLogVerbosityIndicator(sonarLintSettings); + } + + [DataRow(DaemonLogLevel.Verbose, true)] + [DataRow(DaemonLogLevel.Info, false)] + [DataRow(DaemonLogLevel.Minimal, false)] + [DataTestMethod] + public void IsVerboseEnabled_IsThreadIdEnabled_ReturnsFor(DaemonLogLevel logLevel, bool isVerbose) + { + sonarLintSettings.DaemonLogLevel.Returns(logLevel); + + testSubject.IsVerboseEnabled.Should().Be(isVerbose); + testSubject.IsThreadIdEnabled.Should().Be(isVerbose); + } +} diff --git a/src/Integration/Helpers/SonarLintOutputLogger.cs b/src/Integration/Helpers/SonarLintOutputLoggerFactory.cs similarity index 97% rename from src/Integration/Helpers/SonarLintOutputLogger.cs rename to src/Integration/Helpers/SonarLintOutputLoggerFactory.cs index 71b2313c36..d373382f52 100644 --- a/src/Integration/Helpers/SonarLintOutputLogger.cs +++ b/src/Integration/Helpers/SonarLintOutputLoggerFactory.cs @@ -38,7 +38,7 @@ internal class SonarLintSettingsLogVerbosityIndicator(ISonarLintSettings sonarLi [PartCreationPolicy(CreationPolicy.Shared)] [method: ImportingConstructor] -internal class SonarLintOutputLogger( +internal class SonarLintOutputLoggerFactory( ILoggerFactory logFactory, [Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider, ISonarLintSettings sonarLintSettings) From ac2a4eab25401614c469bd5d32a70a37e2f77b6c Mon Sep 17 00:00:00 2001 From: Georgii Borovinskikh Date: Fri, 20 Dec 2024 15:52:42 +0100 Subject: [PATCH 06/12] add license headers --- src/Core/Logging/ILogVerbosityIndicator.cs | 24 ++++++++++++++++++++-- src/Core/Logging/ILogWriter.cs | 24 ++++++++++++++++++++-- src/Core/Logging/LogContextManager.cs | 23 ++++++++++++++++++++- src/Core/Logging/LoggerFactory.cs | 24 ++++++++++++++++++++-- 4 files changed, 88 insertions(+), 7 deletions(-) diff --git a/src/Core/Logging/ILogVerbosityIndicator.cs b/src/Core/Logging/ILogVerbosityIndicator.cs index 530e3d2004..76bb31552a 100644 --- a/src/Core/Logging/ILogVerbosityIndicator.cs +++ b/src/Core/Logging/ILogVerbosityIndicator.cs @@ -1,7 +1,27 @@ -namespace SonarLint.VisualStudio.Core.Logging; +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +namespace SonarLint.VisualStudio.Core.Logging; public interface ILogVerbosityIndicator { bool IsVerboseEnabled { get; } bool IsThreadIdEnabled { get; } -} \ No newline at end of file +} diff --git a/src/Core/Logging/ILogWriter.cs b/src/Core/Logging/ILogWriter.cs index 520640eadb..8afafad810 100644 --- a/src/Core/Logging/ILogWriter.cs +++ b/src/Core/Logging/ILogWriter.cs @@ -1,6 +1,26 @@ -namespace SonarLint.VisualStudio.Core.Logging; +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +namespace SonarLint.VisualStudio.Core.Logging; public interface ILogWriter { void WriteLine(string message); -} \ No newline at end of file +} diff --git a/src/Core/Logging/LogContextManager.cs b/src/Core/Logging/LogContextManager.cs index fdc9fb8600..62f3580b8d 100644 --- a/src/Core/Logging/LogContextManager.cs +++ b/src/Core/Logging/LogContextManager.cs @@ -1,4 +1,25 @@ -using System.Collections.Immutable; +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +using System.Collections.Immutable; using System.ComponentModel.Composition; namespace SonarLint.VisualStudio.Core.Logging; diff --git a/src/Core/Logging/LoggerFactory.cs b/src/Core/Logging/LoggerFactory.cs index 90ff9c135a..7b0e464eb7 100644 --- a/src/Core/Logging/LoggerFactory.cs +++ b/src/Core/Logging/LoggerFactory.cs @@ -1,4 +1,24 @@ -using System.ComponentModel.Composition; +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using System.ComponentModel.Composition; namespace SonarLint.VisualStudio.Core.Logging; @@ -10,4 +30,4 @@ public class LoggerFactory(ILogContextManager logContextManager) : ILoggerFactor public static ILoggerFactory Default { get; } = new LoggerFactory(new LogContextManager()); public ILogger Create(ILogWriter logWriter, ILogVerbosityIndicator verbosityIndicator) => new LoggerBase(logContextManager, logWriter, verbosityIndicator); -} \ No newline at end of file +} From b4a34073cc0f954b695a84a36df612702bba1564 Mon Sep 17 00:00:00 2001 From: Georgii Borovinskikh Date: Fri, 20 Dec 2024 16:20:11 +0100 Subject: [PATCH 07/12] Add LogContextManagerTests --- .../Logging/LogContextManagerTests.cs | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 src/Core.UnitTests/Logging/LogContextManagerTests.cs diff --git a/src/Core.UnitTests/Logging/LogContextManagerTests.cs b/src/Core.UnitTests/Logging/LogContextManagerTests.cs new file mode 100644 index 0000000000..bf33d5b9da --- /dev/null +++ b/src/Core.UnitTests/Logging/LogContextManagerTests.cs @@ -0,0 +1,105 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using SonarLint.VisualStudio.Core.Logging; +using SonarLint.VisualStudio.TestInfrastructure; + +namespace SonarLint.VisualStudio.Core.UnitTests.Logging; + +[TestClass] +public class LogContextManagerTests +{ + [TestMethod] + public void MefCtor_CheckIsExported() => + MefTestHelpers.CheckTypeCanBeImported(); + + [TestMethod] + public void MefCtor_CheckIsSingleton() => + MefTestHelpers.CheckIsNonSharedMefComponent(); + + [TestMethod] + public void EmptyContext() + { + var testSubject = new LogContextManager(); + + testSubject.FormatedContext.Should().BeNull(); + testSubject.FormatedVerboseContext.Should().BeNull(); + } + + [TestMethod] + public void Augmented_Immutable() + { + var testSubject = new LogContextManager(); + var contextualized= testSubject.CreateAugmentedContext(["a"]); + var verboseContextualized = testSubject.CreateAugmentedVerboseContext(["b"]); + var doubleContextualized = testSubject.CreateAugmentedContext(["c"]).CreateAugmentedVerboseContext(["d"]); + + testSubject.FormatedContext.Should().BeNull(); + testSubject.FormatedVerboseContext.Should().BeNull(); + contextualized.FormatedContext.Should().Be("a"); + contextualized.FormatedVerboseContext.Should().BeNull(); + verboseContextualized.FormatedContext.Should().BeNull(); + verboseContextualized.FormatedVerboseContext.Should().Be("b"); + doubleContextualized.FormatedContext.Should().Be("c"); + doubleContextualized.FormatedVerboseContext.Should().Be("d"); + } + + [TestMethod] + public void Augmented_MultipleAtOnce_Combines() => + new LogContextManager() + .CreateAugmentedContext(["a", "b"]) + .FormatedContext.Should().Be("a > b"); + + [TestMethod] + public void Augmented_MultipleInSequence_Combines() => + new LogContextManager() + .CreateAugmentedContext(["a"]) + .CreateAugmentedContext(["b"]) + .FormatedContext.Should().Be("a > b"); + + [TestMethod] + public void Augmented_AtOnceAndInSequence_CombinesInCorrectOrder() => + new LogContextManager() + .CreateAugmentedContext(["a"]) + .CreateAugmentedContext(["b", "c"]) + .CreateAugmentedContext(["d"]) + .FormatedContext.Should().Be("a > b > c > d"); + + [TestMethod] + public void AugmentedVerbose_MultipleAtOnce_Combines() => + new LogContextManager() + .CreateAugmentedVerboseContext(["a", "b"]) + .FormatedVerboseContext.Should().Be("a > b"); + + [TestMethod] + public void AugmentedVerbose_MultipleInSequence_Combines() => + new LogContextManager() + .CreateAugmentedVerboseContext(["a"]) + .CreateAugmentedVerboseContext(["b"]) + .FormatedVerboseContext.Should().Be("a > b"); + + [TestMethod] + public void AugmentedVerbose_AtOnceAndInSequence_CombinesInCorrectOrder() => + new LogContextManager() + .CreateAugmentedVerboseContext(["a"]) + .CreateAugmentedVerboseContext(["b", "c"]) + .CreateAugmentedVerboseContext(["d"]) + .FormatedVerboseContext.Should().Be("a > b > c > d"); +} From e84dc9418f28af02ea79c96af6e1a28ff023371d Mon Sep 17 00:00:00 2001 From: Georgii Borovinskikh Date: Mon, 23 Dec 2024 10:22:57 +0100 Subject: [PATCH 08/12] rearrange code in TestLogger --- .../Framework/TestLogger.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/TestInfrastructure/Framework/TestLogger.cs b/src/TestInfrastructure/Framework/TestLogger.cs index 6b71907fb5..7b76f65381 100644 --- a/src/TestInfrastructure/Framework/TestLogger.cs +++ b/src/TestInfrastructure/Framework/TestLogger.cs @@ -31,6 +31,7 @@ public class TestLogger : ILogger, ILogWriter, ILogVerbosityIndicator public event EventHandler LogMessageAdded; private readonly bool logToConsole; + private readonly bool logThreadId; private readonly ILogger logger; public TestLogger(bool logToConsole = false, bool logThreadId = false) @@ -40,7 +41,7 @@ public TestLogger(bool logToConsole = false, bool logThreadId = false) // link to show the output. this.logToConsole = logToConsole; - IsThreadIdEnabled = logThreadId; + this.logThreadId = logThreadId; logger = LoggerFactory.Default.Create(this, this); } @@ -95,18 +96,6 @@ public void Reset() OutputStrings = new BlockingCollection(); } - void ILogWriter.WriteLine(string message) - { - var messageToLog = message + Environment.NewLine; - OutputStrings.Add(messageToLog); - if (logToConsole) - { - Console.WriteLine(messageToLog); - } - - LogMessageAdded?.Invoke(this, EventArgs.Empty); - } - #region ILogger methods public void WriteLine(string message) => logger.WriteLine(message); @@ -121,7 +110,18 @@ void ILogWriter.WriteLine(string message) #endregion - public bool IsVerboseEnabled => true; - public bool IsThreadIdEnabled { get; } + void ILogWriter.WriteLine(string message) + { + var messageToLog = message + Environment.NewLine; + OutputStrings.Add(messageToLog); + if (logToConsole) + { + Console.WriteLine(messageToLog); + } + + LogMessageAdded?.Invoke(this, EventArgs.Empty); + } + bool ILogVerbosityIndicator.IsVerboseEnabled => true; + bool ILogVerbosityIndicator.IsThreadIdEnabled => logThreadId; } } From b9a2df931b2adefadf584d227d6a24d300d8d405 Mon Sep 17 00:00:00 2001 From: Georgii Borovinskikh Date: Mon, 23 Dec 2024 10:34:46 +0100 Subject: [PATCH 09/12] renames --- ...rTests.cs => LoggerContextManagerTests.cs} | 22 ++++++------ ...extManager.cs => ILoggerContextManager.cs} | 6 ++-- src/Core/Logging/ILoggerFactory.cs | 2 +- ...ndicator.cs => ILoggerSettingsProvider.cs} | 2 +- .../{ILogWriter.cs => ILoggerWriter.cs} | 2 +- src/Core/Logging/LoggerBase.cs | 36 +++++++++---------- ...textManager.cs => LoggerContextManager.cs} | 12 +++---- src/Core/Logging/LoggerFactory.cs | 8 ++--- .../Helpers/SonarLintOutputTests.cs | 2 +- ...SonarLintOutputWindowLoggerWriterTests.cs} | 6 ++-- ...intSettingsLoggerSettingsProviderTests.cs} | 6 ++-- .../Helpers/SonarLintOutputLoggerFactory.cs | 8 ++--- .../Roslyn.Suppressions/Container.cs | 2 +- .../Roslyn.Suppressions/Logger.cs | 23 ++++++------ .../Framework/TestLogger.cs | 8 ++--- 15 files changed, 73 insertions(+), 72 deletions(-) rename src/Core.UnitTests/Logging/{LogContextManagerTests.cs => LoggerContextManagerTests.cs} (87%) rename src/Core/Logging/{ILogContextManager.cs => ILoggerContextManager.cs} (81%) rename src/Core/Logging/{ILogVerbosityIndicator.cs => ILoggerSettingsProvider.cs} (96%) rename src/Core/Logging/{ILogWriter.cs => ILoggerWriter.cs} (96%) rename src/Core/Logging/{LogContextManager.cs => LoggerContextManager.cs} (73%) rename src/Integration.UnitTests/Helpers/{SonarLintOutputWindowLogWriterTests.cs => SonarLintOutputWindowLoggerWriterTests.cs} (93%) rename src/Integration.UnitTests/Helpers/{SonarLintSettingsLogVerbosityIndicatorTests.cs => SonarLintSettingsLoggerSettingsProviderTests.cs} (88%) diff --git a/src/Core.UnitTests/Logging/LogContextManagerTests.cs b/src/Core.UnitTests/Logging/LoggerContextManagerTests.cs similarity index 87% rename from src/Core.UnitTests/Logging/LogContextManagerTests.cs rename to src/Core.UnitTests/Logging/LoggerContextManagerTests.cs index bf33d5b9da..f92065c74d 100644 --- a/src/Core.UnitTests/Logging/LogContextManagerTests.cs +++ b/src/Core.UnitTests/Logging/LoggerContextManagerTests.cs @@ -24,20 +24,20 @@ namespace SonarLint.VisualStudio.Core.UnitTests.Logging; [TestClass] -public class LogContextManagerTests +public class LoggerContextManagerTests { [TestMethod] public void MefCtor_CheckIsExported() => - MefTestHelpers.CheckTypeCanBeImported(); + MefTestHelpers.CheckTypeCanBeImported(); [TestMethod] public void MefCtor_CheckIsSingleton() => - MefTestHelpers.CheckIsNonSharedMefComponent(); + MefTestHelpers.CheckIsNonSharedMefComponent(); [TestMethod] public void EmptyContext() { - var testSubject = new LogContextManager(); + var testSubject = new LoggerContextManager(); testSubject.FormatedContext.Should().BeNull(); testSubject.FormatedVerboseContext.Should().BeNull(); @@ -46,7 +46,7 @@ public void EmptyContext() [TestMethod] public void Augmented_Immutable() { - var testSubject = new LogContextManager(); + var testSubject = new LoggerContextManager(); var contextualized= testSubject.CreateAugmentedContext(["a"]); var verboseContextualized = testSubject.CreateAugmentedVerboseContext(["b"]); var doubleContextualized = testSubject.CreateAugmentedContext(["c"]).CreateAugmentedVerboseContext(["d"]); @@ -63,20 +63,20 @@ public void Augmented_Immutable() [TestMethod] public void Augmented_MultipleAtOnce_Combines() => - new LogContextManager() + new LoggerContextManager() .CreateAugmentedContext(["a", "b"]) .FormatedContext.Should().Be("a > b"); [TestMethod] public void Augmented_MultipleInSequence_Combines() => - new LogContextManager() + new LoggerContextManager() .CreateAugmentedContext(["a"]) .CreateAugmentedContext(["b"]) .FormatedContext.Should().Be("a > b"); [TestMethod] public void Augmented_AtOnceAndInSequence_CombinesInCorrectOrder() => - new LogContextManager() + new LoggerContextManager() .CreateAugmentedContext(["a"]) .CreateAugmentedContext(["b", "c"]) .CreateAugmentedContext(["d"]) @@ -84,20 +84,20 @@ public void Augmented_AtOnceAndInSequence_CombinesInCorrectOrder() => [TestMethod] public void AugmentedVerbose_MultipleAtOnce_Combines() => - new LogContextManager() + new LoggerContextManager() .CreateAugmentedVerboseContext(["a", "b"]) .FormatedVerboseContext.Should().Be("a > b"); [TestMethod] public void AugmentedVerbose_MultipleInSequence_Combines() => - new LogContextManager() + new LoggerContextManager() .CreateAugmentedVerboseContext(["a"]) .CreateAugmentedVerboseContext(["b"]) .FormatedVerboseContext.Should().Be("a > b"); [TestMethod] public void AugmentedVerbose_AtOnceAndInSequence_CombinesInCorrectOrder() => - new LogContextManager() + new LoggerContextManager() .CreateAugmentedVerboseContext(["a"]) .CreateAugmentedVerboseContext(["b", "c"]) .CreateAugmentedVerboseContext(["d"]) diff --git a/src/Core/Logging/ILogContextManager.cs b/src/Core/Logging/ILoggerContextManager.cs similarity index 81% rename from src/Core/Logging/ILogContextManager.cs rename to src/Core/Logging/ILoggerContextManager.cs index f15dc5ebad..8b1f06a90e 100644 --- a/src/Core/Logging/ILogContextManager.cs +++ b/src/Core/Logging/ILoggerContextManager.cs @@ -20,12 +20,12 @@ namespace SonarLint.VisualStudio.Core.Logging; -public interface ILogContextManager +public interface ILoggerContextManager { public string FormatedContext { get; } public string FormatedVerboseContext { get; } - ILogContextManager CreateAugmentedContext(IEnumerable additionalContexts); + ILoggerContextManager CreateAugmentedContext(IEnumerable additionalContexts); - ILogContextManager CreateAugmentedVerboseContext(IEnumerable additionalVerboseContexts); + ILoggerContextManager CreateAugmentedVerboseContext(IEnumerable additionalVerboseContexts); } diff --git a/src/Core/Logging/ILoggerFactory.cs b/src/Core/Logging/ILoggerFactory.cs index d6994dc833..7e2f857a61 100644 --- a/src/Core/Logging/ILoggerFactory.cs +++ b/src/Core/Logging/ILoggerFactory.cs @@ -22,5 +22,5 @@ namespace SonarLint.VisualStudio.Core.Logging; public interface ILoggerFactory { - ILogger Create(ILogWriter logWriter, ILogVerbosityIndicator verbosityIndicator); + ILogger Create(ILoggerWriter writer, ILoggerSettingsProvider settingsProvider); } diff --git a/src/Core/Logging/ILogVerbosityIndicator.cs b/src/Core/Logging/ILoggerSettingsProvider.cs similarity index 96% rename from src/Core/Logging/ILogVerbosityIndicator.cs rename to src/Core/Logging/ILoggerSettingsProvider.cs index 76bb31552a..65ba4d8c55 100644 --- a/src/Core/Logging/ILogVerbosityIndicator.cs +++ b/src/Core/Logging/ILoggerSettingsProvider.cs @@ -20,7 +20,7 @@ namespace SonarLint.VisualStudio.Core.Logging; -public interface ILogVerbosityIndicator +public interface ILoggerSettingsProvider { bool IsVerboseEnabled { get; } bool IsThreadIdEnabled { get; } diff --git a/src/Core/Logging/ILogWriter.cs b/src/Core/Logging/ILoggerWriter.cs similarity index 96% rename from src/Core/Logging/ILogWriter.cs rename to src/Core/Logging/ILoggerWriter.cs index 8afafad810..af4b19f1ec 100644 --- a/src/Core/Logging/ILogWriter.cs +++ b/src/Core/Logging/ILoggerWriter.cs @@ -20,7 +20,7 @@ namespace SonarLint.VisualStudio.Core.Logging; -public interface ILogWriter +public interface ILoggerWriter { void WriteLine(string message); } diff --git a/src/Core/Logging/LoggerBase.cs b/src/Core/Logging/LoggerBase.cs index 445b79c0e8..35e475fb14 100644 --- a/src/Core/Logging/LoggerBase.cs +++ b/src/Core/Logging/LoggerBase.cs @@ -24,31 +24,31 @@ namespace SonarLint.VisualStudio.Core.Logging; internal class LoggerBase( - ILogContextManager logContextManager, - ILogWriter logWriter, - ILogVerbosityIndicator logVerbosityIndicator) : ILogger + ILoggerContextManager contextManager, + ILoggerWriter writer, + ILoggerSettingsProvider settingsProvider) : ILogger { public ILogger ForContext(params string[] context) => new LoggerBase( - logContextManager.CreateAugmentedContext(context.Where(x => !string.IsNullOrEmpty(x))), - logWriter, - logVerbosityIndicator); + contextManager.CreateAugmentedContext(context.Where(x => !string.IsNullOrEmpty(x))), + writer, + settingsProvider); public ILogger ForVerboseContext(params string[] context) => new LoggerBase( - logContextManager.CreateAugmentedVerboseContext(context.Where(x => !string.IsNullOrEmpty(x))), - logWriter, - logVerbosityIndicator); + contextManager.CreateAugmentedVerboseContext(context.Where(x => !string.IsNullOrEmpty(x))), + writer, + settingsProvider); public void WriteLine(string message) => - logWriter.WriteLine(CreateStandardLogPrefix().Append(message).ToString()); + writer.WriteLine(CreateStandardLogPrefix().Append(message).ToString()); public void WriteLine(string messageFormat, params object[] args) => - logWriter.WriteLine(CreateStandardLogPrefix().AppendFormat(CultureInfo.CurrentCulture, messageFormat, args).ToString()); + writer.WriteLine(CreateStandardLogPrefix().AppendFormat(CultureInfo.CurrentCulture, messageFormat, args).ToString()); public void LogVerbose(string messageFormat, params object[] args) { - if (!logVerbosityIndicator.IsVerboseEnabled) + if (!settingsProvider.IsVerboseEnabled) { return; } @@ -57,7 +57,7 @@ public void LogVerbose(string messageFormat, params object[] args) var logLine = args.Length > 0 ? debugLogPrefix.AppendFormat(CultureInfo.CurrentCulture, messageFormat, args) : debugLogPrefix.Append(messageFormat); - logWriter.WriteLine(logLine.ToString()); + writer.WriteLine(logLine.ToString()); } private StringBuilder CreateStandardLogPrefix() => @@ -68,19 +68,19 @@ private StringBuilder CreateDebugLogPrefix() => private StringBuilder AddStandardProperties(StringBuilder builder) { - if (logVerbosityIndicator.IsThreadIdEnabled) + if (settingsProvider.IsThreadIdEnabled) { AppendPropertyFormat(builder, "ThreadId {0, 3}", Thread.CurrentThread.ManagedThreadId); } - if (logContextManager.FormatedContext != null) + if (contextManager.FormatedContext != null) { - AppendProperty(builder, logContextManager.FormatedContext); + AppendProperty(builder, contextManager.FormatedContext); } - if (logVerbosityIndicator.IsVerboseEnabled && logContextManager.FormatedVerboseContext != null) + if (settingsProvider.IsVerboseEnabled && contextManager.FormatedVerboseContext != null) { - AppendProperty(builder, logContextManager.FormatedVerboseContext); + AppendProperty(builder, contextManager.FormatedVerboseContext); } return builder; diff --git a/src/Core/Logging/LogContextManager.cs b/src/Core/Logging/LoggerContextManager.cs similarity index 73% rename from src/Core/Logging/LogContextManager.cs rename to src/Core/Logging/LoggerContextManager.cs index 62f3580b8d..10f0602676 100644 --- a/src/Core/Logging/LogContextManager.cs +++ b/src/Core/Logging/LoggerContextManager.cs @@ -24,9 +24,9 @@ namespace SonarLint.VisualStudio.Core.Logging; -[Export(typeof(ILogContextManager))] +[Export(typeof(ILoggerContextManager))] [PartCreationPolicy(CreationPolicy.NonShared)] -public class LogContextManager : ILogContextManager +public class LoggerContextManager : ILoggerContextManager { private readonly ImmutableList contexts; private readonly ImmutableList verboseContexts; @@ -34,9 +34,9 @@ public class LogContextManager : ILogContextManager private readonly Lazy formatedVerboseContext; [ImportingConstructor] - public LogContextManager() : this(ImmutableList.Empty, ImmutableList.Empty) { } + public LoggerContextManager() : this(ImmutableList.Empty, ImmutableList.Empty) { } - private LogContextManager(ImmutableList contexts, ImmutableList verboseContexts) + private LoggerContextManager(ImmutableList contexts, ImmutableList verboseContexts) { this.contexts = contexts; this.verboseContexts = verboseContexts; @@ -47,9 +47,9 @@ private LogContextManager(ImmutableList contexts, ImmutableList public string FormatedContext => formatedContext.Value; public string FormatedVerboseContext => formatedVerboseContext.Value; - public ILogContextManager CreateAugmentedContext(IEnumerable additionalContexts) => new LogContextManager(contexts.AddRange(additionalContexts), verboseContexts); + public ILoggerContextManager CreateAugmentedContext(IEnumerable additionalContexts) => new LoggerContextManager(contexts.AddRange(additionalContexts), verboseContexts); - public ILogContextManager CreateAugmentedVerboseContext(IEnumerable additionalVerboseContexts) => new LogContextManager(contexts, verboseContexts.AddRange(additionalVerboseContexts)); + public ILoggerContextManager CreateAugmentedVerboseContext(IEnumerable additionalVerboseContexts) => new LoggerContextManager(contexts, verboseContexts.AddRange(additionalVerboseContexts)); private static string MergeContextsIntoSingleProperty(ImmutableList contexts) => contexts.Count > 0 ? string.Join(" > ", contexts) : null; } diff --git a/src/Core/Logging/LoggerFactory.cs b/src/Core/Logging/LoggerFactory.cs index 7b0e464eb7..d280442b4d 100644 --- a/src/Core/Logging/LoggerFactory.cs +++ b/src/Core/Logging/LoggerFactory.cs @@ -25,9 +25,9 @@ namespace SonarLint.VisualStudio.Core.Logging; [Export(typeof(ILoggerFactory))] [PartCreationPolicy(CreationPolicy.NonShared)] [method: ImportingConstructor] -public class LoggerFactory(ILogContextManager logContextManager) : ILoggerFactory +public class LoggerFactory(ILoggerContextManager loggerContextManager) : ILoggerFactory { - public static ILoggerFactory Default { get; } = new LoggerFactory(new LogContextManager()); - public ILogger Create(ILogWriter logWriter, ILogVerbosityIndicator verbosityIndicator) => - new LoggerBase(logContextManager, logWriter, verbosityIndicator); + public static ILoggerFactory Default { get; } = new LoggerFactory(new LoggerContextManager()); + public ILogger Create(ILoggerWriter writer, ILoggerSettingsProvider settingsProvider) => + new LoggerBase(loggerContextManager, writer, settingsProvider); } diff --git a/src/Integration.UnitTests/Helpers/SonarLintOutputTests.cs b/src/Integration.UnitTests/Helpers/SonarLintOutputTests.cs index d2e1168009..e56a93f27b 100644 --- a/src/Integration.UnitTests/Helpers/SonarLintOutputTests.cs +++ b/src/Integration.UnitTests/Helpers/SonarLintOutputTests.cs @@ -44,7 +44,7 @@ public void Ctor_CreatesLoggerWithExpectedParameters() var testSubject = new SonarLintOutputLoggerFactory(loggerFactory, Substitute.For(), Substitute.For()); testSubject.Instance.Should().NotBeNull(); - loggerFactory.Create(Arg.Any(), Arg.Any()); + loggerFactory.Create(Arg.Any(), Arg.Any()); } } diff --git a/src/Integration.UnitTests/Helpers/SonarLintOutputWindowLogWriterTests.cs b/src/Integration.UnitTests/Helpers/SonarLintOutputWindowLoggerWriterTests.cs similarity index 93% rename from src/Integration.UnitTests/Helpers/SonarLintOutputWindowLogWriterTests.cs rename to src/Integration.UnitTests/Helpers/SonarLintOutputWindowLoggerWriterTests.cs index 88c33f72dc..fc72384941 100644 --- a/src/Integration.UnitTests/Helpers/SonarLintOutputWindowLogWriterTests.cs +++ b/src/Integration.UnitTests/Helpers/SonarLintOutputWindowLoggerWriterTests.cs @@ -25,18 +25,18 @@ namespace SonarLint.VisualStudio.Integration.UnitTests.Helpers; [TestClass] -public class SonarLintOutputWindowLogWriterTests +public class SonarLintOutputWindowLoggerWriterTests { private ConfigurableVsOutputWindow windowMock; private IServiceProvider serviceProviderMock; - private SonarLintOutputWindowLogWriter testSubject; + private SonarLintOutputWindowLoggerWriter testSubject; [TestInitialize] public void TestInitialize() { windowMock = new ConfigurableVsOutputWindow(); serviceProviderMock = CreateConfiguredServiceProvider(windowMock); - testSubject = new SonarLintOutputWindowLogWriter(serviceProviderMock); + testSubject = new SonarLintOutputWindowLoggerWriter(serviceProviderMock); } [TestMethod] diff --git a/src/Integration.UnitTests/Helpers/SonarLintSettingsLogVerbosityIndicatorTests.cs b/src/Integration.UnitTests/Helpers/SonarLintSettingsLoggerSettingsProviderTests.cs similarity index 88% rename from src/Integration.UnitTests/Helpers/SonarLintSettingsLogVerbosityIndicatorTests.cs rename to src/Integration.UnitTests/Helpers/SonarLintSettingsLoggerSettingsProviderTests.cs index e8e2626ff2..325e3622e2 100644 --- a/src/Integration.UnitTests/Helpers/SonarLintSettingsLogVerbosityIndicatorTests.cs +++ b/src/Integration.UnitTests/Helpers/SonarLintSettingsLoggerSettingsProviderTests.cs @@ -23,16 +23,16 @@ namespace SonarLint.VisualStudio.Integration.UnitTests.Helpers; [TestClass] -public class SonarLintSettingsLogVerbosityIndicatorTests +public class SonarLintSettingsLoggerSettingsProviderTests { private ISonarLintSettings sonarLintSettings; - private SonarLintSettingsLogVerbosityIndicator testSubject; + private SonarLintSettingsLoggerSettingsProvider testSubject; [TestInitialize] public void TestInitialize() { sonarLintSettings = Substitute.For(); - testSubject = new SonarLintSettingsLogVerbosityIndicator(sonarLintSettings); + testSubject = new SonarLintSettingsLoggerSettingsProvider(sonarLintSettings); } [DataRow(DaemonLogLevel.Verbose, true)] diff --git a/src/Integration/Helpers/SonarLintOutputLoggerFactory.cs b/src/Integration/Helpers/SonarLintOutputLoggerFactory.cs index d373382f52..fcd53d5399 100644 --- a/src/Integration/Helpers/SonarLintOutputLoggerFactory.cs +++ b/src/Integration/Helpers/SonarLintOutputLoggerFactory.cs @@ -25,12 +25,12 @@ namespace SonarLint.VisualStudio.Integration.Helpers; -internal class SonarLintOutputWindowLogWriter(IServiceProvider serviceProvider) : ILogWriter +internal class SonarLintOutputWindowLoggerWriter(IServiceProvider serviceProvider) : ILoggerWriter { public void WriteLine(string message) => VsShellUtils.WriteToSonarLintOutputPane(serviceProvider, message); } -internal class SonarLintSettingsLogVerbosityIndicator(ISonarLintSettings sonarLintSettings) : ILogVerbosityIndicator +internal class SonarLintSettingsLoggerSettingsProvider(ISonarLintSettings sonarLintSettings) : ILoggerSettingsProvider { public bool IsVerboseEnabled => sonarLintSettings.DaemonLogLevel == DaemonLogLevel.Verbose; public bool IsThreadIdEnabled => IsVerboseEnabled; @@ -46,6 +46,6 @@ internal class SonarLintOutputLoggerFactory( [Export(typeof(ILogger))] public ILogger Instance { get; } = logFactory.Create( - new SonarLintOutputWindowLogWriter(serviceProvider), - new SonarLintSettingsLogVerbosityIndicator(sonarLintSettings)); + new SonarLintOutputWindowLoggerWriter(serviceProvider), + new SonarLintSettingsLoggerSettingsProvider(sonarLintSettings)); } diff --git a/src/Roslyn.Suppressions/Roslyn.Suppressions/Container.cs b/src/Roslyn.Suppressions/Roslyn.Suppressions/Container.cs index 6327f4b5d5..ac8ad1f701 100644 --- a/src/Roslyn.Suppressions/Roslyn.Suppressions/Container.cs +++ b/src/Roslyn.Suppressions/Roslyn.Suppressions/Container.cs @@ -67,7 +67,7 @@ public static IContainer Instance public Container() { Directory.CreateDirectory(RoslynSettingsFileInfo.Directory); - Logger = LoggerFactory.Default.Create(new SystemDebugLogWriter(), new AlwaysOnLogVerbosityIndicator()); + Logger = LoggerFactory.Default.Create(new SystemDebugLoggerWriter(), new EnableAllLoggerSettingsProvider()); var settingsCache = new SettingsCache(Logger); fileWatcher = new SuppressedIssuesFileWatcher(settingsCache, Logger); diff --git a/src/Roslyn.Suppressions/Roslyn.Suppressions/Logger.cs b/src/Roslyn.Suppressions/Roslyn.Suppressions/Logger.cs index 5819bcb52f..6acdd910fd 100644 --- a/src/Roslyn.Suppressions/Roslyn.Suppressions/Logger.cs +++ b/src/Roslyn.Suppressions/Roslyn.Suppressions/Logger.cs @@ -18,20 +18,21 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +using System.Diagnostics.CodeAnalysis; using SonarLint.VisualStudio.Core; using SonarLint.VisualStudio.Core.Logging; -namespace SonarLint.VisualStudio.Roslyn.Suppressions -{ - internal class SystemDebugLogWriter : ILogWriter - { - public void WriteLine(string message) => Debug.WriteLine(message); - } +namespace SonarLint.VisualStudio.Roslyn.Suppressions; - internal class AlwaysOnLogVerbosityIndicator : ILogVerbosityIndicator - { - public bool IsVerboseEnabled => true; - public bool IsThreadIdEnabled => true; - } +[ExcludeFromCodeCoverage] +internal class SystemDebugLoggerWriter : ILoggerWriter +{ + public void WriteLine(string message) => Debug.WriteLine(message); } +[ExcludeFromCodeCoverage] +internal class EnableAllLoggerSettingsProvider : ILoggerSettingsProvider +{ + public bool IsVerboseEnabled => true; + public bool IsThreadIdEnabled => true; +} diff --git a/src/TestInfrastructure/Framework/TestLogger.cs b/src/TestInfrastructure/Framework/TestLogger.cs index 7b76f65381..832812fc1f 100644 --- a/src/TestInfrastructure/Framework/TestLogger.cs +++ b/src/TestInfrastructure/Framework/TestLogger.cs @@ -24,7 +24,7 @@ namespace SonarLint.VisualStudio.TestInfrastructure { - public class TestLogger : ILogger, ILogWriter, ILogVerbosityIndicator + public class TestLogger : ILogger, ILoggerWriter, ILoggerSettingsProvider { public BlockingCollection OutputStrings { get; private set; } = new(); @@ -110,7 +110,7 @@ public void Reset() #endregion - void ILogWriter.WriteLine(string message) + void ILoggerWriter.WriteLine(string message) { var messageToLog = message + Environment.NewLine; OutputStrings.Add(messageToLog); @@ -121,7 +121,7 @@ void ILogWriter.WriteLine(string message) LogMessageAdded?.Invoke(this, EventArgs.Empty); } - bool ILogVerbosityIndicator.IsVerboseEnabled => true; - bool ILogVerbosityIndicator.IsThreadIdEnabled => logThreadId; + bool ILoggerSettingsProvider.IsVerboseEnabled => true; + bool ILoggerSettingsProvider.IsThreadIdEnabled => logThreadId; } } From 135ea4a590f3ad84a641c7c175d0e5b513edf053 Mon Sep 17 00:00:00 2001 From: Georgii Borovinskikh Date: Mon, 23 Dec 2024 10:43:19 +0100 Subject: [PATCH 10/12] Add LoggerFactoryTests --- .../Logging/LoggerFactoryTests.cs | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 src/Core.UnitTests/Logging/LoggerFactoryTests.cs diff --git a/src/Core.UnitTests/Logging/LoggerFactoryTests.cs b/src/Core.UnitTests/Logging/LoggerFactoryTests.cs new file mode 100644 index 0000000000..76a43c5e6f --- /dev/null +++ b/src/Core.UnitTests/Logging/LoggerFactoryTests.cs @@ -0,0 +1,63 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using SonarLint.VisualStudio.Core.Logging; +using SonarLint.VisualStudio.TestInfrastructure; + +namespace SonarLint.VisualStudio.Core.UnitTests.Logging; + +[TestClass] +public class LoggerFactoryTests +{ + private ILoggerContextManager logContextManager; + private ILoggerWriter logWriter; + private ILoggerSettingsProvider logVerbosityIndicator; + private LoggerFactory testSubject; + + [TestInitialize] + public void TestInitialize() + { + logContextManager = Substitute.For(); + logWriter = Substitute.For(); + logVerbosityIndicator = Substitute.For(); + testSubject = new LoggerFactory(logContextManager); + } + + [TestMethod] + public void MefCtor_CheckIsExported() => + MefTestHelpers.CheckTypeCanBeImported( + MefTestHelpers.CreateExport()); + + [TestMethod] + public void MefCtor_CheckIsSingleton() => + MefTestHelpers.CheckIsNonSharedMefComponent(); + + [TestMethod] + public void Create_ReturnsLoggerConfiguredWithCorrectDependencies() + { + var logger = testSubject.Create(logWriter, logVerbosityIndicator); + + logger.Should().NotBeNull(); + logger.WriteLine("msg"); + _ = logContextManager.Received().FormatedContext; + _ = logVerbosityIndicator.Received().IsVerboseEnabled; + logWriter.Received().WriteLine(Arg.Is(x => x.Contains("msg"))); + } +} From 48aeac6eb998e47721ffcd70b37b0652f1065770 Mon Sep 17 00:00:00 2001 From: Georgii Borovinskikh Date: Mon, 23 Dec 2024 11:41:44 +0100 Subject: [PATCH 11/12] Add LoggerBaseTests --- src/Core.UnitTests/Logging/LoggerBaseTests.cs | 319 ++++++++++++++++++ src/Core/Logging/LoggerBase.cs | 4 +- 2 files changed, 321 insertions(+), 2 deletions(-) create mode 100644 src/Core.UnitTests/Logging/LoggerBaseTests.cs diff --git a/src/Core.UnitTests/Logging/LoggerBaseTests.cs b/src/Core.UnitTests/Logging/LoggerBaseTests.cs new file mode 100644 index 0000000000..1ebea4c2b0 --- /dev/null +++ b/src/Core.UnitTests/Logging/LoggerBaseTests.cs @@ -0,0 +1,319 @@ +/* + * SonarLint for Visual Studio + * Copyright (C) 2016-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +using SonarLint.VisualStudio.Core.Logging; + +namespace SonarLint.VisualStudio.Core.UnitTests.Logging; + +[TestClass] +public class LoggerBaseTests +{ + private ILoggerContextManager contextManager; + private ILoggerWriter writer; + private ILoggerSettingsProvider settingsProvider; + private LoggerBase testSubject; + + [TestInitialize] + public void TestInitialize() + { + contextManager = Substitute.For(); + writer = Substitute.For(); + settingsProvider = Substitute.For(); + testSubject = new LoggerBase(contextManager, writer, settingsProvider); + } + + [TestMethod] + public void ForContext_CreatesNewLoggerWithUpdatedContextManager() + { + var newContextManager = Substitute.For(); + contextManager.CreateAugmentedContext(Arg.Any>()).Returns(newContextManager); + + var newLogger = testSubject.ForContext("ctx"); + + contextManager.Received(1).CreateAugmentedContext(Arg.Is>(x => x.SequenceEqual(new[] { "ctx" }))); + newLogger.Should().NotBeSameAs(testSubject); + newLogger.WriteLine("msg"); + _ = contextManager.DidNotReceive().FormatedContext; + _ = newContextManager.Received().FormatedContext; + writer.Received().WriteLine(Arg.Any()); + _ = settingsProvider.Received().IsVerboseEnabled; + } + + [TestMethod] + public void ForContext_FiltersOutNullAndEmptyContext() + { + var newContextManager = Substitute.For(); + contextManager.CreateAugmentedContext(Arg.Any>()).Returns(newContextManager); + + testSubject.ForContext("ctx", string.Empty, null); + + contextManager.Received(1).CreateAugmentedContext(Arg.Is>(x => x.SequenceEqual(new[] { "ctx" }))); + } + + [TestMethod] + public void ForVerboseContext_CreatesNewLoggerWithUpdatedContextManager() + { + var newContextManager = Substitute.For(); + contextManager.CreateAugmentedVerboseContext(Arg.Any>()).Returns(newContextManager); + + var newLogger = testSubject.ForVerboseContext("ctx"); + + contextManager.Received(1).CreateAugmentedVerboseContext(Arg.Is>(x => x.SequenceEqual(new[] { "ctx" }))); + newLogger.Should().NotBeSameAs(testSubject); + newLogger.WriteLine("msg"); + _ = contextManager.DidNotReceive().FormatedContext; + _ = newContextManager.Received().FormatedContext; + writer.Received().WriteLine(Arg.Any()); + _ = settingsProvider.Received().IsVerboseEnabled; + } + + [TestMethod] + public void ForVerboseContext_FiltersOutNullAndEmptyContext() + { + var newContextManager = Substitute.For(); + contextManager.CreateAugmentedVerboseContext(Arg.Any>()).Returns(newContextManager); + + testSubject.ForVerboseContext("ctx", string.Empty, null); + + contextManager.Received(1).CreateAugmentedVerboseContext(Arg.Is>(x => x.SequenceEqual(new[] { "ctx" }))); + } + + [TestMethod] + public void LogVerbose_VerboseDisabled_DoesNothing() + { + settingsProvider.IsVerboseEnabled.Returns(false); + + testSubject.LogVerbose("msg {0}", "sent"); + + writer.DidNotReceiveWithAnyArgs().WriteLine(default); + } + + [TestMethod] + public void LogVerbose_VerboseEnabled_AddsDebugProperty() + { + settingsProvider.IsVerboseEnabled.Returns(true); + + testSubject.LogVerbose("msg {0}", "sent"); + + writer.Received().WriteLine("[DEBUG] msg sent"); + } + + [TestMethod] + public void LogVerbose_ThreadIdLoggingEnabled_AddsThreadIdProperty() + { + settingsProvider.IsThreadIdEnabled.Returns(true); + settingsProvider.IsVerboseEnabled.Returns(true); + + testSubject.LogVerbose("msg {0}", "sent"); + + writer.Received().WriteLine($"[ThreadId {Thread.CurrentThread.ManagedThreadId, 3}] [DEBUG] msg sent"); + } + + [TestMethod] + public void LogVerbose_Context_AddsContextProperty() + { + settingsProvider.IsVerboseEnabled.Returns(true); + contextManager.FormatedContext.Returns("context"); + + testSubject.LogVerbose("msg {0}", "sent"); + + writer.Received().WriteLine("[context] [DEBUG] msg sent"); + } + + [TestMethod] + public void LogVerbose_VerboseContext_AddsVerboseContextProperty() + { + settingsProvider.IsVerboseEnabled.Returns(true); + contextManager.FormatedVerboseContext.Returns("verbose context"); + + testSubject.LogVerbose("msg {0}", "sent"); + + writer.Received().WriteLine("[verbose context] [DEBUG] msg sent"); + } + + [TestMethod] + public void LogVerbose_AllContextsEnabled_AddsInCorrectOrder() + { + settingsProvider.IsThreadIdEnabled.Returns(true); + settingsProvider.IsVerboseEnabled.Returns(true); + contextManager.FormatedContext.Returns("context"); + contextManager.FormatedVerboseContext.Returns("verbose context"); + + testSubject.LogVerbose("msg {0}", "sent"); + + writer.Received().WriteLine($"[ThreadId {Thread.CurrentThread.ManagedThreadId, 3}] [context] [verbose context] [DEBUG] msg sent"); + } + + [TestMethod] + public void WriteLine_VerboseDisabled_Writes() + { + settingsProvider.IsVerboseEnabled.Returns(false); + + testSubject.WriteLine("msg sent"); + + writer.Received().WriteLine("msg sent"); + } + + [TestMethod] + public void WriteLineFormatted_VerboseDisabled_Writes() + { + settingsProvider.IsVerboseEnabled.Returns(false); + + testSubject.WriteLine("msg {0}", "sent"); + + writer.Received().WriteLine("msg sent"); + } + + [TestMethod] + public void WriteLine_VerboseEnabled_DoesNotAddDebugProperty() + { + settingsProvider.IsVerboseEnabled.Returns(true); + + testSubject.WriteLine("msg sent"); + + writer.Received().WriteLine("msg sent"); + } + + [TestMethod] + public void WriteLineFormatted_VerboseEnabled_DoesNotAddDebugProperty() + { + settingsProvider.IsVerboseEnabled.Returns(true); + + testSubject.WriteLine("msg {0}", "sent"); + + writer.Received().WriteLine("msg sent"); + } + + [TestMethod] + public void WriteLine_ThreadIdLoggingEnabled_AddsThreadIdProperty() + { + settingsProvider.IsThreadIdEnabled.Returns(true); + + testSubject.WriteLine("msg sent"); + + writer.Received().WriteLine($"[ThreadId {Thread.CurrentThread.ManagedThreadId, 3}] msg sent"); + } + + [TestMethod] + public void WriteLineFormatted_ThreadIdLoggingEnabled_AddsThreadIdProperty() + { + settingsProvider.IsThreadIdEnabled.Returns(true); + + testSubject.WriteLine("msg {0}", "sent"); + + writer.Received().WriteLine($"[ThreadId {Thread.CurrentThread.ManagedThreadId, 3}] msg sent"); + } + + [DataRow(true)] + [DataRow(false)] + [DataTestMethod] + public void WriteLine_Context_AddsContextProperty(bool isVerboseEnabled) + { + settingsProvider.IsVerboseEnabled.Returns(isVerboseEnabled); + contextManager.FormatedContext.Returns("context"); + + testSubject.WriteLine("msg sent"); + + writer.Received().WriteLine("[context] msg sent"); + } + + [DataRow(true)] + [DataRow(false)] + [DataTestMethod] + public void WriteLineFormatted_Context_AddsContextProperty(bool isVerboseEnabled) + { + settingsProvider.IsVerboseEnabled.Returns(isVerboseEnabled); + contextManager.FormatedContext.Returns("context"); + + testSubject.WriteLine("msg {0}", "sent"); + + writer.Received().WriteLine("[context] msg sent"); + } + + [TestMethod] + public void WriteLine_VerboseContext_VerboseLoggingDisabled_DoesNotAddVerboseContextProperty() + { + settingsProvider.IsVerboseEnabled.Returns(false); + contextManager.FormatedVerboseContext.Returns("verbose context"); + + testSubject.WriteLine("msg sent"); + + writer.Received().WriteLine("msg sent"); + } + + [TestMethod] + public void WriteLineFormatted_VerboseContext_VerboseLoggingDisabled_DoesNotAddVerboseContextProperty() + { + settingsProvider.IsVerboseEnabled.Returns(false); + contextManager.FormatedVerboseContext.Returns("verbose context"); + + testSubject.WriteLine("msg {0}", "sent"); + + writer.Received().WriteLine("msg sent"); + } + + [TestMethod] + public void WriteLine_VerboseContext_VerboseLoggingEnabled_AddsVerboseContextProperty() + { + settingsProvider.IsVerboseEnabled.Returns(true); + contextManager.FormatedVerboseContext.Returns("verbose context"); + + testSubject.WriteLine("msg sent"); + + writer.Received().WriteLine("[verbose context] msg sent"); + } + + [TestMethod] + public void WriteLineFormatted_VerboseContext_VerboseLoggingEnabled_AddsVerboseContextProperty() + { + settingsProvider.IsVerboseEnabled.Returns(true); + contextManager.FormatedVerboseContext.Returns("verbose context"); + + testSubject.WriteLine("msg {0}", "sent"); + + writer.Received().WriteLine("[verbose context] msg sent"); + } + + [TestMethod] + public void WriteLine_AllContextsEnabled_AddsInCorrectOrder() + { + settingsProvider.IsThreadIdEnabled.Returns(true); + settingsProvider.IsVerboseEnabled.Returns(true); + contextManager.FormatedContext.Returns("context"); + contextManager.FormatedVerboseContext.Returns("verbose context"); + + testSubject.WriteLine("msg sent"); + + writer.Received().WriteLine($"[ThreadId {Thread.CurrentThread.ManagedThreadId, 3}] [context] [verbose context] msg sent"); + } + + [TestMethod] + public void WriteLineFormatted_AllContextsEnabled_AddsInCorrectOrder() + { + settingsProvider.IsThreadIdEnabled.Returns(true); + settingsProvider.IsVerboseEnabled.Returns(true); + contextManager.FormatedContext.Returns("context"); + contextManager.FormatedVerboseContext.Returns("verbose context"); + + testSubject.WriteLine("msg {0}", "sent"); + + writer.Received().WriteLine($"[ThreadId {Thread.CurrentThread.ManagedThreadId, 3}] [context] [verbose context] msg sent"); + } +} diff --git a/src/Core/Logging/LoggerBase.cs b/src/Core/Logging/LoggerBase.cs index 35e475fb14..035d26b55f 100644 --- a/src/Core/Logging/LoggerBase.cs +++ b/src/Core/Logging/LoggerBase.cs @@ -73,12 +73,12 @@ private StringBuilder AddStandardProperties(StringBuilder builder) AppendPropertyFormat(builder, "ThreadId {0, 3}", Thread.CurrentThread.ManagedThreadId); } - if (contextManager.FormatedContext != null) + if (!string.IsNullOrEmpty(contextManager.FormatedContext)) { AppendProperty(builder, contextManager.FormatedContext); } - if (settingsProvider.IsVerboseEnabled && contextManager.FormatedVerboseContext != null) + if (settingsProvider.IsVerboseEnabled && !string.IsNullOrEmpty(contextManager.FormatedVerboseContext)) { AppendProperty(builder, contextManager.FormatedVerboseContext); } From d01d7dd65ec6075fe22360de7b582ac349c9c3dd Mon Sep 17 00:00:00 2001 From: Georgii Borovinskikh Date: Mon, 23 Dec 2024 12:50:30 +0100 Subject: [PATCH 12/12] change format --- src/Core.UnitTests/Logging/LoggerBaseTests.cs | 16 ++++++++-------- src/Core/Logging/LoggerBase.cs | 4 ++-- src/Core/Logging/LoggerContextManager.cs | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Core.UnitTests/Logging/LoggerBaseTests.cs b/src/Core.UnitTests/Logging/LoggerBaseTests.cs index 1ebea4c2b0..31f8d59fc8 100644 --- a/src/Core.UnitTests/Logging/LoggerBaseTests.cs +++ b/src/Core.UnitTests/Logging/LoggerBaseTests.cs @@ -123,7 +123,7 @@ public void LogVerbose_ThreadIdLoggingEnabled_AddsThreadIdProperty() testSubject.LogVerbose("msg {0}", "sent"); - writer.Received().WriteLine($"[ThreadId {Thread.CurrentThread.ManagedThreadId, 3}] [DEBUG] msg sent"); + writer.Received().WriteLine($"[DEBUG] [ThreadId {Thread.CurrentThread.ManagedThreadId}] msg sent"); } [TestMethod] @@ -134,7 +134,7 @@ public void LogVerbose_Context_AddsContextProperty() testSubject.LogVerbose("msg {0}", "sent"); - writer.Received().WriteLine("[context] [DEBUG] msg sent"); + writer.Received().WriteLine("[DEBUG] [context] msg sent"); } [TestMethod] @@ -145,7 +145,7 @@ public void LogVerbose_VerboseContext_AddsVerboseContextProperty() testSubject.LogVerbose("msg {0}", "sent"); - writer.Received().WriteLine("[verbose context] [DEBUG] msg sent"); + writer.Received().WriteLine("[DEBUG] [verbose context] msg sent"); } [TestMethod] @@ -158,7 +158,7 @@ public void LogVerbose_AllContextsEnabled_AddsInCorrectOrder() testSubject.LogVerbose("msg {0}", "sent"); - writer.Received().WriteLine($"[ThreadId {Thread.CurrentThread.ManagedThreadId, 3}] [context] [verbose context] [DEBUG] msg sent"); + writer.Received().WriteLine($"[DEBUG] [ThreadId {Thread.CurrentThread.ManagedThreadId}] [context] [verbose context] msg sent"); } [TestMethod] @@ -208,7 +208,7 @@ public void WriteLine_ThreadIdLoggingEnabled_AddsThreadIdProperty() testSubject.WriteLine("msg sent"); - writer.Received().WriteLine($"[ThreadId {Thread.CurrentThread.ManagedThreadId, 3}] msg sent"); + writer.Received().WriteLine($"[ThreadId {Thread.CurrentThread.ManagedThreadId}] msg sent"); } [TestMethod] @@ -218,7 +218,7 @@ public void WriteLineFormatted_ThreadIdLoggingEnabled_AddsThreadIdProperty() testSubject.WriteLine("msg {0}", "sent"); - writer.Received().WriteLine($"[ThreadId {Thread.CurrentThread.ManagedThreadId, 3}] msg sent"); + writer.Received().WriteLine($"[ThreadId {Thread.CurrentThread.ManagedThreadId}] msg sent"); } [DataRow(true)] @@ -301,7 +301,7 @@ public void WriteLine_AllContextsEnabled_AddsInCorrectOrder() testSubject.WriteLine("msg sent"); - writer.Received().WriteLine($"[ThreadId {Thread.CurrentThread.ManagedThreadId, 3}] [context] [verbose context] msg sent"); + writer.Received().WriteLine($"[ThreadId {Thread.CurrentThread.ManagedThreadId}] [context] [verbose context] msg sent"); } [TestMethod] @@ -314,6 +314,6 @@ public void WriteLineFormatted_AllContextsEnabled_AddsInCorrectOrder() testSubject.WriteLine("msg {0}", "sent"); - writer.Received().WriteLine($"[ThreadId {Thread.CurrentThread.ManagedThreadId, 3}] [context] [verbose context] msg sent"); + writer.Received().WriteLine($"[ThreadId {Thread.CurrentThread.ManagedThreadId}] [context] [verbose context] msg sent"); } } diff --git a/src/Core/Logging/LoggerBase.cs b/src/Core/Logging/LoggerBase.cs index 035d26b55f..c287b41d6b 100644 --- a/src/Core/Logging/LoggerBase.cs +++ b/src/Core/Logging/LoggerBase.cs @@ -64,13 +64,13 @@ private StringBuilder CreateStandardLogPrefix() => AddStandardProperties(new StringBuilder()); private StringBuilder CreateDebugLogPrefix() => - AppendProperty(AddStandardProperties(new StringBuilder()), "DEBUG"); + AddStandardProperties(AppendProperty(new StringBuilder(), "DEBUG")); private StringBuilder AddStandardProperties(StringBuilder builder) { if (settingsProvider.IsThreadIdEnabled) { - AppendPropertyFormat(builder, "ThreadId {0, 3}", Thread.CurrentThread.ManagedThreadId); + AppendPropertyFormat(builder, "ThreadId {0}", Thread.CurrentThread.ManagedThreadId); } if (!string.IsNullOrEmpty(contextManager.FormatedContext)) diff --git a/src/Core/Logging/LoggerContextManager.cs b/src/Core/Logging/LoggerContextManager.cs index 10f0602676..25ac91fa1f 100644 --- a/src/Core/Logging/LoggerContextManager.cs +++ b/src/Core/Logging/LoggerContextManager.cs @@ -26,7 +26,7 @@ namespace SonarLint.VisualStudio.Core.Logging; [Export(typeof(ILoggerContextManager))] [PartCreationPolicy(CreationPolicy.NonShared)] -public class LoggerContextManager : ILoggerContextManager +internal class LoggerContextManager : ILoggerContextManager { private readonly ImmutableList contexts; private readonly ImmutableList verboseContexts;