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
+ }
+}