From f895a1273559d8e863acfc11fa6a4217fb08ed86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20St=C3=BChmer?= Date: Fri, 17 May 2024 19:49:41 +0200 Subject: [PATCH] feat: Initial functionality (#4) * feat: Initial functionality * fix(deps): Added missing PackageVersion * fix(deps): More packages * ci: Enabled SonarQube * chore(tests): Added unit tests --- .github/workflows/cicd.yml | 2 +- Directory.Build.props | 5 ++ Directory.Packages.props | 10 ++++ Logging.Abstractions.sln | 22 ++++++++ README.md | 11 +++- .../IsExternalInit.cs | 16 ++++++ .../LoggedMessage.cs | 23 +++++++++ .../NetEvolve.Logging.Abstractions.csproj | 26 ++++++++++ .../NullExternalScopeProvider.cs | 26 ++++++++++ .../NullScope.cs | 21 ++++++++ .../LoggedMessageTests.cs | 29 +++++++++++ ...lve.Logging.Abstractions.Tests.Unit.csproj | 24 +++++++++ .../NullExternalScopeProviderTests.cs | 51 +++++++++++++++++++ 13 files changed, 263 insertions(+), 3 deletions(-) create mode 100644 src/NetEvolve.Logging.Abstractions/IsExternalInit.cs create mode 100644 src/NetEvolve.Logging.Abstractions/LoggedMessage.cs create mode 100644 src/NetEvolve.Logging.Abstractions/NetEvolve.Logging.Abstractions.csproj create mode 100644 src/NetEvolve.Logging.Abstractions/NullExternalScopeProvider.cs create mode 100644 src/NetEvolve.Logging.Abstractions/NullScope.cs create mode 100644 tests/NetEvolve.Logging.Abstractions.Tests.Unit/LoggedMessageTests.cs create mode 100644 tests/NetEvolve.Logging.Abstractions.Tests.Unit/NetEvolve.Logging.Abstractions.Tests.Unit.csproj create mode 100644 tests/NetEvolve.Logging.Abstractions.Tests.Unit/NullExternalScopeProviderTests.cs diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index f318e7f..6962236 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -24,7 +24,7 @@ jobs: name: Build & Tests uses: dailydevops/pipelines/.github/workflows/cicd-dotnet.yml@0.8.8 with: - disablePublish: true + enableSonarQube: true dotnet-logging: ${{ inputs.dotnet-logging }} dotnet-version: | 8.x diff --git a/Directory.Build.props b/Directory.Build.props index 2958b68..9cdab9d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -9,4 +9,9 @@ + + net8.0;netstandard2.0 + net8.0 + + diff --git a/Directory.Packages.props b/Directory.Packages.props index 78f453b..ea174b9 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -15,4 +15,14 @@ + + + + + + + + + + diff --git a/Logging.Abstractions.sln b/Logging.Abstractions.sln index a5418b9..1f4944a 100644 --- a/Logging.Abstractions.sln +++ b/Logging.Abstractions.sln @@ -22,6 +22,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution update-solution.ps1 = update-solution.ps1 EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C155CC82-19F5-4362-9DF0-3C376BF5587B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetEvolve.Logging.Abstractions", "src\NetEvolve.Logging.Abstractions\NetEvolve.Logging.Abstractions.csproj", "{69640616-9CA3-4200-BEB0-CCA07C343E2B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{AE17327A-3F21-42F0-B3F0-2B7D147BBA42}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetEvolve.Logging.Abstractions.Tests.Unit", "tests\NetEvolve.Logging.Abstractions.Tests.Unit\NetEvolve.Logging.Abstractions.Tests.Unit.csproj", "{766B8D41-B637-4572-A259-66FB4550F89C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -30,4 +38,18 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {69640616-9CA3-4200-BEB0-CCA07C343E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {69640616-9CA3-4200-BEB0-CCA07C343E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {69640616-9CA3-4200-BEB0-CCA07C343E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {69640616-9CA3-4200-BEB0-CCA07C343E2B}.Release|Any CPU.Build.0 = Release|Any CPU + {766B8D41-B637-4572-A259-66FB4550F89C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {766B8D41-B637-4572-A259-66FB4550F89C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {766B8D41-B637-4572-A259-66FB4550F89C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {766B8D41-B637-4572-A259-66FB4550F89C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {69640616-9CA3-4200-BEB0-CCA07C343E2B} = {C155CC82-19F5-4362-9DF0-3C376BF5587B} + {766B8D41-B637-4572-A259-66FB4550F89C} = {AE17327A-3F21-42F0-B3F0-2B7D147BBA42} + EndGlobalSection EndGlobal diff --git a/README.md b/README.md index 0e844c8..172b59d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,9 @@ -# template-dotnet -.NET template for repositories +# NetEvolve.Logging.Abstractions + +This library provides some extensions to the `Microsoft.Extensions.Logging` library. It is intended to be used in conjunction further libraries that provide more specific logging implementations. + +## Provided Types & Extensions + +- `LoggedMessage` - A simple class that represents a message that has been logged. It contains the message, the log level, and the exception that was thrown (if any). +- `NullExternalScopeProvider` - An implementation of `IExternalScopeProvider` that does nothing. +- `NullScope` - An implementation of `IDisposable` that does nothing. \ No newline at end of file diff --git a/src/NetEvolve.Logging.Abstractions/IsExternalInit.cs b/src/NetEvolve.Logging.Abstractions/IsExternalInit.cs new file mode 100644 index 0000000..27a4368 --- /dev/null +++ b/src/NetEvolve.Logging.Abstractions/IsExternalInit.cs @@ -0,0 +1,16 @@ +// + +#if NETSTANDARD2_0 +namespace System.Runtime.CompilerServices +{ + using global::System.Diagnostics; + using global::System.Diagnostics.CodeAnalysis; + + /// + /// Reserved to be used by the compiler for tracking metadata. + /// This class should not be used by developers in source code. + /// + [ExcludeFromCodeCoverage, DebuggerNonUserCode] + internal static class IsExternalInit { } +} +#endif diff --git a/src/NetEvolve.Logging.Abstractions/LoggedMessage.cs b/src/NetEvolve.Logging.Abstractions/LoggedMessage.cs new file mode 100644 index 0000000..35c1f67 --- /dev/null +++ b/src/NetEvolve.Logging.Abstractions/LoggedMessage.cs @@ -0,0 +1,23 @@ +namespace NetEvolve.Logging.Abstractions; + +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Logging; + +/// +/// Represents a logged message, including the timestamp, log level, event ID, message, exception, and scopes. +/// +/// Timestamp of the logged message. +/// LogLevel of the logged message. +/// EventId of the logged message. +/// Logged message. +/// Logged exception. (optional) +/// Logged scopes. +public readonly record struct LoggedMessage( + DateTimeOffset Timestamp, + LogLevel LogLevel, + EventId EventId, + string Message, + Exception? Exception = null, + IReadOnlyCollection? Scopes = null +); diff --git a/src/NetEvolve.Logging.Abstractions/NetEvolve.Logging.Abstractions.csproj b/src/NetEvolve.Logging.Abstractions/NetEvolve.Logging.Abstractions.csproj new file mode 100644 index 0000000..697300f --- /dev/null +++ b/src/NetEvolve.Logging.Abstractions/NetEvolve.Logging.Abstractions.csproj @@ -0,0 +1,26 @@ + + + + $(ProjectTargetFrameworks) + + + + $(MSBuildProjectName) + + Contains several abstractions for logging, based on the nuget package `Microsoft.Extensions.Logging.Abstractions`. + + - `LoggedMessage` represents a message that was logged. + - `NullExternalScopeProvider` is a `IExternalScopeProvider` that does nothing. + - `NullScope` is a `IDisposable` that does nothing. + + + $(PackageTags);logging;abstractions + https://github.com/dailydevops/logging.abstractions.git + https://github.com/dailydevops/logging.abstractions.git + + + + + + + diff --git a/src/NetEvolve.Logging.Abstractions/NullExternalScopeProvider.cs b/src/NetEvolve.Logging.Abstractions/NullExternalScopeProvider.cs new file mode 100644 index 0000000..a469e06 --- /dev/null +++ b/src/NetEvolve.Logging.Abstractions/NullExternalScopeProvider.cs @@ -0,0 +1,26 @@ +namespace NetEvolve.Logging.Abstractions; + +using System; +using Microsoft.Extensions.Logging; + +/// +/// Scope provider that does nothing, used when no scope provider is available. +/// +public sealed class NullExternalScopeProvider : IExternalScopeProvider +{ + private static readonly Lazy _instance = + new(() => new NullExternalScopeProvider()); + + private NullExternalScopeProvider() { } + + /// + /// Returns a cached instance of . + /// + public static IExternalScopeProvider Instance => _instance.Value; + + /// + public void ForEachScope(Action callback, TState state) { } + + /// + public IDisposable Push(object? state) => NullScope.Instance; +} diff --git a/src/NetEvolve.Logging.Abstractions/NullScope.cs b/src/NetEvolve.Logging.Abstractions/NullScope.cs new file mode 100644 index 0000000..f493cc0 --- /dev/null +++ b/src/NetEvolve.Logging.Abstractions/NullScope.cs @@ -0,0 +1,21 @@ +namespace NetEvolve.Logging.Abstractions; + +using System; + +/// +/// Scope that does nothing. +/// +public sealed class NullScope : IDisposable +{ + private static readonly Lazy _instance = new(() => new NullScope()); + + /// + /// Returns a cached instance of . + /// + public static IDisposable Instance => _instance.Value; + + private NullScope() { } + + /// + public void Dispose() { } +} diff --git a/tests/NetEvolve.Logging.Abstractions.Tests.Unit/LoggedMessageTests.cs b/tests/NetEvolve.Logging.Abstractions.Tests.Unit/LoggedMessageTests.cs new file mode 100644 index 0000000..e4d55fd --- /dev/null +++ b/tests/NetEvolve.Logging.Abstractions.Tests.Unit/LoggedMessageTests.cs @@ -0,0 +1,29 @@ +namespace NetEvolve.Logging.Abstractions.Tests.Unit; + +using System; +using Microsoft.Extensions.Logging; +using Xunit; + +public class LoggedMessageTests +{ + [Fact] + public void Constructor_WithoutScopes_Expected() + { + // Arrange + var timestamp = DateTimeOffset.Now; + var logLevel = LogLevel.Information; + var message = "Hello World!"; + var eventId = new EventId(1, nameof(Constructor_WithoutScopes_Expected)); + + // Act + var loggedMessage = new LoggedMessage(timestamp, logLevel, eventId, message); + + // Assert + Assert.Equal(timestamp, loggedMessage.Timestamp); + Assert.Equal(logLevel, loggedMessage.LogLevel); + Assert.Equal(eventId, loggedMessage.EventId); + Assert.Equal(message, loggedMessage.Message); + Assert.Null(loggedMessage.Exception); + Assert.Null(loggedMessage.Scopes); + } +} diff --git a/tests/NetEvolve.Logging.Abstractions.Tests.Unit/NetEvolve.Logging.Abstractions.Tests.Unit.csproj b/tests/NetEvolve.Logging.Abstractions.Tests.Unit/NetEvolve.Logging.Abstractions.Tests.Unit.csproj new file mode 100644 index 0000000..77832fc --- /dev/null +++ b/tests/NetEvolve.Logging.Abstractions.Tests.Unit/NetEvolve.Logging.Abstractions.Tests.Unit.csproj @@ -0,0 +1,24 @@ + + + + $(TestTargetFrameworks) + + + + + + + + + + + + + + + + + + + + diff --git a/tests/NetEvolve.Logging.Abstractions.Tests.Unit/NullExternalScopeProviderTests.cs b/tests/NetEvolve.Logging.Abstractions.Tests.Unit/NullExternalScopeProviderTests.cs new file mode 100644 index 0000000..185e1f1 --- /dev/null +++ b/tests/NetEvolve.Logging.Abstractions.Tests.Unit/NullExternalScopeProviderTests.cs @@ -0,0 +1,51 @@ +namespace NetEvolve.Logging.Abstractions.Tests.Unit; + +using System.Collections.Generic; +using Xunit; + +public class NullExternalScopeProviderTests +{ + [Fact] + public void Instance_ReturnsSameInstance_Expected() + { + // Arrange + var instance1 = NullExternalScopeProvider.Instance; + var instance2 = NullExternalScopeProvider.Instance; + + // Assert + Assert.Same(instance1, instance2); + } + + [Fact] + public void ForEachScope_DoNothing_NoExceptionIsThrown() + { + // Arrange + var provider = NullExternalScopeProvider.Instance; + var state = new Dictionary { { "Hello World!", null } }; + + // Act + var ex = Record.Exception(() => provider.ForEachScope((_, _) => { }, state)); + + // Assert + Assert.Null(ex); + } + + [Fact] + public void Push_ReturnsNullScope_Expected() + { + // Arrange + var provider = NullExternalScopeProvider.Instance; + + // Act + var ex = Record.Exception(() => + { + using var scope = provider.Push(null); + + Assert.NotNull(scope); + _ = Assert.IsType(scope); + }); + + // Assert + Assert.Null(ex); + } +}