diff --git a/src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj b/src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj index 12a85dfa..a9f3c2b3 100644 --- a/src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj +++ b/src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj @@ -21,6 +21,9 @@ true + + + @@ -36,24 +39,4 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/src/Logging.XUnit/MartinCostello.Logging.XUnit.csproj b/src/Logging.XUnit/MartinCostello.Logging.XUnit.csproj index 7952a092..b9952979 100644 --- a/src/Logging.XUnit/MartinCostello.Logging.XUnit.csproj +++ b/src/Logging.XUnit/MartinCostello.Logging.XUnit.csproj @@ -16,6 +16,9 @@ true + + + diff --git a/src/Logging.XUnit/AmbientTestOutputHelperAccessor.cs b/src/Shared/AmbientTestOutputHelperAccessor.cs similarity index 97% rename from src/Logging.XUnit/AmbientTestOutputHelperAccessor.cs rename to src/Shared/AmbientTestOutputHelperAccessor.cs index fefe038d..17f1683b 100644 --- a/src/Logging.XUnit/AmbientTestOutputHelperAccessor.cs +++ b/src/Shared/AmbientTestOutputHelperAccessor.cs @@ -1,25 +1,25 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -namespace MartinCostello.Logging.XUnit; - -/// -/// A class representing an implementation of that -/// stores the as an asynchronous local value. This class cannot be inherited. -/// -internal sealed class AmbientTestOutputHelperAccessor : ITestOutputHelperAccessor -{ - /// - /// A backing field for the for the current thread. - /// - private static readonly AsyncLocal _current = new(); - - /// - /// Gets or sets the current . - /// - public ITestOutputHelper? OutputHelper - { - get { return _current.Value; } - set { _current.Value = value; } - } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +namespace MartinCostello.Logging.XUnit; + +/// +/// A class representing an implementation of that +/// stores the as an asynchronous local value. This class cannot be inherited. +/// +internal sealed class AmbientTestOutputHelperAccessor : ITestOutputHelperAccessor +{ + /// + /// A backing field for the for the current thread. + /// + private static readonly AsyncLocal _current = new(); + + /// + /// Gets or sets the current . + /// + public ITestOutputHelper? OutputHelper + { + get { return _current.Value; } + set { _current.Value = value; } + } +} diff --git a/src/Logging.XUnit/IMessageSinkAccessor.cs b/src/Shared/IMessageSinkAccessor.cs similarity index 97% rename from src/Logging.XUnit/IMessageSinkAccessor.cs rename to src/Shared/IMessageSinkAccessor.cs index 8971d230..93867bac 100644 --- a/src/Logging.XUnit/IMessageSinkAccessor.cs +++ b/src/Shared/IMessageSinkAccessor.cs @@ -1,15 +1,15 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -namespace MartinCostello.Logging.XUnit; - -/// -/// Defines a property for accessing an . -/// -public interface IMessageSinkAccessor -{ - /// - /// Gets or sets the to use. - /// - IMessageSink? MessageSink { get; set; } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +namespace MartinCostello.Logging.XUnit; + +/// +/// Defines a property for accessing an . +/// +public interface IMessageSinkAccessor +{ + /// + /// Gets or sets the to use. + /// + IMessageSink? MessageSink { get; set; } +} diff --git a/src/Logging.XUnit/IMessageSinkExtensions.cs b/src/Shared/IMessageSinkExtensions.cs similarity index 97% rename from src/Logging.XUnit/IMessageSinkExtensions.cs rename to src/Shared/IMessageSinkExtensions.cs index 0b3f0d88..08217734 100644 --- a/src/Logging.XUnit/IMessageSinkExtensions.cs +++ b/src/Shared/IMessageSinkExtensions.cs @@ -1,57 +1,57 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using System.ComponentModel; -using Microsoft.Extensions.Logging; - -#if XUNIT_V3 -namespace Xunit; -#else -#pragma warning disable IDE0130 -namespace Xunit.Abstractions; -#endif - -/// -/// A class containing extension methods for the interface. This class cannot be inherited. -/// -[EditorBrowsable(EditorBrowsableState.Never)] -public static class IMessageSinkExtensions -{ - /// - /// Returns an that logs to the message sink. - /// - /// The to create the logger factory from. - /// - /// An that writes messages to the message sink. - /// - /// - /// is . - /// - public static ILoggerFactory ToLoggerFactory(this IMessageSink messageSink) - { -#if NET - ArgumentNullException.ThrowIfNull(messageSink); -#else - if (messageSink == null) - { - throw new ArgumentNullException(nameof(messageSink)); - } -#endif - - return new LoggerFactory().AddXUnit(messageSink); - } - - /// - /// Returns an that logs to the message sink. - /// - /// The type of the logger to create. - /// The to create the logger from. - /// - /// An that writes messages to the message sink. - /// - /// - /// is . - /// - public static ILogger ToLogger(this IMessageSink messageSink) - => messageSink.ToLoggerFactory().CreateLogger(); -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using System.ComponentModel; +using Microsoft.Extensions.Logging; + +#if XUNIT_V3 +namespace Xunit; +#else +#pragma warning disable IDE0130 +namespace Xunit.Abstractions; +#endif + +/// +/// A class containing extension methods for the interface. This class cannot be inherited. +/// +[EditorBrowsable(EditorBrowsableState.Never)] +public static class IMessageSinkExtensions +{ + /// + /// Returns an that logs to the message sink. + /// + /// The to create the logger factory from. + /// + /// An that writes messages to the message sink. + /// + /// + /// is . + /// + public static ILoggerFactory ToLoggerFactory(this IMessageSink messageSink) + { +#if NET + ArgumentNullException.ThrowIfNull(messageSink); +#else + if (messageSink == null) + { + throw new ArgumentNullException(nameof(messageSink)); + } +#endif + + return new LoggerFactory().AddXUnit(messageSink); + } + + /// + /// Returns an that logs to the message sink. + /// + /// The type of the logger to create. + /// The to create the logger from. + /// + /// An that writes messages to the message sink. + /// + /// + /// is . + /// + public static ILogger ToLogger(this IMessageSink messageSink) + => messageSink.ToLoggerFactory().CreateLogger(); +} diff --git a/src/Logging.XUnit/ITestOutputHelperAccessor.cs b/src/Shared/ITestOutputHelperAccessor.cs similarity index 100% rename from src/Logging.XUnit/ITestOutputHelperAccessor.cs rename to src/Shared/ITestOutputHelperAccessor.cs diff --git a/src/Logging.XUnit/ITestOutputHelperExtensions.cs b/src/Shared/ITestOutputHelperExtensions.cs similarity index 100% rename from src/Logging.XUnit/ITestOutputHelperExtensions.cs rename to src/Shared/ITestOutputHelperExtensions.cs diff --git a/src/Logging.XUnit/MessageSinkAccessor.cs b/src/Shared/MessageSinkAccessor.cs similarity index 97% rename from src/Logging.XUnit/MessageSinkAccessor.cs rename to src/Shared/MessageSinkAccessor.cs index 65bc2b8f..e8755484 100644 --- a/src/Logging.XUnit/MessageSinkAccessor.cs +++ b/src/Shared/MessageSinkAccessor.cs @@ -1,22 +1,22 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -namespace MartinCostello.Logging.XUnit; - -/// -/// A class representing the default implementation of . This class cannot be inherited. -/// -/// -/// Initializes a new instance of the class. -/// -/// The to use. -/// -/// is . -/// -internal sealed class MessageSinkAccessor(IMessageSink messageSink) : IMessageSinkAccessor -{ - /// - /// Gets or sets the current . - /// - public IMessageSink? MessageSink { get; set; } = messageSink ?? throw new ArgumentNullException(nameof(messageSink)); -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +namespace MartinCostello.Logging.XUnit; + +/// +/// A class representing the default implementation of . This class cannot be inherited. +/// +/// +/// Initializes a new instance of the class. +/// +/// The to use. +/// +/// is . +/// +internal sealed class MessageSinkAccessor(IMessageSink messageSink) : IMessageSinkAccessor +{ + /// + /// Gets or sets the current . + /// + public IMessageSink? MessageSink { get; set; } = messageSink ?? throw new ArgumentNullException(nameof(messageSink)); +} diff --git a/src/Logging.XUnit/StringSyntaxAttribute.cs b/src/Shared/StringSyntaxAttribute.cs similarity index 100% rename from src/Logging.XUnit/StringSyntaxAttribute.cs rename to src/Shared/StringSyntaxAttribute.cs diff --git a/src/Logging.XUnit/TestOutputHelperAccessor.cs b/src/Shared/TestOutputHelperAccessor.cs similarity index 100% rename from src/Logging.XUnit/TestOutputHelperAccessor.cs rename to src/Shared/TestOutputHelperAccessor.cs diff --git a/src/Logging.XUnit/XUnitLogScope.cs b/src/Shared/XUnitLogScope.cs similarity index 96% rename from src/Logging.XUnit/XUnitLogScope.cs rename to src/Shared/XUnitLogScope.cs index f7c00f8a..7233ca13 100644 --- a/src/Logging.XUnit/XUnitLogScope.cs +++ b/src/Shared/XUnitLogScope.cs @@ -1,73 +1,73 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -namespace MartinCostello.Logging.XUnit; - -/// -/// A class representing a scope for logging. This class cannot be inherited. -/// -/// -/// Initializes a new instance of the class. -/// -/// The state object for the scope. -internal sealed class XUnitLogScope(object state) -{ - /// - /// The scope for the current thread. - /// - private static readonly AsyncLocal _value = new(); - - /// - /// Gets the state object for the scope. - /// - public object State { get; } = state; - - /// - /// Gets or sets the current scope. - /// - internal static XUnitLogScope? Current - { - get { return _value.Value; } - set { _value.Value = value; } - } - - /// - /// Gets the parent scope. - /// - internal XUnitLogScope? Parent { get; private set; } - - /// - public override string? ToString() - => State.ToString(); - - /// - /// Pushes a new value into the scope. - /// - /// The state object for the scope. - /// - /// An that pops the scope. - /// - internal static IDisposable Push(object state) - { - var temp = Current; - - Current = new XUnitLogScope(state) - { - Parent = temp, - }; - - return new DisposableScope(); - } - - /// - /// A class the disposes of the current scope. This class cannot be inherited. - /// - private sealed class DisposableScope : IDisposable - { - /// - public void Dispose() - { - Current = Current?.Parent; - } - } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +namespace MartinCostello.Logging.XUnit; + +/// +/// A class representing a scope for logging. This class cannot be inherited. +/// +/// +/// Initializes a new instance of the class. +/// +/// The state object for the scope. +internal sealed class XUnitLogScope(object state) +{ + /// + /// The scope for the current thread. + /// + private static readonly AsyncLocal _value = new(); + + /// + /// Gets the state object for the scope. + /// + public object State { get; } = state; + + /// + /// Gets or sets the current scope. + /// + internal static XUnitLogScope? Current + { + get { return _value.Value; } + set { _value.Value = value; } + } + + /// + /// Gets the parent scope. + /// + internal XUnitLogScope? Parent { get; private set; } + + /// + public override string? ToString() + => State.ToString(); + + /// + /// Pushes a new value into the scope. + /// + /// The state object for the scope. + /// + /// An that pops the scope. + /// + internal static IDisposable Push(object state) + { + var temp = Current; + + Current = new XUnitLogScope(state) + { + Parent = temp, + }; + + return new DisposableScope(); + } + + /// + /// A class the disposes of the current scope. This class cannot be inherited. + /// + private sealed class DisposableScope : IDisposable + { + /// + public void Dispose() + { + Current = Current?.Parent; + } + } +} diff --git a/src/Logging.XUnit/XUnitLogger.IMessageSink.cs b/src/Shared/XUnitLogger.IMessageSink.cs similarity index 97% rename from src/Logging.XUnit/XUnitLogger.IMessageSink.cs rename to src/Shared/XUnitLogger.IMessageSink.cs index 1471b532..4d2efed0 100644 --- a/src/Logging.XUnit/XUnitLogger.IMessageSink.cs +++ b/src/Shared/XUnitLogger.IMessageSink.cs @@ -1,63 +1,63 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using Microsoft.Extensions.Logging; - -namespace MartinCostello.Logging.XUnit; - -/// -/// A class representing an to use with xunit. -/// -public partial class XUnitLogger -{ - /// - /// The to use. This field is read-only. - /// - private readonly IMessageSinkAccessor? _messageSinkAccessor; - - /// - /// Gets or sets the message sink message factory to use when writing to an . - /// - private Func _messageSinkMessageFactory; - - /// - /// Initializes a new instance of the class. - /// - /// The name for messages produced by the logger. - /// The to use. - /// The to use. - /// - /// or is . - /// - public XUnitLogger(string name, IMessageSink messageSink, XUnitLoggerOptions? options) - : this(name, new MessageSinkAccessor(messageSink), options) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The name for messages produced by the logger. - /// The to use. - /// The to use. - /// - /// or is . - /// - public XUnitLogger(string name, IMessageSinkAccessor accessor, XUnitLoggerOptions? options) - : this(name, options) - { - _messageSinkAccessor = accessor ?? throw new ArgumentNullException(nameof(accessor)); - } - - /// - /// Gets or sets the message sink message factory to use when writing to an . - /// - /// - /// is . - /// - public Func MessageSinkMessageFactory - { - get { return _messageSinkMessageFactory; } - set { _messageSinkMessageFactory = value ?? throw new ArgumentNullException(nameof(value)); } - } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using Microsoft.Extensions.Logging; + +namespace MartinCostello.Logging.XUnit; + +/// +/// A class representing an to use with xunit. +/// +public partial class XUnitLogger +{ + /// + /// The to use. This field is read-only. + /// + private readonly IMessageSinkAccessor? _messageSinkAccessor; + + /// + /// Gets or sets the message sink message factory to use when writing to an . + /// + private Func _messageSinkMessageFactory; + + /// + /// Initializes a new instance of the class. + /// + /// The name for messages produced by the logger. + /// The to use. + /// The to use. + /// + /// or is . + /// + public XUnitLogger(string name, IMessageSink messageSink, XUnitLoggerOptions? options) + : this(name, new MessageSinkAccessor(messageSink), options) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The name for messages produced by the logger. + /// The to use. + /// The to use. + /// + /// or is . + /// + public XUnitLogger(string name, IMessageSinkAccessor accessor, XUnitLoggerOptions? options) + : this(name, options) + { + _messageSinkAccessor = accessor ?? throw new ArgumentNullException(nameof(accessor)); + } + + /// + /// Gets or sets the message sink message factory to use when writing to an . + /// + /// + /// is . + /// + public Func MessageSinkMessageFactory + { + get { return _messageSinkMessageFactory; } + set { _messageSinkMessageFactory = value ?? throw new ArgumentNullException(nameof(value)); } + } +} diff --git a/src/Logging.XUnit/XUnitLogger.ITestOutputHelper.cs b/src/Shared/XUnitLogger.ITestOutputHelper.cs similarity index 97% rename from src/Logging.XUnit/XUnitLogger.ITestOutputHelper.cs rename to src/Shared/XUnitLogger.ITestOutputHelper.cs index b11505e8..60675af6 100644 --- a/src/Logging.XUnit/XUnitLogger.ITestOutputHelper.cs +++ b/src/Shared/XUnitLogger.ITestOutputHelper.cs @@ -1,46 +1,46 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using Microsoft.Extensions.Logging; - -namespace MartinCostello.Logging.XUnit; - -/// -/// A class representing an to use with xunit. -/// -public partial class XUnitLogger -{ - /// - /// The to use. This field is read-only. - /// - private readonly ITestOutputHelperAccessor? _outputHelperAccessor; - - /// - /// Initializes a new instance of the class. - /// - /// The name for messages produced by the logger. - /// The to use. - /// The to use. - /// - /// or is . - /// - public XUnitLogger(string name, ITestOutputHelper outputHelper, XUnitLoggerOptions? options) - : this(name, new TestOutputHelperAccessor(outputHelper), options) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The name for messages produced by the logger. - /// The to use. - /// The to use. - /// - /// or is . - /// - public XUnitLogger(string name, ITestOutputHelperAccessor accessor, XUnitLoggerOptions? options) - : this(name, options) - { - _outputHelperAccessor = accessor ?? throw new ArgumentNullException(nameof(accessor)); - } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using Microsoft.Extensions.Logging; + +namespace MartinCostello.Logging.XUnit; + +/// +/// A class representing an to use with xunit. +/// +public partial class XUnitLogger +{ + /// + /// The to use. This field is read-only. + /// + private readonly ITestOutputHelperAccessor? _outputHelperAccessor; + + /// + /// Initializes a new instance of the class. + /// + /// The name for messages produced by the logger. + /// The to use. + /// The to use. + /// + /// or is . + /// + public XUnitLogger(string name, ITestOutputHelper outputHelper, XUnitLoggerOptions? options) + : this(name, new TestOutputHelperAccessor(outputHelper), options) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The name for messages produced by the logger. + /// The to use. + /// The to use. + /// + /// or is . + /// + public XUnitLogger(string name, ITestOutputHelperAccessor accessor, XUnitLoggerOptions? options) + : this(name, options) + { + _outputHelperAccessor = accessor ?? throw new ArgumentNullException(nameof(accessor)); + } +} diff --git a/src/Logging.XUnit/XUnitLogger.cs b/src/Shared/XUnitLogger.cs similarity index 96% rename from src/Logging.XUnit/XUnitLogger.cs rename to src/Shared/XUnitLogger.cs index 33a1abd3..90c6df8b 100644 --- a/src/Logging.XUnit/XUnitLogger.cs +++ b/src/Shared/XUnitLogger.cs @@ -1,314 +1,314 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using System.Text; -using Microsoft.Extensions.Logging; - -namespace MartinCostello.Logging.XUnit; - -/// -/// A class representing an to use with xunit. -/// -public partial class XUnitLogger : ILogger -{ - //// Based on https://github.com/dotnet/runtime/blob/65067052e433eda400c5e7cc9f7b21c84640f901/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLogger.cs#L41-L66 - - /// - /// The padding to use for log levels. - /// - private const string LogLevelPadding = ": "; - - /// - /// The padding to use for messages. This field is read-only. - /// - private static readonly string MessagePadding = new(' ', GetLogLevelString(LogLevel.Debug).Length + LogLevelPadding.Length); - - /// - /// The padding to use for new lines. This field is read-only. - /// - private static readonly string NewLineWithMessagePadding = Environment.NewLine + MessagePadding; - - /// - /// The current builder to use to generate log messages. - /// - [ThreadStatic] - private static StringBuilder? _logBuilder; - - /// - /// The format string used to format the timestamp in log messages. - /// - private readonly string _timestampFormat; - - /// - /// Gets or sets the filter to use. - /// - private Func _filter; - - /// - /// Initializes a new instance of the class. - /// - /// The name for messages produced by the logger. - /// The to use. - private XUnitLogger(string name, XUnitLoggerOptions? options) - { - Name = name ?? throw new ArgumentNullException(nameof(name)); - - _filter = options?.Filter ?? (static (_, _) => true); - _messageSinkMessageFactory = options?.MessageSinkMessageFactory ?? (static (message) => new DiagnosticMessage(message)); - _timestampFormat = options?.TimestampFormat ?? "u"; - IncludeScopes = options?.IncludeScopes ?? false; - } - - /// - /// Gets or sets the category filter to apply to logs. - /// - /// - /// is . - /// - public Func Filter - { - get => _filter; - set => _filter = value ?? throw new ArgumentNullException(nameof(value)); - } - - /// - /// Gets or sets a value indicating whether to include scopes. - /// - public bool IncludeScopes { get; set; } - - /// - /// Gets the name of the logger. - /// - public string Name { get; } - - /// - /// Gets or sets a delegate representing the system clock. - /// - internal Func Clock { get; set; } = static () => DateTimeOffset.Now; - - /// - public IDisposable? BeginScope(TState state) - where TState : notnull - { -#if NET - ArgumentNullException.ThrowIfNull(state); -#else - if (state == null) - { - throw new ArgumentNullException(nameof(state)); - } -#endif - - return XUnitLogScope.Push(state); - } - - /// - public bool IsEnabled(LogLevel logLevel) - { - if (logLevel == LogLevel.None) - { - return false; - } - - return Filter(Name, logLevel); - } - - /// - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) - { - if (!IsEnabled(logLevel)) - { - return; - } - -#if NET - ArgumentNullException.ThrowIfNull(formatter); -#else - if (formatter == null) - { - throw new ArgumentNullException(nameof(formatter)); - } -#endif - - string? message = formatter(state, exception); - - if (!string.IsNullOrEmpty(message) || exception != null) - { - WriteMessage(logLevel, eventId.Id, message, exception); - } - } - - /// - /// Writes a message to the or associated with the instance. - /// - /// The message to write will be written on this level. - /// The Id of the event. - /// The message to write. - /// The exception related to this message. - public virtual void WriteMessage(LogLevel logLevel, int eventId, string? message, Exception? exception) - { - ITestOutputHelper? outputHelper = _outputHelperAccessor?.OutputHelper; - IMessageSink? messageSink = _messageSinkAccessor?.MessageSink; - - if (outputHelper is null && messageSink is null) - { - return; - } - - StringBuilder? logBuilder = _logBuilder; - _logBuilder = null; - - logBuilder ??= new StringBuilder(); - - string logLevelString = GetLogLevelString(logLevel); - - logBuilder.Append(LogLevelPadding); - logBuilder.Append(Name); - logBuilder.Append('['); - logBuilder.Append(eventId); - logBuilder.Append(']'); - logBuilder.AppendLine(); - - if (IncludeScopes) - { - GetScopeInformation(logBuilder); - } - - bool hasMessage = !string.IsNullOrEmpty(message); - - if (hasMessage) - { - logBuilder.Append(MessagePadding); - - int length = logBuilder.Length; - logBuilder.Append(message); - logBuilder.Replace(Environment.NewLine, NewLineWithMessagePadding, length, message!.Length); - } - - if (exception != null) - { - if (hasMessage) - { - logBuilder.AppendLine(); - } - - logBuilder.Append(exception); - } - - // Prefix the formatted message so it renders like this: - // [{timestamp}] {logLevelString}{message} - logBuilder.Insert(0, logLevelString); - logBuilder.Insert(0, "] "); - logBuilder.Insert(0, Clock().ToString(_timestampFormat, CultureInfo.CurrentCulture)); - logBuilder.Insert(0, '['); - - string line = logBuilder.ToString(); - - try - { - outputHelper?.WriteLine(line); - - if (messageSink != null) - { - var sinkMessage = _messageSinkMessageFactory(line); - messageSink.OnMessage(sinkMessage); - } - } - catch (InvalidOperationException) - { - // Ignore exception if the application tries to log after the test ends - // but before the ITestOutputHelper is detached, e.g. "There is no currently active test." - } - - logBuilder.Clear(); - - if (logBuilder.Capacity > 1024) - { - logBuilder.Capacity = 1024; - } - - _logBuilder = logBuilder; - } - - /// - /// Returns the string to use for the specified logging level. - /// - /// The log level to get the representation for. - /// - /// A containing the text representation of . - /// - private static string GetLogLevelString(LogLevel logLevel) - { - return logLevel switch - { - LogLevel.Critical => "crit", - LogLevel.Debug => "dbug", - LogLevel.Error => "fail", - LogLevel.Information => "info", - LogLevel.Trace => "trce", - LogLevel.Warning => "warn", - _ => throw new ArgumentOutOfRangeException(nameof(logLevel)), - }; - } - - /// - /// Gets the scope information for the current operation. - /// - /// The to write the scope to. - private static void GetScopeInformation(StringBuilder builder) - { - var current = XUnitLogScope.Current; - - var stack = new Stack(); - while (current != null) - { - stack.Push(current); - current = current.Parent; - } - - var depth = 0; - static string DepthPadding(int depth) => new(' ', depth * 2); - - while (stack.Count > 0) - { - var elem = stack.Pop(); - foreach (var property in StringifyScope(elem)) - { - builder.Append(MessagePadding) - .Append(DepthPadding(depth)) - .Append("=> ") - .Append(property) - .AppendLine(); - } - - depth++; - } - } - - /// - /// Returns one or more stringified properties from the log scope. - /// - /// The to stringify. - /// An enumeration of scope properties from the current scope. - private static IEnumerable StringifyScope(XUnitLogScope scope) - { - if (scope.State is IEnumerable> pairs) - { - foreach (var pair in pairs) - { - yield return $"{pair.Key}: {pair.Value}"; - } - } - else if (scope.State is IEnumerable entries) - { - foreach (var entry in entries) - { - yield return entry; - } - } - else - { - yield return scope.ToString(); - } - } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using System.Text; +using Microsoft.Extensions.Logging; + +namespace MartinCostello.Logging.XUnit; + +/// +/// A class representing an to use with xunit. +/// +public partial class XUnitLogger : ILogger +{ + //// Based on https://github.com/dotnet/runtime/blob/65067052e433eda400c5e7cc9f7b21c84640f901/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLogger.cs#L41-L66 + + /// + /// The padding to use for log levels. + /// + private const string LogLevelPadding = ": "; + + /// + /// The padding to use for messages. This field is read-only. + /// + private static readonly string MessagePadding = new(' ', GetLogLevelString(LogLevel.Debug).Length + LogLevelPadding.Length); + + /// + /// The padding to use for new lines. This field is read-only. + /// + private static readonly string NewLineWithMessagePadding = Environment.NewLine + MessagePadding; + + /// + /// The current builder to use to generate log messages. + /// + [ThreadStatic] + private static StringBuilder? _logBuilder; + + /// + /// The format string used to format the timestamp in log messages. + /// + private readonly string _timestampFormat; + + /// + /// Gets or sets the filter to use. + /// + private Func _filter; + + /// + /// Initializes a new instance of the class. + /// + /// The name for messages produced by the logger. + /// The to use. + private XUnitLogger(string name, XUnitLoggerOptions? options) + { + Name = name ?? throw new ArgumentNullException(nameof(name)); + + _filter = options?.Filter ?? (static (_, _) => true); + _messageSinkMessageFactory = options?.MessageSinkMessageFactory ?? (static (message) => new DiagnosticMessage(message)); + _timestampFormat = options?.TimestampFormat ?? "u"; + IncludeScopes = options?.IncludeScopes ?? false; + } + + /// + /// Gets or sets the category filter to apply to logs. + /// + /// + /// is . + /// + public Func Filter + { + get => _filter; + set => _filter = value ?? throw new ArgumentNullException(nameof(value)); + } + + /// + /// Gets or sets a value indicating whether to include scopes. + /// + public bool IncludeScopes { get; set; } + + /// + /// Gets the name of the logger. + /// + public string Name { get; } + + /// + /// Gets or sets a delegate representing the system clock. + /// + internal Func Clock { get; set; } = static () => DateTimeOffset.Now; + + /// + public IDisposable? BeginScope(TState state) + where TState : notnull + { +#if NET + ArgumentNullException.ThrowIfNull(state); +#else + if (state == null) + { + throw new ArgumentNullException(nameof(state)); + } +#endif + + return XUnitLogScope.Push(state); + } + + /// + public bool IsEnabled(LogLevel logLevel) + { + if (logLevel == LogLevel.None) + { + return false; + } + + return Filter(Name, logLevel); + } + + /// + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + { + if (!IsEnabled(logLevel)) + { + return; + } + +#if NET + ArgumentNullException.ThrowIfNull(formatter); +#else + if (formatter == null) + { + throw new ArgumentNullException(nameof(formatter)); + } +#endif + + string? message = formatter(state, exception); + + if (!string.IsNullOrEmpty(message) || exception != null) + { + WriteMessage(logLevel, eventId.Id, message, exception); + } + } + + /// + /// Writes a message to the or associated with the instance. + /// + /// The message to write will be written on this level. + /// The Id of the event. + /// The message to write. + /// The exception related to this message. + public virtual void WriteMessage(LogLevel logLevel, int eventId, string? message, Exception? exception) + { + ITestOutputHelper? outputHelper = _outputHelperAccessor?.OutputHelper; + IMessageSink? messageSink = _messageSinkAccessor?.MessageSink; + + if (outputHelper is null && messageSink is null) + { + return; + } + + StringBuilder? logBuilder = _logBuilder; + _logBuilder = null; + + logBuilder ??= new StringBuilder(); + + string logLevelString = GetLogLevelString(logLevel); + + logBuilder.Append(LogLevelPadding); + logBuilder.Append(Name); + logBuilder.Append('['); + logBuilder.Append(eventId); + logBuilder.Append(']'); + logBuilder.AppendLine(); + + if (IncludeScopes) + { + GetScopeInformation(logBuilder); + } + + bool hasMessage = !string.IsNullOrEmpty(message); + + if (hasMessage) + { + logBuilder.Append(MessagePadding); + + int length = logBuilder.Length; + logBuilder.Append(message); + logBuilder.Replace(Environment.NewLine, NewLineWithMessagePadding, length, message!.Length); + } + + if (exception != null) + { + if (hasMessage) + { + logBuilder.AppendLine(); + } + + logBuilder.Append(exception); + } + + // Prefix the formatted message so it renders like this: + // [{timestamp}] {logLevelString}{message} + logBuilder.Insert(0, logLevelString); + logBuilder.Insert(0, "] "); + logBuilder.Insert(0, Clock().ToString(_timestampFormat, CultureInfo.CurrentCulture)); + logBuilder.Insert(0, '['); + + string line = logBuilder.ToString(); + + try + { + outputHelper?.WriteLine(line); + + if (messageSink != null) + { + var sinkMessage = _messageSinkMessageFactory(line); + messageSink.OnMessage(sinkMessage); + } + } + catch (InvalidOperationException) + { + // Ignore exception if the application tries to log after the test ends + // but before the ITestOutputHelper is detached, e.g. "There is no currently active test." + } + + logBuilder.Clear(); + + if (logBuilder.Capacity > 1024) + { + logBuilder.Capacity = 1024; + } + + _logBuilder = logBuilder; + } + + /// + /// Returns the string to use for the specified logging level. + /// + /// The log level to get the representation for. + /// + /// A containing the text representation of . + /// + private static string GetLogLevelString(LogLevel logLevel) + { + return logLevel switch + { + LogLevel.Critical => "crit", + LogLevel.Debug => "dbug", + LogLevel.Error => "fail", + LogLevel.Information => "info", + LogLevel.Trace => "trce", + LogLevel.Warning => "warn", + _ => throw new ArgumentOutOfRangeException(nameof(logLevel)), + }; + } + + /// + /// Gets the scope information for the current operation. + /// + /// The to write the scope to. + private static void GetScopeInformation(StringBuilder builder) + { + var current = XUnitLogScope.Current; + + var stack = new Stack(); + while (current != null) + { + stack.Push(current); + current = current.Parent; + } + + var depth = 0; + static string DepthPadding(int depth) => new(' ', depth * 2); + + while (stack.Count > 0) + { + var elem = stack.Pop(); + foreach (var property in StringifyScope(elem)) + { + builder.Append(MessagePadding) + .Append(DepthPadding(depth)) + .Append("=> ") + .Append(property) + .AppendLine(); + } + + depth++; + } + } + + /// + /// Returns one or more stringified properties from the log scope. + /// + /// The to stringify. + /// An enumeration of scope properties from the current scope. + private static IEnumerable StringifyScope(XUnitLogScope scope) + { + if (scope.State is IEnumerable> pairs) + { + foreach (var pair in pairs) + { + yield return $"{pair.Key}: {pair.Value}"; + } + } + else if (scope.State is IEnumerable entries) + { + foreach (var entry in entries) + { + yield return entry; + } + } + else + { + yield return scope.ToString(); + } + } +} diff --git a/src/Logging.XUnit/XUnitLoggerExtensions.IMessageSink.cs b/src/Shared/XUnitLoggerExtensions.IMessageSink.cs similarity index 97% rename from src/Logging.XUnit/XUnitLoggerExtensions.IMessageSink.cs rename to src/Shared/XUnitLoggerExtensions.IMessageSink.cs index 5254210e..fa101e02 100644 --- a/src/Logging.XUnit/XUnitLoggerExtensions.IMessageSink.cs +++ b/src/Shared/XUnitLoggerExtensions.IMessageSink.cs @@ -1,393 +1,393 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using MartinCostello.Logging.XUnit; -using Microsoft.Extensions.DependencyInjection.Extensions; - -namespace Microsoft.Extensions.Logging; - -/// -/// A class containing extension methods for configuring logging to xunit. This class cannot be inherited. -/// -public static partial class XUnitLoggerExtensions -{ - /// - /// Adds an xunit logger to the logging builder. - /// - /// The to use. - /// The to use. - /// - /// The instance of specified by . - /// - /// - /// or is . - /// - public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, IMessageSinkAccessor accessor) - { -#if NET - ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(accessor); -#else - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (accessor == null) - { - throw new ArgumentNullException(nameof(accessor)); - } -#endif - - return builder.AddXUnit(accessor, static (_) => { }); - } - - /// - /// Adds an xunit logger to the logging builder. - /// - /// The to use. - /// The to use. - /// A delegate to a method to use to configure the logging options. - /// - /// The instance of specified by . - /// - /// - /// , or is . - /// - public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, IMessageSinkAccessor accessor, Action configure) - { -#if NET - ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(accessor); - ArgumentNullException.ThrowIfNull(configure); -#else - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (accessor == null) - { - throw new ArgumentNullException(nameof(accessor)); - } - - if (configure == null) - { - throw new ArgumentNullException(nameof(configure)); - } -#endif - - var options = new XUnitLoggerOptions(); - - configure(options); - -#pragma warning disable CA2000 - builder.AddProvider(new XUnitLoggerProvider(accessor, options)); -#pragma warning restore CA2000 - - builder.Services.TryAddSingleton(accessor); - - return builder; - } - - /// - /// Adds an xunit logger to the logging builder. - /// - /// The to use. - /// The to use. - /// - /// The instance of specified by . - /// - /// - /// or is . - /// - public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, IMessageSink messageSink) - { -#if NET - ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(messageSink); -#else - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (messageSink == null) - { - throw new ArgumentNullException(nameof(messageSink)); - } -#endif - - return builder.AddXUnit(messageSink, static (_) => { }); - } - - /// - /// Adds an xunit logger to the logging builder. - /// - /// The to use. - /// The to use. - /// A delegate to a method to use to configure the logging options. - /// - /// The instance of specified by . - /// - /// - /// , or is . - /// - public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, IMessageSink messageSink, Action configure) - { -#if NET - ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(messageSink); - ArgumentNullException.ThrowIfNull(configure); -#else - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (messageSink == null) - { - throw new ArgumentNullException(nameof(messageSink)); - } - - if (configure == null) - { - throw new ArgumentNullException(nameof(configure)); - } -#endif - - var options = new XUnitLoggerOptions(); - - configure(options); - -#pragma warning disable CA2000 - return builder.AddProvider(new XUnitLoggerProvider(messageSink, options)); -#pragma warning restore CA2000 - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// The minimum to be logged. - /// - /// The instance of specified by . - /// - /// - /// or is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink, LogLevel minLevel) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(messageSink); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (messageSink == null) - { - throw new ArgumentNullException(nameof(messageSink)); - } -#endif - - return factory.AddXUnit(messageSink, (_, level) => level >= minLevel); - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// The category filter to apply to logs. - /// - /// The instance of specified by . - /// - /// - /// , or is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink, Func filter) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(messageSink); - ArgumentNullException.ThrowIfNull(filter); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (messageSink == null) - { - throw new ArgumentNullException(nameof(messageSink)); - } - - if (filter == null) - { - throw new ArgumentNullException(nameof(filter)); - } -#endif - - return factory.AddXUnit(messageSink, (options) => options.Filter = filter); - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// - /// The instance of specified by . - /// - /// - /// or is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(messageSink); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (messageSink == null) - { - throw new ArgumentNullException(nameof(messageSink)); - } -#endif - - return factory.AddXUnit(messageSink, static (_) => { }); - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// The options to use for logging to xunit. - /// - /// The instance of specified by . - /// - /// - /// , OR is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink, XUnitLoggerOptions options) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(messageSink); - ArgumentNullException.ThrowIfNull(options); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (messageSink == null) - { - throw new ArgumentNullException(nameof(messageSink)); - } - - if (options == null) - { - throw new ArgumentNullException(nameof(options)); - } -#endif - - return factory.AddXUnit(messageSink, () => options); - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// A delegate to a method to use to configure the logging options. - /// - /// The instance of specified by . - /// - /// - /// , or is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink, Action configure) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(messageSink); - ArgumentNullException.ThrowIfNull(configure); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (messageSink == null) - { - throw new ArgumentNullException(nameof(messageSink)); - } - - if (configure == null) - { - throw new ArgumentNullException(nameof(configure)); - } -#endif - - return factory.AddXUnit(messageSink, () => - { - var options = new XUnitLoggerOptions(); - configure(options); - return options; - }); - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// A delegate to a method that returns a configured to use. - /// - /// The instance of specified by . - /// - /// - /// , or is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink, Func configure) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(messageSink); - ArgumentNullException.ThrowIfNull(configure); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (messageSink == null) - { - throw new ArgumentNullException(nameof(messageSink)); - } - - if (configure == null) - { - throw new ArgumentNullException(nameof(configure)); - } -#endif - - var options = configure(); - -#pragma warning disable CA2000 - factory.AddProvider(new XUnitLoggerProvider(messageSink, options)); -#pragma warning restore CA2000 - - return factory; - } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using MartinCostello.Logging.XUnit; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Microsoft.Extensions.Logging; + +/// +/// A class containing extension methods for configuring logging to xunit. This class cannot be inherited. +/// +public static partial class XUnitLoggerExtensions +{ + /// + /// Adds an xunit logger to the logging builder. + /// + /// The to use. + /// The to use. + /// + /// The instance of specified by . + /// + /// + /// or is . + /// + public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, IMessageSinkAccessor accessor) + { +#if NET + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(accessor); +#else + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (accessor == null) + { + throw new ArgumentNullException(nameof(accessor)); + } +#endif + + return builder.AddXUnit(accessor, static (_) => { }); + } + + /// + /// Adds an xunit logger to the logging builder. + /// + /// The to use. + /// The to use. + /// A delegate to a method to use to configure the logging options. + /// + /// The instance of specified by . + /// + /// + /// , or is . + /// + public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, IMessageSinkAccessor accessor, Action configure) + { +#if NET + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(accessor); + ArgumentNullException.ThrowIfNull(configure); +#else + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (accessor == null) + { + throw new ArgumentNullException(nameof(accessor)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } +#endif + + var options = new XUnitLoggerOptions(); + + configure(options); + +#pragma warning disable CA2000 + builder.AddProvider(new XUnitLoggerProvider(accessor, options)); +#pragma warning restore CA2000 + + builder.Services.TryAddSingleton(accessor); + + return builder; + } + + /// + /// Adds an xunit logger to the logging builder. + /// + /// The to use. + /// The to use. + /// + /// The instance of specified by . + /// + /// + /// or is . + /// + public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, IMessageSink messageSink) + { +#if NET + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(messageSink); +#else + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (messageSink == null) + { + throw new ArgumentNullException(nameof(messageSink)); + } +#endif + + return builder.AddXUnit(messageSink, static (_) => { }); + } + + /// + /// Adds an xunit logger to the logging builder. + /// + /// The to use. + /// The to use. + /// A delegate to a method to use to configure the logging options. + /// + /// The instance of specified by . + /// + /// + /// , or is . + /// + public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, IMessageSink messageSink, Action configure) + { +#if NET + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(messageSink); + ArgumentNullException.ThrowIfNull(configure); +#else + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (messageSink == null) + { + throw new ArgumentNullException(nameof(messageSink)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } +#endif + + var options = new XUnitLoggerOptions(); + + configure(options); + +#pragma warning disable CA2000 + return builder.AddProvider(new XUnitLoggerProvider(messageSink, options)); +#pragma warning restore CA2000 + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// The minimum to be logged. + /// + /// The instance of specified by . + /// + /// + /// or is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink, LogLevel minLevel) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(messageSink); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (messageSink == null) + { + throw new ArgumentNullException(nameof(messageSink)); + } +#endif + + return factory.AddXUnit(messageSink, (_, level) => level >= minLevel); + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// The category filter to apply to logs. + /// + /// The instance of specified by . + /// + /// + /// , or is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink, Func filter) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(messageSink); + ArgumentNullException.ThrowIfNull(filter); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (messageSink == null) + { + throw new ArgumentNullException(nameof(messageSink)); + } + + if (filter == null) + { + throw new ArgumentNullException(nameof(filter)); + } +#endif + + return factory.AddXUnit(messageSink, (options) => options.Filter = filter); + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// + /// The instance of specified by . + /// + /// + /// or is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(messageSink); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (messageSink == null) + { + throw new ArgumentNullException(nameof(messageSink)); + } +#endif + + return factory.AddXUnit(messageSink, static (_) => { }); + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// The options to use for logging to xunit. + /// + /// The instance of specified by . + /// + /// + /// , OR is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink, XUnitLoggerOptions options) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(messageSink); + ArgumentNullException.ThrowIfNull(options); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (messageSink == null) + { + throw new ArgumentNullException(nameof(messageSink)); + } + + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } +#endif + + return factory.AddXUnit(messageSink, () => options); + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// A delegate to a method to use to configure the logging options. + /// + /// The instance of specified by . + /// + /// + /// , or is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink, Action configure) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(messageSink); + ArgumentNullException.ThrowIfNull(configure); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (messageSink == null) + { + throw new ArgumentNullException(nameof(messageSink)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } +#endif + + return factory.AddXUnit(messageSink, () => + { + var options = new XUnitLoggerOptions(); + configure(options); + return options; + }); + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// A delegate to a method that returns a configured to use. + /// + /// The instance of specified by . + /// + /// + /// , or is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink, Func configure) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(messageSink); + ArgumentNullException.ThrowIfNull(configure); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (messageSink == null) + { + throw new ArgumentNullException(nameof(messageSink)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } +#endif + + var options = configure(); + +#pragma warning disable CA2000 + factory.AddProvider(new XUnitLoggerProvider(messageSink, options)); +#pragma warning restore CA2000 + + return factory; + } +} diff --git a/src/Logging.XUnit/XUnitLoggerExtensions.ITestOutputHelper.cs b/src/Shared/XUnitLoggerExtensions.ITestOutputHelper.cs similarity index 97% rename from src/Logging.XUnit/XUnitLoggerExtensions.ITestOutputHelper.cs rename to src/Shared/XUnitLoggerExtensions.ITestOutputHelper.cs index 3bc6dc80..4b92e11a 100644 --- a/src/Logging.XUnit/XUnitLoggerExtensions.ITestOutputHelper.cs +++ b/src/Shared/XUnitLoggerExtensions.ITestOutputHelper.cs @@ -1,419 +1,419 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using System.ComponentModel; -using MartinCostello.Logging.XUnit; -using Microsoft.Extensions.DependencyInjection.Extensions; - -namespace Microsoft.Extensions.Logging; - -/// -/// A class containing extension methods for configuring logging to xunit. This class cannot be inherited. -/// -[EditorBrowsable(EditorBrowsableState.Never)] -public static partial class XUnitLoggerExtensions -{ - /// - /// Adds an xunit logger to the logging builder. - /// - /// The to use. - /// - /// The instance of specified by . - /// - /// - /// is . - /// - public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder) - { -#if NET - ArgumentNullException.ThrowIfNull(builder); -#else - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } -#endif - - return builder.AddXUnit(new AmbientTestOutputHelperAccessor(), static (_) => { }); - } - - /// - /// Adds an xunit logger to the logging builder. - /// - /// The to use. - /// The to use. - /// - /// The instance of specified by . - /// - /// - /// or is . - /// - public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, ITestOutputHelperAccessor accessor) - { -#if NET - ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(accessor); -#else - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (accessor == null) - { - throw new ArgumentNullException(nameof(accessor)); - } -#endif - - return builder.AddXUnit(accessor, static (_) => { }); - } - - /// - /// Adds an xunit logger to the logging builder. - /// - /// The to use. - /// The to use. - /// A delegate to a method to use to configure the logging options. - /// - /// The instance of specified by . - /// - /// - /// , OR is . - /// - public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, ITestOutputHelperAccessor accessor, Action configure) - { -#if NET - ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(accessor); - ArgumentNullException.ThrowIfNull(configure); -#else - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (accessor == null) - { - throw new ArgumentNullException(nameof(accessor)); - } - - if (configure == null) - { - throw new ArgumentNullException(nameof(configure)); - } -#endif - - var options = new XUnitLoggerOptions(); - - configure(options); - -#pragma warning disable CA2000 - builder.AddProvider(new XUnitLoggerProvider(accessor, options)); -#pragma warning restore CA2000 - - builder.Services.TryAddSingleton(accessor); - - return builder; - } - - /// - /// Adds an xunit logger to the logging builder. - /// - /// The to use. - /// The to use. - /// - /// The instance of specified by . - /// - /// - /// or is . - /// - public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, ITestOutputHelper outputHelper) - { -#if NET - ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(outputHelper); -#else - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (outputHelper == null) - { - throw new ArgumentNullException(nameof(outputHelper)); - } -#endif - - return builder.AddXUnit(outputHelper, static (_) => { }); - } - - /// - /// Adds an xunit logger to the logging builder. - /// - /// The to use. - /// The to use. - /// A delegate to a method to use to configure the logging options. - /// - /// The instance of specified by . - /// - /// - /// , OR is . - /// - public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, ITestOutputHelper outputHelper, Action configure) - { -#if NET - ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(outputHelper); - ArgumentNullException.ThrowIfNull(configure); -#else - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (outputHelper == null) - { - throw new ArgumentNullException(nameof(outputHelper)); - } - - if (configure == null) - { - throw new ArgumentNullException(nameof(configure)); - } -#endif - - var options = new XUnitLoggerOptions(); - - configure(options); - -#pragma warning disable CA2000 - return builder.AddProvider(new XUnitLoggerProvider(outputHelper, options)); -#pragma warning restore CA2000 - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// The minimum to be logged. - /// - /// The instance of specified by . - /// - /// - /// or is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper, LogLevel minLevel) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(outputHelper); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (outputHelper == null) - { - throw new ArgumentNullException(nameof(outputHelper)); - } -#endif - - return factory.AddXUnit(outputHelper, (_, level) => level >= minLevel); - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// The category filter to apply to logs. - /// - /// The instance of specified by . - /// - /// - /// , or is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper, Func filter) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(outputHelper); - ArgumentNullException.ThrowIfNull(filter); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (outputHelper == null) - { - throw new ArgumentNullException(nameof(outputHelper)); - } - - if (filter == null) - { - throw new ArgumentNullException(nameof(filter)); - } -#endif - - return factory.AddXUnit(outputHelper, (options) => options.Filter = filter); - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// - /// The instance of specified by . - /// - /// - /// or is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(outputHelper); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (outputHelper == null) - { - throw new ArgumentNullException(nameof(outputHelper)); - } -#endif - - return factory.AddXUnit(outputHelper, static (_) => { }); - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// The options to use for logging to xunit. - /// - /// The instance of specified by . - /// - /// - /// , OR is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper, XUnitLoggerOptions options) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(outputHelper); - ArgumentNullException.ThrowIfNull(options); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (outputHelper == null) - { - throw new ArgumentNullException(nameof(outputHelper)); - } - - if (options == null) - { - throw new ArgumentNullException(nameof(options)); - } -#endif - - return factory.AddXUnit(outputHelper, () => options); - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// A delegate to a method to use to configure the logging options. - /// - /// The instance of specified by . - /// - /// - /// , OR is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper, Action configure) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(outputHelper); - ArgumentNullException.ThrowIfNull(configure); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (outputHelper == null) - { - throw new ArgumentNullException(nameof(outputHelper)); - } - - if (configure == null) - { - throw new ArgumentNullException(nameof(configure)); - } -#endif - - return factory.AddXUnit(outputHelper, () => - { - var options = new XUnitLoggerOptions(); - configure(options); - return options; - }); - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// A delegate to a method that returns a configured to use. - /// - /// The instance of specified by . - /// - /// - /// , or is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper, Func configure) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(outputHelper); - ArgumentNullException.ThrowIfNull(configure); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (outputHelper == null) - { - throw new ArgumentNullException(nameof(outputHelper)); - } - - if (configure == null) - { - throw new ArgumentNullException(nameof(configure)); - } -#endif - - var options = configure(); - -#pragma warning disable CA2000 - factory.AddProvider(new XUnitLoggerProvider(outputHelper, options)); -#pragma warning restore CA2000 - - return factory; - } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using System.ComponentModel; +using MartinCostello.Logging.XUnit; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Microsoft.Extensions.Logging; + +/// +/// A class containing extension methods for configuring logging to xunit. This class cannot be inherited. +/// +[EditorBrowsable(EditorBrowsableState.Never)] +public static partial class XUnitLoggerExtensions +{ + /// + /// Adds an xunit logger to the logging builder. + /// + /// The to use. + /// + /// The instance of specified by . + /// + /// + /// is . + /// + public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder) + { +#if NET + ArgumentNullException.ThrowIfNull(builder); +#else + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } +#endif + + return builder.AddXUnit(new AmbientTestOutputHelperAccessor(), static (_) => { }); + } + + /// + /// Adds an xunit logger to the logging builder. + /// + /// The to use. + /// The to use. + /// + /// The instance of specified by . + /// + /// + /// or is . + /// + public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, ITestOutputHelperAccessor accessor) + { +#if NET + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(accessor); +#else + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (accessor == null) + { + throw new ArgumentNullException(nameof(accessor)); + } +#endif + + return builder.AddXUnit(accessor, static (_) => { }); + } + + /// + /// Adds an xunit logger to the logging builder. + /// + /// The to use. + /// The to use. + /// A delegate to a method to use to configure the logging options. + /// + /// The instance of specified by . + /// + /// + /// , OR is . + /// + public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, ITestOutputHelperAccessor accessor, Action configure) + { +#if NET + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(accessor); + ArgumentNullException.ThrowIfNull(configure); +#else + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (accessor == null) + { + throw new ArgumentNullException(nameof(accessor)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } +#endif + + var options = new XUnitLoggerOptions(); + + configure(options); + +#pragma warning disable CA2000 + builder.AddProvider(new XUnitLoggerProvider(accessor, options)); +#pragma warning restore CA2000 + + builder.Services.TryAddSingleton(accessor); + + return builder; + } + + /// + /// Adds an xunit logger to the logging builder. + /// + /// The to use. + /// The to use. + /// + /// The instance of specified by . + /// + /// + /// or is . + /// + public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, ITestOutputHelper outputHelper) + { +#if NET + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(outputHelper); +#else + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (outputHelper == null) + { + throw new ArgumentNullException(nameof(outputHelper)); + } +#endif + + return builder.AddXUnit(outputHelper, static (_) => { }); + } + + /// + /// Adds an xunit logger to the logging builder. + /// + /// The to use. + /// The to use. + /// A delegate to a method to use to configure the logging options. + /// + /// The instance of specified by . + /// + /// + /// , OR is . + /// + public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, ITestOutputHelper outputHelper, Action configure) + { +#if NET + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(outputHelper); + ArgumentNullException.ThrowIfNull(configure); +#else + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (outputHelper == null) + { + throw new ArgumentNullException(nameof(outputHelper)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } +#endif + + var options = new XUnitLoggerOptions(); + + configure(options); + +#pragma warning disable CA2000 + return builder.AddProvider(new XUnitLoggerProvider(outputHelper, options)); +#pragma warning restore CA2000 + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// The minimum to be logged. + /// + /// The instance of specified by . + /// + /// + /// or is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper, LogLevel minLevel) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(outputHelper); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (outputHelper == null) + { + throw new ArgumentNullException(nameof(outputHelper)); + } +#endif + + return factory.AddXUnit(outputHelper, (_, level) => level >= minLevel); + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// The category filter to apply to logs. + /// + /// The instance of specified by . + /// + /// + /// , or is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper, Func filter) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(outputHelper); + ArgumentNullException.ThrowIfNull(filter); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (outputHelper == null) + { + throw new ArgumentNullException(nameof(outputHelper)); + } + + if (filter == null) + { + throw new ArgumentNullException(nameof(filter)); + } +#endif + + return factory.AddXUnit(outputHelper, (options) => options.Filter = filter); + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// + /// The instance of specified by . + /// + /// + /// or is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(outputHelper); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (outputHelper == null) + { + throw new ArgumentNullException(nameof(outputHelper)); + } +#endif + + return factory.AddXUnit(outputHelper, static (_) => { }); + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// The options to use for logging to xunit. + /// + /// The instance of specified by . + /// + /// + /// , OR is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper, XUnitLoggerOptions options) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(outputHelper); + ArgumentNullException.ThrowIfNull(options); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (outputHelper == null) + { + throw new ArgumentNullException(nameof(outputHelper)); + } + + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } +#endif + + return factory.AddXUnit(outputHelper, () => options); + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// A delegate to a method to use to configure the logging options. + /// + /// The instance of specified by . + /// + /// + /// , OR is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper, Action configure) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(outputHelper); + ArgumentNullException.ThrowIfNull(configure); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (outputHelper == null) + { + throw new ArgumentNullException(nameof(outputHelper)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } +#endif + + return factory.AddXUnit(outputHelper, () => + { + var options = new XUnitLoggerOptions(); + configure(options); + return options; + }); + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// A delegate to a method that returns a configured to use. + /// + /// The instance of specified by . + /// + /// + /// , or is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper, Func configure) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(outputHelper); + ArgumentNullException.ThrowIfNull(configure); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (outputHelper == null) + { + throw new ArgumentNullException(nameof(outputHelper)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } +#endif + + var options = configure(); + +#pragma warning disable CA2000 + factory.AddProvider(new XUnitLoggerProvider(outputHelper, options)); +#pragma warning restore CA2000 + + return factory; + } +} diff --git a/src/Logging.XUnit/XUnitLoggerOptions.cs b/src/Shared/XUnitLoggerOptions.cs similarity index 97% rename from src/Logging.XUnit/XUnitLoggerOptions.cs rename to src/Shared/XUnitLoggerOptions.cs index 7e4e169f..db397713 100644 --- a/src/Logging.XUnit/XUnitLoggerOptions.cs +++ b/src/Shared/XUnitLoggerOptions.cs @@ -1,41 +1,41 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using System.Diagnostics.CodeAnalysis; -using Microsoft.Extensions.Logging; - -namespace MartinCostello.Logging.XUnit; - -/// -/// A class representing configuration options for logging to xunit. -/// -public class XUnitLoggerOptions -{ - /// - /// Initializes a new instance of the class. - /// - public XUnitLoggerOptions() - { - } - - /// - /// Gets or sets the category filter to apply to logs. - /// - public Func Filter { get; set; } = static (c, l) => true; // By default log everything - - /// - /// Gets or sets the message sink message factory to use when writing to a . - /// - public Func MessageSinkMessageFactory { get; set; } = static (m) => new DiagnosticMessage(m); - - /// - /// Gets or sets a value indicating whether to include scopes. - /// - public bool IncludeScopes { get; set; } - - /// - /// Gets or sets format string used to format the timestamp in log messages. Defaults to u. - /// - [StringSyntax(StringSyntaxAttribute.DateTimeFormat)] - public string? TimestampFormat { get; set; } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using System.Diagnostics.CodeAnalysis; +using Microsoft.Extensions.Logging; + +namespace MartinCostello.Logging.XUnit; + +/// +/// A class representing configuration options for logging to xunit. +/// +public class XUnitLoggerOptions +{ + /// + /// Initializes a new instance of the class. + /// + public XUnitLoggerOptions() + { + } + + /// + /// Gets or sets the category filter to apply to logs. + /// + public Func Filter { get; set; } = static (c, l) => true; // By default log everything + + /// + /// Gets or sets the message sink message factory to use when writing to a . + /// + public Func MessageSinkMessageFactory { get; set; } = static (m) => new DiagnosticMessage(m); + + /// + /// Gets or sets a value indicating whether to include scopes. + /// + public bool IncludeScopes { get; set; } + + /// + /// Gets or sets format string used to format the timestamp in log messages. Defaults to u. + /// + [StringSyntax(StringSyntaxAttribute.DateTimeFormat)] + public string? TimestampFormat { get; set; } +} diff --git a/src/Logging.XUnit/XUnitLoggerProvider.IMessageSink.cs b/src/Shared/XUnitLoggerProvider.IMessageSink.cs similarity index 97% rename from src/Logging.XUnit/XUnitLoggerProvider.IMessageSink.cs rename to src/Shared/XUnitLoggerProvider.IMessageSink.cs index 3e974504..e5c82f3e 100644 --- a/src/Logging.XUnit/XUnitLoggerProvider.IMessageSink.cs +++ b/src/Shared/XUnitLoggerProvider.IMessageSink.cs @@ -1,44 +1,44 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using Microsoft.Extensions.Logging; - -namespace MartinCostello.Logging.XUnit; - -/// -/// A class representing an to use with xunit. -/// -public partial class XUnitLoggerProvider -{ - /// - /// The to use. This field is readonly. - /// - private readonly IMessageSinkAccessor? _messageSinkAccessor; - - /// - /// Initializes a new instance of the class. - /// - /// The to use. - /// The options to use for logging to xunit. - /// - /// or is . - /// - public XUnitLoggerProvider(IMessageSink messageSink, XUnitLoggerOptions options) - : this(new MessageSinkAccessor(messageSink), options) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The to use. - /// The options to use for logging to xunit. - /// - /// or is . - /// - public XUnitLoggerProvider(IMessageSinkAccessor accessor, XUnitLoggerOptions options) - { - _messageSinkAccessor = accessor ?? throw new ArgumentNullException(nameof(accessor)); - _options = options ?? throw new ArgumentNullException(nameof(options)); - } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using Microsoft.Extensions.Logging; + +namespace MartinCostello.Logging.XUnit; + +/// +/// A class representing an to use with xunit. +/// +public partial class XUnitLoggerProvider +{ + /// + /// The to use. This field is readonly. + /// + private readonly IMessageSinkAccessor? _messageSinkAccessor; + + /// + /// Initializes a new instance of the class. + /// + /// The to use. + /// The options to use for logging to xunit. + /// + /// or is . + /// + public XUnitLoggerProvider(IMessageSink messageSink, XUnitLoggerOptions options) + : this(new MessageSinkAccessor(messageSink), options) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The to use. + /// The options to use for logging to xunit. + /// + /// or is . + /// + public XUnitLoggerProvider(IMessageSinkAccessor accessor, XUnitLoggerOptions options) + { + _messageSinkAccessor = accessor ?? throw new ArgumentNullException(nameof(accessor)); + _options = options ?? throw new ArgumentNullException(nameof(options)); + } +} diff --git a/src/Logging.XUnit/XUnitLoggerProvider.ITestOutputHelper.cs b/src/Shared/XUnitLoggerProvider.ITestOutputHelper.cs similarity index 97% rename from src/Logging.XUnit/XUnitLoggerProvider.ITestOutputHelper.cs rename to src/Shared/XUnitLoggerProvider.ITestOutputHelper.cs index 13e2f868..c5eccc84 100644 --- a/src/Logging.XUnit/XUnitLoggerProvider.ITestOutputHelper.cs +++ b/src/Shared/XUnitLoggerProvider.ITestOutputHelper.cs @@ -1,44 +1,44 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using Microsoft.Extensions.Logging; - -namespace MartinCostello.Logging.XUnit; - -/// -/// A class representing an to use with xunit. -/// -public partial class XUnitLoggerProvider -{ - /// - /// The to use. This field is readonly. - /// - private readonly ITestOutputHelperAccessor? _outputHelperAccessor; - - /// - /// Initializes a new instance of the class. - /// - /// The to use. - /// The options to use for logging to xunit. - /// - /// or is . - /// - public XUnitLoggerProvider(ITestOutputHelper outputHelper, XUnitLoggerOptions options) - : this(new TestOutputHelperAccessor(outputHelper), options) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The to use. - /// The options to use for logging to xunit. - /// - /// or is . - /// - public XUnitLoggerProvider(ITestOutputHelperAccessor accessor, XUnitLoggerOptions options) - { - _outputHelperAccessor = accessor ?? throw new ArgumentNullException(nameof(accessor)); - _options = options ?? throw new ArgumentNullException(nameof(options)); - } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using Microsoft.Extensions.Logging; + +namespace MartinCostello.Logging.XUnit; + +/// +/// A class representing an to use with xunit. +/// +public partial class XUnitLoggerProvider +{ + /// + /// The to use. This field is readonly. + /// + private readonly ITestOutputHelperAccessor? _outputHelperAccessor; + + /// + /// Initializes a new instance of the class. + /// + /// The to use. + /// The options to use for logging to xunit. + /// + /// or is . + /// + public XUnitLoggerProvider(ITestOutputHelper outputHelper, XUnitLoggerOptions options) + : this(new TestOutputHelperAccessor(outputHelper), options) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The to use. + /// The options to use for logging to xunit. + /// + /// or is . + /// + public XUnitLoggerProvider(ITestOutputHelperAccessor accessor, XUnitLoggerOptions options) + { + _outputHelperAccessor = accessor ?? throw new ArgumentNullException(nameof(accessor)); + _options = options ?? throw new ArgumentNullException(nameof(options)); + } +} diff --git a/src/Logging.XUnit/XUnitLoggerProvider.cs b/src/Shared/XUnitLoggerProvider.cs similarity index 96% rename from src/Logging.XUnit/XUnitLoggerProvider.cs rename to src/Shared/XUnitLoggerProvider.cs index 38a18622..99a44892 100644 --- a/src/Logging.XUnit/XUnitLoggerProvider.cs +++ b/src/Shared/XUnitLoggerProvider.cs @@ -1,60 +1,60 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using System.Collections.Concurrent; -using Microsoft.Extensions.Logging; - -namespace MartinCostello.Logging.XUnit; - -/// -/// A class representing an to use with xunit. -/// -[ProviderAlias("XUnit")] -public partial class XUnitLoggerProvider : ILoggerProvider -{ - /// - /// The cached loggers to use for each category. This field is readonly. - /// - private readonly ConcurrentDictionary _loggers = []; - - /// - /// The to use. This field is readonly. - /// - private readonly XUnitLoggerOptions _options; - - /// - /// Finalizes an instance of the class. - /// - ~XUnitLoggerProvider() - { - Dispose(false); - } - - /// - public virtual ILogger CreateLogger(string categoryName) - { - return _loggers.GetOrAdd(categoryName, (name) => - _outputHelperAccessor is not null ? - new(name, _outputHelperAccessor, _options) : - new(name, _messageSinkAccessor!, _options)); - } - - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - /// - /// to release both managed and unmanaged resources; - /// to release only unmanaged resources. - /// - protected virtual void Dispose(bool disposing) - { - // Nothing to dispose of - } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using System.Collections.Concurrent; +using Microsoft.Extensions.Logging; + +namespace MartinCostello.Logging.XUnit; + +/// +/// A class representing an to use with xunit. +/// +[ProviderAlias("XUnit")] +public partial class XUnitLoggerProvider : ILoggerProvider +{ + /// + /// The cached loggers to use for each category. This field is readonly. + /// + private readonly ConcurrentDictionary _loggers = []; + + /// + /// The to use. This field is readonly. + /// + private readonly XUnitLoggerOptions _options; + + /// + /// Finalizes an instance of the class. + /// + ~XUnitLoggerProvider() + { + Dispose(false); + } + + /// + public virtual ILogger CreateLogger(string categoryName) + { + return _loggers.GetOrAdd(categoryName, (name) => + _outputHelperAccessor is not null ? + new(name, _outputHelperAccessor, _options) : + new(name, _messageSinkAccessor!, _options)); + } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + // Nothing to dispose of + } +}