-
-
Notifications
You must be signed in to change notification settings - Fork 100
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Meziantou.Extensions.Logging.Xunit.v3 (#698)
- Loading branch information
Showing
15 changed files
with
343 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
src/Meziantou.Extensions.Logging.Xunit.v3/Meziantou.Extensions.Logging.Xunit.v3.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFrameworks>$(LatestTargetFrameworks);netstandard2.0</TargetFrameworks> | ||
<IsTrimmable>false</IsTrimmable> | ||
|
||
<Description>Microsoft.Extension.Logging.ILogger implementation for xunit.v3</Description> | ||
<PackageTags>xunit, xunit.v3, logger</PackageTags> | ||
<Version>1.0.0</Version> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.0" /> | ||
<PackageReference Include="xunit.v3.extensibility.core" Version="1.0.0" /> | ||
</ItemGroup> | ||
|
||
</Project> |
112 changes: 112 additions & 0 deletions
112
src/Meziantou.Extensions.Logging.Xunit.v3/XUnitLogger.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
using System.Text; | ||
using Microsoft.Extensions.Logging; | ||
using Xunit; | ||
|
||
#pragma warning disable IDE1006 // Naming Styles | ||
namespace Meziantou.Extensions.Logging.Xunit.v3; | ||
#pragma warning restore IDE1006 // Naming Styles | ||
|
||
public sealed class XUnitLogger<T> : XUnitLogger, ILogger<T> | ||
{ | ||
public XUnitLogger(ITestOutputHelper testOutputHelper, LoggerExternalScopeProvider scopeProvider) | ||
: base(testOutputHelper, scopeProvider, typeof(T).FullName) | ||
{ | ||
} | ||
} | ||
|
||
public class XUnitLogger : ILogger | ||
{ | ||
private readonly ITestOutputHelper _testOutputHelper; | ||
private readonly string? _categoryName; | ||
private readonly XUnitLoggerOptions _options; | ||
private readonly LoggerExternalScopeProvider _scopeProvider; | ||
|
||
public static ILogger CreateLogger(ITestOutputHelper testOutputHelper) => new XUnitLogger(testOutputHelper, new LoggerExternalScopeProvider(), ""); | ||
public static ILogger<T> CreateLogger<T>(ITestOutputHelper testOutputHelper) => new XUnitLogger<T>(testOutputHelper, new LoggerExternalScopeProvider()); | ||
|
||
public XUnitLogger(ITestOutputHelper testOutputHelper, LoggerExternalScopeProvider scopeProvider, string? categoryName) | ||
: this(testOutputHelper, scopeProvider, categoryName, appendScope: true) | ||
{ | ||
} | ||
|
||
public XUnitLogger(ITestOutputHelper testOutputHelper, LoggerExternalScopeProvider scopeProvider, string? categoryName, bool appendScope) | ||
: this(testOutputHelper, scopeProvider, categoryName, options: new XUnitLoggerOptions { IncludeScopes = appendScope }) | ||
{ | ||
} | ||
|
||
public XUnitLogger(ITestOutputHelper testOutputHelper, LoggerExternalScopeProvider scopeProvider, string? categoryName, XUnitLoggerOptions? options) | ||
{ | ||
_testOutputHelper = testOutputHelper; | ||
_scopeProvider = scopeProvider; | ||
_categoryName = categoryName; | ||
_options = options ?? new(); | ||
} | ||
|
||
public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None; | ||
|
||
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => _scopeProvider.Push(state); | ||
|
||
[SuppressMessage("ApiDesign", "RS0030:Do not use banned APIs")] | ||
[SuppressMessage("Usage", "MA0011:IFormatProvider is missing")] | ||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter) | ||
{ | ||
var sb = new StringBuilder(); | ||
|
||
if (_options.TimestampFormat is not null) | ||
{ | ||
var now = _options.UseUtcTimestamp ? DateTimeOffset.UtcNow : DateTimeOffset.Now; | ||
var timestamp = now.ToString(_options.TimestampFormat); | ||
sb.Append(timestamp).Append(' '); | ||
} | ||
|
||
if (_options.IncludeLogLevel) | ||
{ | ||
sb.Append(GetLogLevelString(logLevel)).Append(' '); | ||
} | ||
|
||
if (_options.IncludeCategory) | ||
{ | ||
sb.Append('[').Append(_categoryName).Append("] "); | ||
} | ||
|
||
sb.Append(formatter(state, exception)); | ||
|
||
if (exception is not null) | ||
{ | ||
sb.Append('\n').Append(exception); | ||
} | ||
|
||
// Append scopes | ||
if (_options.IncludeScopes) | ||
{ | ||
_scopeProvider.ForEachScope((scope, state) => | ||
{ | ||
state.Append("\n => "); | ||
state.Append(scope); | ||
}, sb); | ||
} | ||
|
||
try | ||
{ | ||
_testOutputHelper.WriteLine(sb.ToString()); | ||
} | ||
catch | ||
{ | ||
// This can happen when the test is not active | ||
} | ||
} | ||
|
||
private static string GetLogLevelString(LogLevel logLevel) | ||
{ | ||
return logLevel switch | ||
{ | ||
LogLevel.Trace => "trce", | ||
LogLevel.Debug => "dbug", | ||
LogLevel.Information => "info", | ||
LogLevel.Warning => "warn", | ||
LogLevel.Error => "fail", | ||
LogLevel.Critical => "crit", | ||
_ => throw new ArgumentOutOfRangeException(nameof(logLevel)) | ||
}; | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
src/Meziantou.Extensions.Logging.Xunit.v3/XUnitLoggerOptions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
#pragma warning disable IDE1006 // Naming Styles | ||
namespace Meziantou.Extensions.Logging.Xunit.v3; | ||
#pragma warning restore IDE1006 // Naming Styles | ||
|
||
public sealed class XUnitLoggerOptions | ||
{ | ||
/// <summary> | ||
/// Includes scopes when <see langword="true" />. | ||
/// </summary> | ||
public bool IncludeScopes { get; set; } | ||
|
||
/// <summary> | ||
/// Includes category when <see langword="true" />. | ||
/// </summary> | ||
public bool IncludeCategory { get; set; } | ||
|
||
/// <summary> | ||
/// Includes log level when <see langword="true" />. | ||
/// </summary> | ||
public bool IncludeLogLevel { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets format string used to format timestamp in logging messages. Defaults to <see langword="null" />. | ||
/// </summary> | ||
[StringSyntax(StringSyntaxAttribute.DateTimeFormat)] | ||
public string? TimestampFormat { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets indication whether or not UTC timezone should be used to format timestamps in logging messages. Defaults to <see langword="false" />. | ||
/// </summary> | ||
public bool UseUtcTimestamp { get; set; } | ||
} |
38 changes: 38 additions & 0 deletions
38
src/Meziantou.Extensions.Logging.Xunit.v3/XUnitLoggerProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
using Microsoft.Extensions.Logging; | ||
using Xunit; | ||
|
||
#pragma warning disable IDE1006 // Naming Styles | ||
namespace Meziantou.Extensions.Logging.Xunit.v3; | ||
#pragma warning restore IDE1006 // Naming Styles | ||
|
||
public sealed class XUnitLoggerProvider : ILoggerProvider | ||
{ | ||
private readonly ITestOutputHelper _testOutputHelper; | ||
private readonly XUnitLoggerOptions _options; | ||
private readonly LoggerExternalScopeProvider _scopeProvider = new(); | ||
|
||
public XUnitLoggerProvider(ITestOutputHelper testOutputHelper) | ||
: this(testOutputHelper, options: null) | ||
{ | ||
} | ||
|
||
public XUnitLoggerProvider(ITestOutputHelper testOutputHelper, bool appendScope) | ||
: this(testOutputHelper, new XUnitLoggerOptions { IncludeScopes = appendScope }) | ||
{ | ||
} | ||
|
||
public XUnitLoggerProvider(ITestOutputHelper testOutputHelper, XUnitLoggerOptions? options) | ||
{ | ||
_testOutputHelper = testOutputHelper; | ||
_options = options ?? new XUnitLoggerOptions(); | ||
} | ||
|
||
public ILogger CreateLogger(string categoryName) | ||
{ | ||
return new XUnitLogger(_testOutputHelper, _scopeProvider, categoryName, _options); | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# Meziantou.Extensions.Logging.Xunit.v3 | ||
|
||
```c# | ||
ILogger logger = XUnitLogger.CreateLogger(); | ||
ILogger<MyType> logger = XUnitLogger.CreateLogger<MyType>(); | ||
``` | ||
|
||
If you are using a `WebApplicationFactory`: | ||
|
||
```c# | ||
public class UnitTest1(ITestOutputHelper testOutputHelper) | ||
{ | ||
[Fact] | ||
public async Task Test1() | ||
{ | ||
using var factory = new WebApplicationFactory<Program>() | ||
.WithWebHostBuilder(builder => | ||
{ | ||
builder.ConfigureLogging(builder => | ||
{ | ||
// You can override the logging configuration if needed | ||
//builder.SetMinimumLevel(LogLevel.Trace); | ||
//builder.AddFilter(_ => true); | ||
// Register the xUnit logger provider | ||
builder.Services.AddSingleton<ILoggerProvider>(new XUnitLoggerProvider(testOutputHelper, appendScope: false)); | ||
}); | ||
}); | ||
} | ||
} | ||
``` | ||
|
||
Blog post about this package: [How to write logs from ILogger to xUnit.net ITestOutputHelper](https://www.meziantou.net/how-to-view-logs-from-ilogger-in-xunitdotnet.htm) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...g.Tests.Tests/InMemoryTestOutputHelper.cs → ...g.Xunit.Tests/InMemoryTestOutputHelper.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
46 changes: 46 additions & 0 deletions
46
tests/Meziantou.Extensions.Logging.Xunit.v3.Tests/InMemoryTestOutputHelper.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
#pragma warning disable IDE1006 // Naming Styles | ||
using System.Globalization; | ||
using Xunit; | ||
|
||
namespace Meziantou.Extensions.Logging.Xunit.v3.Tests; | ||
|
||
internal sealed class InMemoryTestOutputHelper : ITestOutputHelper | ||
{ | ||
private readonly List<string> _logs = new(); | ||
|
||
public IEnumerable<string> Logs => _logs; | ||
|
||
public string Output { get; } | ||
|
||
public void Write(string message) | ||
{ | ||
lock (_logs) | ||
{ | ||
_logs.Add(message); | ||
} | ||
} | ||
|
||
public void Write(string format, params object[] args) | ||
{ | ||
lock (_logs) | ||
{ | ||
_logs.Add(string.Format(CultureInfo.InvariantCulture, format, args)); | ||
} | ||
} | ||
|
||
public void WriteLine(string message) | ||
{ | ||
lock (_logs) | ||
{ | ||
_logs.Add(message + Environment.NewLine); | ||
} | ||
} | ||
|
||
public void WriteLine(string format, params object[] args) | ||
{ | ||
lock (_logs) | ||
{ | ||
_logs.Add(string.Format(CultureInfo.InvariantCulture, format, args) + Environment.NewLine); | ||
} | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
...ntou.Extensions.Logging.Xunit.v3.Tests/Meziantou.Extensions.Logging.Xunit.v3.Tests.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFrameworks>$(LatestTargetFrameworks)</TargetFrameworks> | ||
<IncludeDefaultTestReferences>false</IncludeDefaultTestReferences> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" /> | ||
<PackageReference Include="xunit.v3" Version="1.0.0" /> | ||
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.0" /> | ||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\src\Meziantou.Extensions.Logging.Xunit.v3\Meziantou.Extensions.Logging.Xunit.v3.csproj" /> | ||
</ItemGroup> | ||
</Project> |
32 changes: 32 additions & 0 deletions
32
tests/Meziantou.Extensions.Logging.Xunit.v3.Tests/XunitLoggerTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
#pragma warning disable CA1848 // Use the LoggerMessage delegates | ||
#pragma warning disable IDE1006 // Naming Styles | ||
using Microsoft.Extensions.Hosting; | ||
using Microsoft.Extensions.Logging; | ||
using Xunit; | ||
using Meziantou.Extensions.Logging.Xunit; | ||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
namespace Meziantou.Extensions.Logging.Xunit.v3.Tests; | ||
|
||
public sealed class XunitLoggerTests | ||
{ | ||
[Fact] | ||
public void XUnitLoggerProviderTest() | ||
{ | ||
var output = new InMemoryTestOutputHelper(); | ||
using var provider = new XUnitLoggerProvider(output); | ||
var host = new HostBuilder() | ||
.ConfigureLogging(builder => | ||
{ | ||
builder.Services.AddSingleton<ILoggerProvider>(provider); | ||
|
||
}) | ||
.Build(); | ||
|
||
var logger = host.Services.GetRequiredService<ILogger<XunitLoggerTests>>(); | ||
logger.LogInformation("Test"); | ||
logger.LogInformation("Test {Sample}", "value"); | ||
|
||
Assert.Equal(["Test" + Environment.NewLine, "Test value" + Environment.NewLine], output.Logs); | ||
} | ||
} |
Oops, something went wrong.