mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Replace local xunit logging project with new signed nuget package
This commit is contained in:
@@ -52,8 +52,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LegacyTestApp", "test\Legac
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharpWpf", "samples\CSharpWpf\CSharpWpf.csproj", "{9E0F2B00-1B88-4B75-BEED-6DF8DBCA36B5}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharpWpf", "samples\CSharpWpf\CSharpWpf.csproj", "{9E0F2B00-1B88-4B75-BEED-6DF8DBCA36B5}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Divergic.Logging.Xunit", "test\Divergic.Logging.Xunit\Divergic.Logging.Xunit.csproj", "{5ED2E9AF-101D-4D2D-B0B5-90A920EF692D}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Velopack.Build", "src\vpk\Velopack.Build\Velopack.Build.csproj", "{97C9B2CF-877F-4C98-A513-058784A23697}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Velopack.Build", "src\vpk\Velopack.Build\Velopack.Build.csproj", "{97C9B2CF-877F-4C98-A513-058784A23697}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Velopack.IcoLib", "src\vpk\Velopack.IcoLib\Velopack.IcoLib.csproj", "{8A0A980A-D51C-458E-8942-00BC900FD2D0}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Velopack.IcoLib", "src\vpk\Velopack.IcoLib\Velopack.IcoLib.csproj", "{8A0A980A-D51C-458E-8942-00BC900FD2D0}"
|
||||||
@@ -116,10 +114,6 @@ Global
|
|||||||
{9E0F2B00-1B88-4B75-BEED-6DF8DBCA36B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{9E0F2B00-1B88-4B75-BEED-6DF8DBCA36B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{9E0F2B00-1B88-4B75-BEED-6DF8DBCA36B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{9E0F2B00-1B88-4B75-BEED-6DF8DBCA36B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{9E0F2B00-1B88-4B75-BEED-6DF8DBCA36B5}.Release|Any CPU.Build.0 = Release|Any CPU
|
{9E0F2B00-1B88-4B75-BEED-6DF8DBCA36B5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{5ED2E9AF-101D-4D2D-B0B5-90A920EF692D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{5ED2E9AF-101D-4D2D-B0B5-90A920EF692D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{5ED2E9AF-101D-4D2D-B0B5-90A920EF692D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{5ED2E9AF-101D-4D2D-B0B5-90A920EF692D}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{97C9B2CF-877F-4C98-A513-058784A23697}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{97C9B2CF-877F-4C98-A513-058784A23697}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{97C9B2CF-877F-4C98-A513-058784A23697}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{97C9B2CF-877F-4C98-A513-058784A23697}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{97C9B2CF-877F-4C98-A513-058784A23697}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{97C9B2CF-877F-4C98-A513-058784A23697}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
@@ -140,7 +134,6 @@ Global
|
|||||||
{1FF6A262-13D0-45DF-B818-77AC84C52C6F} = {3EBFA551-780C-473D-A197-0EE56F2CBA82}
|
{1FF6A262-13D0-45DF-B818-77AC84C52C6F} = {3EBFA551-780C-473D-A197-0EE56F2CBA82}
|
||||||
{8B27C4BF-21B8-48B0-80F8-74520227C35F} = {7AC3A776-B582-4B65-9D03-BD52332B5CA3}
|
{8B27C4BF-21B8-48B0-80F8-74520227C35F} = {7AC3A776-B582-4B65-9D03-BD52332B5CA3}
|
||||||
{9E0F2B00-1B88-4B75-BEED-6DF8DBCA36B5} = {3EBFA551-780C-473D-A197-0EE56F2CBA82}
|
{9E0F2B00-1B88-4B75-BEED-6DF8DBCA36B5} = {3EBFA551-780C-473D-A197-0EE56F2CBA82}
|
||||||
{5ED2E9AF-101D-4D2D-B0B5-90A920EF692D} = {7AC3A776-B582-4B65-9D03-BD52332B5CA3}
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {68CA987A-9BAB-4C75-8EEB-4596BA6BBD07}
|
SolutionGuid = {68CA987A-9BAB-4C75-8EEB-4596BA6BBD07}
|
||||||
|
|||||||
@@ -37,8 +37,8 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition=" $(MSBuildProjectName.EndsWith('Tests')) ">
|
<ItemGroup Condition=" $(MSBuildProjectName.EndsWith('Tests')) ">
|
||||||
<ProjectReference Include="..\Divergic.Logging.Xunit\Divergic.Logging.Xunit.csproj" />
|
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||||
|
<PackageReference Include="Neovolve.Logging.Xunit.Signed" Version="6.1.0" />
|
||||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||||
<PackageReference Include="xunit" Version="2.9.0" />
|
<PackageReference Include="xunit" Version="2.9.0" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
|
||||||
|
|||||||
@@ -1,156 +0,0 @@
|
|||||||
namespace Divergic.Logging.Xunit
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="CacheLogger" />
|
|
||||||
/// class provides a cache of log entries written to the logger.
|
|
||||||
/// </summary>
|
|
||||||
public class CacheLogger : FilterLogger, ICacheLogger
|
|
||||||
{
|
|
||||||
private static readonly AsyncLocal<ConcurrentStack<CacheScope>> _scopes =
|
|
||||||
new AsyncLocal<ConcurrentStack<CacheScope>>();
|
|
||||||
|
|
||||||
private readonly ILoggerFactory? _factory;
|
|
||||||
private readonly IList<LogEntry> _logEntries = new List<LogEntry>();
|
|
||||||
private readonly ILogger? _logger;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new instance of the <see cref="CacheLogger" /> class.
|
|
||||||
/// </summary>
|
|
||||||
public CacheLogger()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new instance of the <see cref="CacheLogger" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="logger">The source logger.</param>
|
|
||||||
/// <exception cref="ArgumentNullException">The <paramref name="logger" /> is <c>null</c>.</exception>
|
|
||||||
public CacheLogger(ILogger logger)
|
|
||||||
{
|
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
||||||
}
|
|
||||||
|
|
||||||
internal CacheLogger(ILogger logger, ILoggerFactory factory)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_factory = factory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override IDisposable? BeginScope<TState>(TState state)
|
|
||||||
{
|
|
||||||
var scope = _logger?.BeginScope(state) ?? NoopDisposable.Instance;
|
|
||||||
|
|
||||||
var cacheScope = new CacheScope(scope, state, () => Scopes.TryPop(out _));
|
|
||||||
|
|
||||||
Scopes.Push(cacheScope);
|
|
||||||
|
|
||||||
return cacheScope;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override bool IsEnabled(LogLevel logLevel)
|
|
||||||
{
|
|
||||||
if (_logger == null)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _logger.IsEnabled(logLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Disposes resources held by this instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="disposing"><c>true</c> if disposing unmanaged types; otherwise <c>false</c>.</param>
|
|
||||||
protected virtual void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
_factory?.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void WriteLogEntry<TState>(
|
|
||||||
LogLevel logLevel,
|
|
||||||
EventId eventId,
|
|
||||||
TState state,
|
|
||||||
string message,
|
|
||||||
Exception? exception,
|
|
||||||
Func<TState, Exception?, string> formatter)
|
|
||||||
{
|
|
||||||
var entry = new LogEntry(
|
|
||||||
logLevel,
|
|
||||||
eventId,
|
|
||||||
state,
|
|
||||||
exception,
|
|
||||||
message,
|
|
||||||
Scopes.Select(s => s.State).ToArray());
|
|
||||||
|
|
||||||
_logEntries.Add(entry);
|
|
||||||
|
|
||||||
_logger?.Log(logLevel, eventId, state, exception, formatter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the count of cached log entries.
|
|
||||||
/// </summary>
|
|
||||||
public int Count => _logEntries.Count;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the cached log entries.
|
|
||||||
/// </summary>
|
|
||||||
public IReadOnlyCollection<LogEntry> Entries => new ReadOnlyCollection<LogEntry>(_logEntries);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the last entry logged.
|
|
||||||
/// </summary>
|
|
||||||
public LogEntry? Last
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var count = _logEntries.Count;
|
|
||||||
|
|
||||||
if (count == 0)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _logEntries[count - 1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ConcurrentStack<CacheScope> Scopes
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var scopes = _scopes.Value;
|
|
||||||
|
|
||||||
if (scopes == null)
|
|
||||||
{
|
|
||||||
scopes = new ConcurrentStack<CacheScope>();
|
|
||||||
|
|
||||||
_scopes.Value = scopes;
|
|
||||||
}
|
|
||||||
|
|
||||||
return scopes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
namespace Divergic.Logging.Xunit
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="CacheLogger{T}" />
|
|
||||||
/// class provides a cache logger for <see cref="ILogger{TCategoryName}" />.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The generic type of logger.</typeparam>
|
|
||||||
public class CacheLogger<T> : CacheLogger, ICacheLogger<T>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new instance of the <see cref="CacheLogger{T}" /> class.
|
|
||||||
/// </summary>
|
|
||||||
public CacheLogger()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new instance of the <see cref="CacheLogger{T}" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="logger">The source logger.</param>
|
|
||||||
/// <exception cref="ArgumentNullException">The <paramref name="logger" /> is <c>null</c>.</exception>
|
|
||||||
public CacheLogger(ILogger logger)
|
|
||||||
: base(logger)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
internal CacheLogger(ILogger logger, ILoggerFactory factory)
|
|
||||||
: base(logger, factory)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
namespace Divergic.Logging.Xunit
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
|
|
||||||
internal class CacheScope : IDisposable
|
|
||||||
{
|
|
||||||
private readonly Action _onScopeEnd;
|
|
||||||
private readonly IDisposable _scope;
|
|
||||||
|
|
||||||
public CacheScope(IDisposable scope, object? state, Action onScopeEnd)
|
|
||||||
{
|
|
||||||
_scope = scope;
|
|
||||||
State = state;
|
|
||||||
_onScopeEnd = onScopeEnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
// Pass on the end scope request
|
|
||||||
_scope.Dispose();
|
|
||||||
|
|
||||||
// Clean up the scope in the cache logger
|
|
||||||
_onScopeEnd.Invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
public object? State { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
namespace Divergic.Logging.Xunit
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="DefaultFormatter" />
|
|
||||||
/// class provides the default formatting of log messages for xUnit test output.
|
|
||||||
/// </summary>
|
|
||||||
public class DefaultFormatter : ILogFormatter
|
|
||||||
{
|
|
||||||
private readonly LoggingConfig _config;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="DefaultFormatter" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="config">The logging configuration.</param>
|
|
||||||
/// <exception cref="ArgumentNullException">The <paramref name="config" /> value is <c>null</c>.</exception>
|
|
||||||
public DefaultFormatter(LoggingConfig config)
|
|
||||||
{
|
|
||||||
_config = config ?? throw new ArgumentNullException(nameof(config));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public virtual string Format(
|
|
||||||
int scopeLevel,
|
|
||||||
string categoryName,
|
|
||||||
LogLevel logLevel,
|
|
||||||
EventId eventId,
|
|
||||||
string message,
|
|
||||||
Exception? exception)
|
|
||||||
{
|
|
||||||
var padding = new string(' ', scopeLevel * _config.ScopePaddingSpaces);
|
|
||||||
var parts = new List<string>(2);
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(message) == false)
|
|
||||||
{
|
|
||||||
var part = string.Format(CultureInfo.InvariantCulture, FormatMask, padding, logLevel, eventId.Id,
|
|
||||||
message);
|
|
||||||
|
|
||||||
part = MaskSensitiveValues(part);
|
|
||||||
|
|
||||||
parts.Add(part);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exception != null)
|
|
||||||
{
|
|
||||||
var part = string.Format(
|
|
||||||
CultureInfo.InvariantCulture,
|
|
||||||
FormatMask,
|
|
||||||
padding,
|
|
||||||
logLevel,
|
|
||||||
eventId.Id,
|
|
||||||
exception);
|
|
||||||
|
|
||||||
part = MaskSensitiveValues(part);
|
|
||||||
|
|
||||||
parts.Add(part);
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Join(Environment.NewLine, parts);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string MaskSensitiveValues(string value)
|
|
||||||
{
|
|
||||||
const string mask = "****";
|
|
||||||
|
|
||||||
for (var index = 0; index < _config.SensitiveValues.Count; index++)
|
|
||||||
{
|
|
||||||
var sensitiveValue = _config.SensitiveValues[index];
|
|
||||||
|
|
||||||
value = value.Replace(sensitiveValue, mask);
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the string format mask used to generate a log message.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>The format values are:
|
|
||||||
/// <ul>
|
|
||||||
/// <li>0: Padding</li>
|
|
||||||
/// <li>1: Level</li>
|
|
||||||
/// <li>2: Event Id</li>
|
|
||||||
/// <li>3: Message</li>
|
|
||||||
/// </ul>
|
|
||||||
/// </remarks>
|
|
||||||
protected virtual string FormatMask { get; } = "{0}{1} [{2}]: {3}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
namespace Divergic.Logging.Xunit;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="DefaultScopeFormatter" />
|
|
||||||
/// class is used to provide log message formatting logic for scope beginning and end messages.
|
|
||||||
/// </summary>
|
|
||||||
public class DefaultScopeFormatter : DefaultFormatter
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="DefaultScopeFormatter" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="config">The logging configuration.</param>
|
|
||||||
public DefaultScopeFormatter(LoggingConfig config) : base(config)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override string FormatMask { get; } = "{0}{3}";
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFrameworks>net48;net8.0</TargetFrameworks>
|
|
||||||
<LangVersion>latest</LangVersion>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<SignAssembly>True</SignAssembly>
|
|
||||||
<AssemblyOriginatorKeyFile>..\..\Velopack.snk</AssemblyOriginatorKeyFile>
|
|
||||||
<NoWarn>$(NoWarn);IDE0161</NoWarn>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
|
|
||||||
<PackageReference Include="System.Text.Json" Version="8.0.4" />
|
|
||||||
<PackageReference Include="Xunit.Abstractions" Version="2.0.3" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
namespace Divergic.Logging.Xunit
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="FilterLogger" />
|
|
||||||
/// class provides common filtering logic to ensure log records are only written for enabled loggers where there is a
|
|
||||||
/// formatted message and/or exception to log.
|
|
||||||
/// </summary>
|
|
||||||
public abstract class FilterLogger : ILogger
|
|
||||||
{
|
|
||||||
private readonly string _nullFormatted = "[null]";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public abstract IDisposable? BeginScope<TState>(TState state) where TState : notnull;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public abstract bool IsEnabled(LogLevel logLevel);
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Log<TState>(
|
|
||||||
LogLevel logLevel,
|
|
||||||
EventId eventId,
|
|
||||||
TState state,
|
|
||||||
Exception? exception,
|
|
||||||
Func<TState, Exception?, string> formatter)
|
|
||||||
{
|
|
||||||
formatter = formatter ?? throw new ArgumentNullException(nameof(formatter));
|
|
||||||
|
|
||||||
if (IsEnabled(logLevel) == false)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var formattedMessage = FormatMessage(state, exception, formatter);
|
|
||||||
|
|
||||||
if (ShouldFilter(formattedMessage, exception))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteLogEntry(logLevel, eventId, state, formattedMessage, exception, formatter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the formatted log message.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TState">The type of state data to log.</typeparam>
|
|
||||||
/// <param name="state">The state data to log.</param>
|
|
||||||
/// <param name="exception">The exception to log.</param>
|
|
||||||
/// <param name="formatter">The formatter that creates the log message.</param>
|
|
||||||
/// <returns>The log message.</returns>
|
|
||||||
protected string FormatMessage<TState>(
|
|
||||||
TState state,
|
|
||||||
Exception? exception,
|
|
||||||
Func<TState, Exception?, string> formatter)
|
|
||||||
{
|
|
||||||
formatter = formatter ?? throw new ArgumentNullException(nameof(formatter));
|
|
||||||
|
|
||||||
#pragma warning disable CA1062 // Validate arguments of public methods
|
|
||||||
var formattedMessage = formatter(state, exception);
|
|
||||||
#pragma warning restore CA1062 // Validate arguments of public methods
|
|
||||||
|
|
||||||
// Clear the message if it looks like a null formatted message
|
|
||||||
if (formattedMessage == _nullFormatted)
|
|
||||||
{
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
return formattedMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the log message should be filtered and not written.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="message">The message to log.</param>
|
|
||||||
/// <param name="exception">The exception to log.</param>
|
|
||||||
/// <returns><c>true</c> if the log should not be written; otherwise <c>false</c>.</returns>
|
|
||||||
protected virtual bool ShouldFilter(string message, Exception? exception)
|
|
||||||
{
|
|
||||||
if (exception != null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(message))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Writes the log entry with the specified values.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TState">The type of state data to log.</typeparam>
|
|
||||||
/// <param name="logLevel">The log level.</param>
|
|
||||||
/// <param name="eventId">The event id.</param>
|
|
||||||
/// <param name="state">The state data to log.</param>
|
|
||||||
/// <param name="message">The formatted message.</param>
|
|
||||||
/// <param name="exception">The exception to log.</param>
|
|
||||||
/// <param name="formatter">The formatter that creates the log message.</param>
|
|
||||||
protected abstract void WriteLogEntry<TState>(
|
|
||||||
LogLevel logLevel,
|
|
||||||
EventId eventId,
|
|
||||||
TState state,
|
|
||||||
string message,
|
|
||||||
Exception? exception,
|
|
||||||
Func<TState, Exception?, string> formatter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
namespace Divergic.Logging.Xunit
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="ICacheLogger" />
|
|
||||||
/// interface defines the members for recording and accessing log entries.
|
|
||||||
/// </summary>
|
|
||||||
public interface ICacheLogger : ILogger, IDisposable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the number of cache entries recorded.
|
|
||||||
/// </summary>
|
|
||||||
int Count { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the recorded cache entries.
|
|
||||||
/// </summary>
|
|
||||||
IReadOnlyCollection<LogEntry> Entries { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the latest cache entry.
|
|
||||||
/// </summary>
|
|
||||||
LogEntry? Last { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
namespace Divergic.Logging.Xunit
|
|
||||||
{
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="ICacheLogger" />
|
|
||||||
/// interface defines the members for recording and accessing log entries.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of class using the cache.</typeparam>
|
|
||||||
public interface ICacheLogger<out T> : ICacheLogger, ILogger<T>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
namespace Divergic.Logging.Xunit
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="ILogFormatter" />
|
|
||||||
/// interface defines the members for formatting log messages.
|
|
||||||
/// </summary>
|
|
||||||
public interface ILogFormatter
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Formats the log message with the specified values.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="scopeLevel">The number of active logging scopes.</param>
|
|
||||||
/// <param name="categoryName">The logger name.</param>
|
|
||||||
/// <param name="logLevel">The log level.</param>
|
|
||||||
/// <param name="eventId">The event id.</param>
|
|
||||||
/// <param name="message">The log message.</param>
|
|
||||||
/// <param name="exception">The exception to be logged.</param>
|
|
||||||
/// <returns>The formatted log message.</returns>
|
|
||||||
string Format(
|
|
||||||
int scopeLevel,
|
|
||||||
string categoryName,
|
|
||||||
LogLevel logLevel,
|
|
||||||
EventId eventId,
|
|
||||||
string message,
|
|
||||||
Exception? exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
namespace Divergic.Logging.Xunit
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="LogEntry" />
|
|
||||||
/// class is used to identify the data related to a log entry.
|
|
||||||
/// </summary>
|
|
||||||
public class LogEntry
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="LogEntry" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="logLevel">The log level.</param>
|
|
||||||
/// <param name="eventId">The event id.</param>
|
|
||||||
/// <param name="state">The state.</param>
|
|
||||||
/// <param name="exception">The exception.</param>
|
|
||||||
/// <param name="message">The message.</param>
|
|
||||||
/// <param name="scopes">The currently active scopes.</param>
|
|
||||||
public LogEntry(
|
|
||||||
LogLevel logLevel,
|
|
||||||
EventId eventId,
|
|
||||||
object? state,
|
|
||||||
Exception? exception,
|
|
||||||
string message,
|
|
||||||
IReadOnlyCollection<object?> scopes)
|
|
||||||
{
|
|
||||||
LogLevel = logLevel;
|
|
||||||
EventId = eventId;
|
|
||||||
State = state;
|
|
||||||
Exception = exception;
|
|
||||||
Message = message;
|
|
||||||
Scopes = scopes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the event id of the entry.
|
|
||||||
/// </summary>
|
|
||||||
public EventId EventId { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the exception of the entry.
|
|
||||||
/// </summary>
|
|
||||||
public Exception? Exception { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the log level of the entry.
|
|
||||||
/// </summary>
|
|
||||||
public LogLevel LogLevel { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the message of the entry.
|
|
||||||
/// </summary>
|
|
||||||
public string Message { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the scopes active at the time of the call to <see cref="ILogger.Log{TState}" />
|
|
||||||
/// </summary>
|
|
||||||
public IReadOnlyCollection<object?> Scopes { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the state of the entry.
|
|
||||||
/// </summary>
|
|
||||||
public object? State { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
namespace Divergic.Logging.Xunit
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
using global::Xunit.Abstractions;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="LogFactory" />
|
|
||||||
/// class is used to create <see cref="ILogger" /> instances.
|
|
||||||
/// </summary>
|
|
||||||
public static class LogFactory
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Creates an <see cref="ILoggerFactory" /> instance that is configured for xUnit output.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="output">The test output.</param>
|
|
||||||
/// <param name="config">Optional logging configuration.</param>
|
|
||||||
/// <returns>The logger factory.</returns>
|
|
||||||
/// <exception cref="ArgumentNullException">The <paramref name="output" /> is <c>null</c>.</exception>
|
|
||||||
public static ILoggerFactory Create(
|
|
||||||
ITestOutputHelper output, LoggingConfig? config = null)
|
|
||||||
{
|
|
||||||
output = output ?? throw new ArgumentNullException(nameof(output));
|
|
||||||
|
|
||||||
var factory = new LoggerFactory();
|
|
||||||
|
|
||||||
factory.AddXunit(output, config);
|
|
||||||
|
|
||||||
return factory;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
namespace Divergic.Logging.Xunit
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="LoggerExtensions" />
|
|
||||||
/// class provides extension methods for wrapping <see cref="ILogger" /> instances in <see cref="ICacheLogger" />.
|
|
||||||
/// </summary>
|
|
||||||
public static class LoggerExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a <see cref="ICacheLogger" /> for the specified logger.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="logger">The source logger.</param>
|
|
||||||
/// <returns>The cache logger.</returns>
|
|
||||||
/// <exception cref="ArgumentNullException">The <paramref name="logger" /> is <c>null</c>.</exception>
|
|
||||||
public static ICacheLogger WithCache(this ILogger logger)
|
|
||||||
{
|
|
||||||
logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
||||||
|
|
||||||
var cacheLogger = new CacheLogger(logger);
|
|
||||||
|
|
||||||
return cacheLogger;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a <see cref="ICacheLogger{T}" /> for the specified logger.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of generic logger.</typeparam>
|
|
||||||
/// <param name="logger">The source logger.</param>
|
|
||||||
/// <returns>The cache logger.</returns>
|
|
||||||
/// <exception cref="ArgumentNullException">The <paramref name="logger" /> is <c>null</c>.</exception>
|
|
||||||
public static ICacheLogger<T> WithCache<T>(this ILogger<T> logger)
|
|
||||||
{
|
|
||||||
logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
||||||
|
|
||||||
var cacheLogger = new CacheLogger<T>(logger);
|
|
||||||
|
|
||||||
return cacheLogger;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static ICacheLogger WithCache(this ILogger logger, ILoggerFactory factory)
|
|
||||||
{
|
|
||||||
var cacheLogger = new CacheLogger(logger, factory);
|
|
||||||
|
|
||||||
return cacheLogger;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static ICacheLogger<T> WithCache<T>(this ILogger<T> logger, ILoggerFactory factory)
|
|
||||||
{
|
|
||||||
var cacheLogger = new CacheLogger<T>(logger, factory);
|
|
||||||
|
|
||||||
return cacheLogger;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
namespace Microsoft.Extensions.Logging
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
using Divergic.Logging.Xunit;
|
|
||||||
using Xunit.Abstractions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="LoggerFactoryExtensions" />
|
|
||||||
/// class provides extension methods for configuring <see cref="ILoggerFactory" /> with providers.
|
|
||||||
/// </summary>
|
|
||||||
public static class LoggerFactoryExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Registers the <see cref="TestOutputLoggerProvider" /> in the factory using the specified
|
|
||||||
/// <see cref="ITestOutputHelper" />.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="factory">The factory to add the provider to.</param>
|
|
||||||
/// <param name="output">The test output reference.</param>
|
|
||||||
/// <param name="config">Optional logging configuration.</param>
|
|
||||||
/// <returns>The logger factory.</returns>
|
|
||||||
/// <exception cref="ArgumentNullException">The <paramref name="factory" /> is <c>null</c>.</exception>
|
|
||||||
/// <exception cref="ArgumentNullException">The <paramref name="output" /> is <c>null</c>.</exception>
|
|
||||||
public static ILoggerFactory AddXunit(this ILoggerFactory factory, ITestOutputHelper output,
|
|
||||||
LoggingConfig? config = null)
|
|
||||||
{
|
|
||||||
factory = factory ?? throw new ArgumentNullException(nameof(factory));
|
|
||||||
output = output ?? throw new ArgumentNullException(nameof(output));
|
|
||||||
|
|
||||||
#pragma warning disable CA2000 // Dispose objects before losing scope
|
|
||||||
var provider = new TestOutputLoggerProvider(output, config);
|
|
||||||
#pragma warning restore CA2000 // Dispose objects before losing scope
|
|
||||||
|
|
||||||
#pragma warning disable CA1062 // Validate arguments of public methods
|
|
||||||
factory.AddProvider(provider);
|
|
||||||
#pragma warning restore CA1062 // Validate arguments of public methods
|
|
||||||
|
|
||||||
return factory;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
// ReSharper disable once CheckNamespace
|
|
||||||
namespace Microsoft.Extensions.Logging
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
using Divergic.Logging.Xunit;
|
|
||||||
using Xunit.Abstractions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="LoggingBuilderExtensions" />
|
|
||||||
/// class provides extension methods for the <see cref="ILoggingBuilder" /> interface.
|
|
||||||
/// </summary>
|
|
||||||
public static class LoggingBuilderExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a logger to writes to the xUnit test output to the specified logging builder.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="builder">The logging builder.</param>
|
|
||||||
/// <param name="output">The xUnit test output helper.</param>
|
|
||||||
/// <param name="config">Optional logging configuration.</param>
|
|
||||||
public static void AddXunit(this ILoggingBuilder builder, ITestOutputHelper output,
|
|
||||||
LoggingConfig? config = null)
|
|
||||||
{
|
|
||||||
builder = builder ?? throw new ArgumentNullException(nameof(builder));
|
|
||||||
output = output ?? throw new ArgumentNullException(nameof(output));
|
|
||||||
|
|
||||||
// Object is added as a provider to the builder and cannot be disposed of here
|
|
||||||
#pragma warning disable CA2000 // Dispose objects before losing scope
|
|
||||||
var provider = new TestOutputLoggerProvider(output, config);
|
|
||||||
#pragma warning restore CA2000 // Dispose objects before losing scope
|
|
||||||
|
|
||||||
builder.AddProvider(provider);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
namespace Divergic.Logging.Xunit
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="LoggingConfig" />
|
|
||||||
/// class is used to configure how logging operates.
|
|
||||||
/// </summary>
|
|
||||||
public class LoggingConfig
|
|
||||||
{
|
|
||||||
private ILogFormatter _formatter;
|
|
||||||
private ILogFormatter _scopeFormatter;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="LoggingConfig" /> class.
|
|
||||||
/// </summary>
|
|
||||||
public LoggingConfig()
|
|
||||||
{
|
|
||||||
_formatter = new DefaultFormatter(this);
|
|
||||||
_scopeFormatter = new DefaultScopeFormatter(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a custom formatting for rendering log messages to xUnit test output.
|
|
||||||
/// </summary>
|
|
||||||
public ILogFormatter Formatter
|
|
||||||
{
|
|
||||||
get => _formatter;
|
|
||||||
set =>
|
|
||||||
_formatter = value ?? throw new ArgumentNullException(nameof(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets whether exceptions thrown while logging outside of the test execution will be ignored.
|
|
||||||
/// </summary>
|
|
||||||
public bool IgnoreTestBoundaryException { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the minimum logging level.
|
|
||||||
/// </summary>
|
|
||||||
public LogLevel LogLevel { get; set; } = LogLevel.Trace;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a custom formatting for rendering scope beginning and end messages to xUnit test output.
|
|
||||||
/// </summary>
|
|
||||||
public ILogFormatter ScopeFormatter
|
|
||||||
{
|
|
||||||
get => _scopeFormatter;
|
|
||||||
set =>
|
|
||||||
_scopeFormatter = value ?? throw new ArgumentNullException(nameof(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Identifies the number of spaces to use for indenting scopes.
|
|
||||||
/// </summary>
|
|
||||||
public int ScopePaddingSpaces { get; set; } = 3;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the set of sensitive values that should be filtered out when writing log messages.
|
|
||||||
/// </summary>
|
|
||||||
public Collection<string> SensitiveValues { get; } = new();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
namespace Divergic.Logging.Xunit
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
using global::Xunit.Abstractions;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="LoggingTestsBase" />
|
|
||||||
/// class is used to provide a simple logging bootstrap class for xUnit test classes.
|
|
||||||
/// </summary>
|
|
||||||
public abstract class LoggingTestsBase : IDisposable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="LoggingTestsBase" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="output">The xUnit test output.</param>
|
|
||||||
/// <param name="logLevel">The minimum log level to output.</param>
|
|
||||||
protected LoggingTestsBase(ITestOutputHelper output, LogLevel logLevel)
|
|
||||||
{
|
|
||||||
Output = output ?? throw new ArgumentNullException(nameof(output));
|
|
||||||
Logger = output.BuildLogger(logLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="LoggingTestsBase" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="output">The xUnit test output.</param>
|
|
||||||
/// <param name="config">Optional logging configuration.</param>
|
|
||||||
protected LoggingTestsBase(ITestOutputHelper output, LoggingConfig? config = null)
|
|
||||||
{
|
|
||||||
Output = output ?? throw new ArgumentNullException(nameof(output));
|
|
||||||
Logger = output.BuildLogger(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Disposes resources held by this instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="disposing"><c>true</c> if disposing unmanaged types; otherwise <c>false</c>.</param>
|
|
||||||
protected virtual void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
Logger.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the logger instance.
|
|
||||||
/// </summary>
|
|
||||||
protected ICacheLogger Logger { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the xUnit test output.
|
|
||||||
/// </summary>
|
|
||||||
protected ITestOutputHelper Output { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
namespace Divergic.Logging.Xunit
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
using global::Xunit.Abstractions;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="LoggingTestsBase{T}" />
|
|
||||||
/// class is used to provide a simple logging bootstrap class for xUnit test classes.
|
|
||||||
/// </summary>
|
|
||||||
public abstract class LoggingTestsBase<T> : IDisposable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="LoggingTestsBase{T}" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="output">The xUnit test output.</param>
|
|
||||||
/// <param name="logLevel">The minimum log level to output.</param>
|
|
||||||
protected LoggingTestsBase(ITestOutputHelper output, LogLevel logLevel)
|
|
||||||
{
|
|
||||||
Output = output ?? throw new ArgumentNullException(nameof(output));
|
|
||||||
Logger = output.BuildLoggerFor<T>(logLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="LoggingTestsBase{T}" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="output">The xUnit test output.</param>
|
|
||||||
/// <param name="config">Optional logging configuration.</param>
|
|
||||||
protected LoggingTestsBase(ITestOutputHelper output, LoggingConfig? config = null)
|
|
||||||
{
|
|
||||||
Output = output ?? throw new ArgumentNullException(nameof(output));
|
|
||||||
Logger = output.BuildLoggerFor<T>(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Disposes resources held by this instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="disposing"><c>true</c> if disposing unmanaged types; otherwise <c>false</c>.</param>
|
|
||||||
protected virtual void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
Logger.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the logger instance.
|
|
||||||
/// </summary>
|
|
||||||
protected ICacheLogger<T> Logger { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the xUnit test output.
|
|
||||||
/// </summary>
|
|
||||||
protected ITestOutputHelper Output { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
namespace Divergic.Logging.Xunit
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
|
|
||||||
internal class NoopDisposable : IDisposable
|
|
||||||
{
|
|
||||||
public static readonly NoopDisposable Instance = new NoopDisposable();
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
// No-op
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
namespace Divergic.Logging.Xunit
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Text.Json;
|
|
||||||
using global::Xunit.Abstractions;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
internal class ScopeWriter : IDisposable
|
|
||||||
{
|
|
||||||
private readonly string _category;
|
|
||||||
private readonly LoggingConfig _config;
|
|
||||||
private readonly int _depth;
|
|
||||||
private readonly Action _onScopeEnd;
|
|
||||||
private readonly ITestOutputHelper _output;
|
|
||||||
private readonly object? _state;
|
|
||||||
private string _scopeMessage = string.Empty;
|
|
||||||
private string _structuredStateData = string.Empty;
|
|
||||||
|
|
||||||
public ScopeWriter(
|
|
||||||
ITestOutputHelper output,
|
|
||||||
object? state,
|
|
||||||
int depth,
|
|
||||||
string category,
|
|
||||||
Action onScopeEnd,
|
|
||||||
LoggingConfig config)
|
|
||||||
{
|
|
||||||
_output = output;
|
|
||||||
_state = state;
|
|
||||||
_depth = depth;
|
|
||||||
_category = category;
|
|
||||||
_onScopeEnd = onScopeEnd;
|
|
||||||
_config = config;
|
|
||||||
|
|
||||||
DetermineScopeStateMessage();
|
|
||||||
|
|
||||||
var scopeStartMessage = BuildScopeStateMessage(false);
|
|
||||||
|
|
||||||
WriteLog(_depth, scopeStartMessage);
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(_structuredStateData) == false)
|
|
||||||
{
|
|
||||||
// Add the padding to the structured data
|
|
||||||
var structuredLines =
|
|
||||||
_structuredStateData.Split(new[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
|
|
||||||
WriteLog(_depth + 1, "Scope data: ");
|
|
||||||
|
|
||||||
foreach (var structuredLine in structuredLines)
|
|
||||||
{
|
|
||||||
WriteLog(_depth + 1, structuredLine);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WriteLog(int depth, string message)
|
|
||||||
{
|
|
||||||
var formattedMessage = _config.ScopeFormatter.Format(depth, _category, LogLevel.Information, 0, message, null);
|
|
||||||
|
|
||||||
_output.WriteLine(formattedMessage);
|
|
||||||
|
|
||||||
// Write the message to the output window
|
|
||||||
Trace.WriteLine(formattedMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
var scopeStartMessage = BuildScopeStateMessage(true);
|
|
||||||
|
|
||||||
_output.WriteLine(scopeStartMessage);
|
|
||||||
|
|
||||||
_onScopeEnd.Invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
private string BuildScopeStateMessage(bool isScopeEnd)
|
|
||||||
{
|
|
||||||
var endScopeMarker = isScopeEnd ? "/" : string.Empty;
|
|
||||||
const string format = "<{0}{1}>";
|
|
||||||
|
|
||||||
var message = string.Format(CultureInfo.InvariantCulture, format, endScopeMarker, _scopeMessage);
|
|
||||||
|
|
||||||
var formattedMessage =
|
|
||||||
_config.ScopeFormatter.Format(_depth, _category, LogLevel.Information, 0, message, null);
|
|
||||||
|
|
||||||
return formattedMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DetermineScopeStateMessage()
|
|
||||||
{
|
|
||||||
const string scopeMarker = "Scope: ";
|
|
||||||
var defaultScopeMessage = "Scope " + (_depth + 1);
|
|
||||||
|
|
||||||
if (_state == null)
|
|
||||||
{
|
|
||||||
_scopeMessage = defaultScopeMessage;
|
|
||||||
}
|
|
||||||
else if (_state is string state)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(state))
|
|
||||||
{
|
|
||||||
_scopeMessage = defaultScopeMessage;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_scopeMessage = scopeMarker + state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (_state.GetType().IsValueType)
|
|
||||||
{
|
|
||||||
_scopeMessage = scopeMarker + _state;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// The data is probably a complex object or a structured log entry
|
|
||||||
_structuredStateData = JsonSerializer.Serialize(_state, SerializerSettings.Default);
|
|
||||||
}
|
|
||||||
catch (JsonException ex)
|
|
||||||
{
|
|
||||||
_structuredStateData = ex.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
_scopeMessage = defaultScopeMessage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
namespace Divergic.Logging.Xunit
|
|
||||||
{
|
|
||||||
using System.Text.Json;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="SerializerSettings" />
|
|
||||||
/// class provides access to settings for JSON serialization.
|
|
||||||
/// </summary>
|
|
||||||
public static class SerializerSettings
|
|
||||||
{
|
|
||||||
private static JsonSerializerOptions BuildSerializerSettings()
|
|
||||||
{
|
|
||||||
var settings = new JsonSerializerOptions
|
|
||||||
{
|
|
||||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
|
||||||
WriteIndented = true
|
|
||||||
};
|
|
||||||
|
|
||||||
return settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the default serializer settings.
|
|
||||||
/// </summary>
|
|
||||||
public static JsonSerializerOptions Default { get; } = BuildSerializerSettings();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,183 +0,0 @@
|
|||||||
// ReSharper disable once CheckNamespace
|
|
||||||
namespace Xunit.Abstractions
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Divergic.Logging.Xunit;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="TestOutputHelperExtensions" /> class provides extension methods for the
|
|
||||||
/// <see cref="ITestOutputHelper" />.
|
|
||||||
/// </summary>
|
|
||||||
public static class TestOutputHelperExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Builds a logger from the specified test output helper.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="output">The test output helper.</param>
|
|
||||||
/// <param name="memberName">
|
|
||||||
/// The member to create the logger for. This is automatically populated using <see cref="CallerMemberNameAttribute" />
|
|
||||||
/// .
|
|
||||||
/// </param>
|
|
||||||
/// <returns>The logger.</returns>
|
|
||||||
/// <exception cref="ArgumentNullException">The <paramref name="output" /> is <c>null</c>.</exception>
|
|
||||||
public static ICacheLogger BuildLogger(
|
|
||||||
this ITestOutputHelper output,
|
|
||||||
[CallerMemberName] string memberName = "")
|
|
||||||
{
|
|
||||||
return BuildLogger(output, null, memberName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Builds a logger from the specified test output helper.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="output">The test output helper.</param>
|
|
||||||
/// <param name="logLevel">The minimum log level to output.</param>
|
|
||||||
/// <param name="memberName">
|
|
||||||
/// The member to create the logger for. This is automatically populated using <see cref="CallerMemberNameAttribute" />
|
|
||||||
/// .
|
|
||||||
/// </param>
|
|
||||||
/// <returns>The logger.</returns>
|
|
||||||
/// <exception cref="ArgumentNullException">The <paramref name="output" /> is <c>null</c>.</exception>
|
|
||||||
public static ICacheLogger BuildLogger(
|
|
||||||
this ITestOutputHelper output,
|
|
||||||
LogLevel logLevel,
|
|
||||||
[CallerMemberName] string memberName = "")
|
|
||||||
{
|
|
||||||
var config = new LoggingConfig
|
|
||||||
{
|
|
||||||
LogLevel = logLevel
|
|
||||||
};
|
|
||||||
|
|
||||||
return BuildLogger(output, config, memberName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Builds a logger from the specified test output helper.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="output">The test output helper.</param>
|
|
||||||
/// <param name="config">Optional logging configuration.</param>
|
|
||||||
/// <param name="memberName">
|
|
||||||
/// The member to create the logger for. This is automatically populated using <see cref="CallerMemberNameAttribute" />
|
|
||||||
/// .
|
|
||||||
/// </param>
|
|
||||||
/// <returns>The logger.</returns>
|
|
||||||
/// <exception cref="ArgumentNullException">The <paramref name="output" /> is <c>null</c>.</exception>
|
|
||||||
public static ICacheLogger BuildLogger(
|
|
||||||
this ITestOutputHelper output,
|
|
||||||
LoggingConfig? config,
|
|
||||||
[CallerMemberName] string memberName = "")
|
|
||||||
{
|
|
||||||
output = output ?? throw new ArgumentNullException(nameof(output));
|
|
||||||
|
|
||||||
// Do not use the using keyword here because the factory will be disposed before the cache logger is finished with it
|
|
||||||
var factory = LogFactory.Create(output, config);
|
|
||||||
|
|
||||||
var logger = factory.CreateLogger(memberName);
|
|
||||||
|
|
||||||
return logger.WithCache(factory);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Builds a logger from the specified test output helper for the specified type.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type to create the logger for.</typeparam>
|
|
||||||
/// <param name="output">The test output helper.</param>
|
|
||||||
/// <returns>The logger.</returns>
|
|
||||||
/// <exception cref="ArgumentNullException">The <paramref name="output" /> is <c>null</c>.</exception>
|
|
||||||
public static ICacheLogger<T> BuildLoggerFor<T>(this ITestOutputHelper output)
|
|
||||||
{
|
|
||||||
return BuildLoggerFor<T>(output, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Builds a logger from the specified test output helper for the specified type.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type to create the logger for.</typeparam>
|
|
||||||
/// <param name="output">The test output helper.</param>
|
|
||||||
/// <param name="logLevel">The minimum log level to output.</param>
|
|
||||||
/// <returns>The logger.</returns>
|
|
||||||
/// <exception cref="ArgumentNullException">The <paramref name="output" /> is <c>null</c>.</exception>
|
|
||||||
public static ICacheLogger<T> BuildLoggerFor<T>(this ITestOutputHelper output, LogLevel logLevel)
|
|
||||||
{
|
|
||||||
var config = new LoggingConfig
|
|
||||||
{
|
|
||||||
LogLevel = logLevel
|
|
||||||
};
|
|
||||||
|
|
||||||
return BuildLoggerFor<T>(output, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Builds a logger from the specified test output helper for the specified type.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type to create the logger for.</typeparam>
|
|
||||||
/// <param name="output">The test output helper.</param>
|
|
||||||
/// <param name="config">Optional logging configuration.</param>
|
|
||||||
/// <returns>The logger.</returns>
|
|
||||||
/// <exception cref="ArgumentNullException">The <paramref name="output" /> is <c>null</c>.</exception>
|
|
||||||
public static ICacheLogger<T> BuildLoggerFor<T>(this ITestOutputHelper output, LoggingConfig? config)
|
|
||||||
{
|
|
||||||
output = output ?? throw new ArgumentNullException(nameof(output));
|
|
||||||
|
|
||||||
// Do not use the using keyword here because the factory will be disposed before the cache logger is finished with it
|
|
||||||
var factory = LogFactory.Create(output, config);
|
|
||||||
|
|
||||||
var logger = factory.CreateLogger<T>();
|
|
||||||
|
|
||||||
return logger.WithCache(factory);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Builds a logger factory from the specified test output helper.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="output">The test output helper.</param>
|
|
||||||
/// <returns>The logger factory.</returns>
|
|
||||||
/// <exception cref="ArgumentNullException">The <paramref name="output" /> is <c>null</c>.</exception>
|
|
||||||
public static ILoggerFactory BuildLoggerFactory(
|
|
||||||
this ITestOutputHelper output)
|
|
||||||
{
|
|
||||||
return BuildLoggerFactory(output, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Builds a logger factory from the specified test output helper.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="output">The test output helper.</param>
|
|
||||||
/// <param name="logLevel">The minimum log level to output.</param>
|
|
||||||
/// <returns>The logger factory.</returns>
|
|
||||||
/// <exception cref="ArgumentNullException">The <paramref name="output" /> is <c>null</c>.</exception>
|
|
||||||
public static ILoggerFactory BuildLoggerFactory(
|
|
||||||
this ITestOutputHelper output,
|
|
||||||
LogLevel logLevel)
|
|
||||||
{
|
|
||||||
var config = new LoggingConfig
|
|
||||||
{
|
|
||||||
LogLevel = logLevel
|
|
||||||
};
|
|
||||||
|
|
||||||
return BuildLoggerFactory(output, config);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Builds a logger factory from the specified test output helper.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="output">The test output helper.</param>
|
|
||||||
/// <param name="config">Optional logging configuration.</param>
|
|
||||||
/// <returns>The logger factory.</returns>
|
|
||||||
/// <exception cref="ArgumentNullException">The <paramref name="output" /> is <c>null</c>.</exception>
|
|
||||||
public static ILoggerFactory BuildLoggerFactory(
|
|
||||||
this ITestOutputHelper output,
|
|
||||||
LoggingConfig? config)
|
|
||||||
{
|
|
||||||
output = output ?? throw new ArgumentNullException(nameof(output));
|
|
||||||
|
|
||||||
// Do not use the using keyword here because the factory will be disposed before the cache logger is finished with it
|
|
||||||
var factory = LogFactory.Create(output, config);
|
|
||||||
|
|
||||||
return factory;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
namespace Divergic.Logging.Xunit
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Threading;
|
|
||||||
using global::Xunit.Abstractions;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="TestOutputLogger" />
|
|
||||||
/// class is used to provide logging implementation for Xunit.
|
|
||||||
/// </summary>
|
|
||||||
public class TestOutputLogger : FilterLogger
|
|
||||||
{
|
|
||||||
private static readonly AsyncLocal<ConcurrentStack<ScopeWriter>> _scopes =
|
|
||||||
new AsyncLocal<ConcurrentStack<ScopeWriter>>();
|
|
||||||
|
|
||||||
private readonly LoggingConfig _config;
|
|
||||||
private readonly string _categoryName;
|
|
||||||
private readonly ITestOutputHelper _output;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new instance of the <see cref="TestOutputLogger" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="categoryName">The category name of the logger.</param>
|
|
||||||
/// <param name="output">The test output helper.</param>
|
|
||||||
/// <param name="config">Optional logging configuration.</param>
|
|
||||||
/// <exception cref="ArgumentException">The <paramref name="categoryName" /> is <c>null</c>, empty or whitespace.</exception>
|
|
||||||
/// <exception cref="ArgumentNullException">The <paramref name="output" /> is <c>null</c>.</exception>
|
|
||||||
public TestOutputLogger(string categoryName, ITestOutputHelper output, LoggingConfig? config = null)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(categoryName))
|
|
||||||
{
|
|
||||||
throw new ArgumentException("No name value has been supplied", nameof(categoryName));
|
|
||||||
}
|
|
||||||
|
|
||||||
_categoryName = categoryName;
|
|
||||||
_output = output ?? throw new ArgumentNullException(nameof(output));
|
|
||||||
_config = config ?? new LoggingConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override IDisposable BeginScope<TState>(TState state)
|
|
||||||
{
|
|
||||||
var scopeWriter = new ScopeWriter(_output, state, Scopes.Count, _categoryName, () => Scopes.TryPop(out _), _config);
|
|
||||||
|
|
||||||
Scopes.Push(scopeWriter);
|
|
||||||
|
|
||||||
return scopeWriter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override bool IsEnabled(LogLevel logLevel)
|
|
||||||
{
|
|
||||||
if (logLevel == LogLevel.None)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return logLevel >= _config.LogLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void WriteLogEntry<TState>(
|
|
||||||
LogLevel logLevel,
|
|
||||||
EventId eventId,
|
|
||||||
TState state,
|
|
||||||
string message,
|
|
||||||
Exception? exception,
|
|
||||||
Func<TState, Exception?, string> formatter)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
WriteLog(logLevel, eventId, message, exception);
|
|
||||||
}
|
|
||||||
catch (InvalidOperationException)
|
|
||||||
{
|
|
||||||
if (_config.IgnoreTestBoundaryException == false)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WriteLog(LogLevel logLevel, EventId eventId, string message, Exception? exception)
|
|
||||||
{
|
|
||||||
var formattedMessage = _config.Formatter.Format(Scopes.Count, _categoryName, logLevel, eventId, message, exception);
|
|
||||||
|
|
||||||
_output.WriteLine(formattedMessage);
|
|
||||||
|
|
||||||
// Write the message to the output window
|
|
||||||
Trace.WriteLine(formattedMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ConcurrentStack<ScopeWriter> Scopes
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var scopes = _scopes.Value;
|
|
||||||
|
|
||||||
if (scopes == null)
|
|
||||||
{
|
|
||||||
scopes = new ConcurrentStack<ScopeWriter>();
|
|
||||||
|
|
||||||
_scopes.Value = scopes;
|
|
||||||
}
|
|
||||||
|
|
||||||
return scopes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
namespace Divergic.Logging.Xunit
|
|
||||||
{
|
|
||||||
using System;
|
|
||||||
using global::Xunit.Abstractions;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="TestOutputLoggerProvider" /> class is used to provide Xunit logging to <see cref="ILoggerFactory" />
|
|
||||||
/// .
|
|
||||||
/// </summary>
|
|
||||||
public sealed class TestOutputLoggerProvider : ILoggerProvider
|
|
||||||
{
|
|
||||||
private readonly LoggingConfig? _config;
|
|
||||||
private readonly ITestOutputHelper _output;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="TestOutputLoggerProvider" /> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="output">The test output helper.</param>
|
|
||||||
/// <param name="config">Optional logging configuration.</param>
|
|
||||||
/// <exception cref="ArgumentNullException">The <paramref name="output" /> is <c>null</c>.</exception>
|
|
||||||
public TestOutputLoggerProvider(ITestOutputHelper output, LoggingConfig? config = null)
|
|
||||||
{
|
|
||||||
_output = output ?? throw new ArgumentNullException(nameof(output));
|
|
||||||
_config = config;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
/// <exception cref="ArgumentException">The <paramref name="categoryName" /> is <c>null</c>, empty or whitespace.</exception>
|
|
||||||
public ILogger CreateLogger(string categoryName)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(categoryName))
|
|
||||||
{
|
|
||||||
throw new ArgumentException("No categoryName value has been supplied", nameof(categoryName));
|
|
||||||
}
|
|
||||||
|
|
||||||
return new TestOutputLogger(categoryName, _output, _config);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using Divergic.Logging.Xunit;
|
using Neovolve.Logging.Xunit;
|
||||||
using Velopack.Packaging.Exceptions;
|
using Velopack.Packaging.Exceptions;
|
||||||
using Velopack.Packaging.Windows;
|
using Velopack.Packaging.Windows;
|
||||||
using Velopack.Vpk;
|
using Velopack.Vpk;
|
||||||
|
|||||||
Reference in New Issue
Block a user