diff --git a/src/NetEvolve.Logging.XUnit/NetEvolve.Logging.XUnit.csproj b/src/NetEvolve.Logging.XUnit/NetEvolve.Logging.XUnit.csproj
index 2c4ae7a..04c1fe7 100644
--- a/src/NetEvolve.Logging.XUnit/NetEvolve.Logging.XUnit.csproj
+++ b/src/NetEvolve.Logging.XUnit/NetEvolve.Logging.XUnit.csproj
@@ -22,6 +22,7 @@
+
diff --git a/src/NetEvolve.Logging.XUnit/XUnitLogger.cs b/src/NetEvolve.Logging.XUnit/XUnitLogger.cs
index 809cf08..c2791e6 100644
--- a/src/NetEvolve.Logging.XUnit/XUnitLogger.cs
+++ b/src/NetEvolve.Logging.XUnit/XUnitLogger.cs
@@ -8,6 +8,7 @@
using NetEvolve.Arguments;
using NetEvolve.Logging.Abstractions;
using Xunit.Abstractions;
+using Xunit.Sdk;
///
/// Represents a logger that writes messages to xunit output.
@@ -15,11 +16,12 @@
public class XUnitLogger : ILogger, ISupportExternalScope
{
private readonly IXUnitLoggerOptions _options;
- private readonly ITestOutputHelper _testOutputHelper;
private readonly TimeProvider _timeProvider;
private readonly List _loggedMessages;
+ private readonly Action _writeToLog;
+
private const int DefaultCapacity = 1024;
[ThreadStatic]
@@ -33,6 +35,43 @@ public class XUnitLogger : ILogger, ISupportExternalScope
///
public IReadOnlyList LoggedMessages => _loggedMessages.AsReadOnly();
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The to write the log messages to.
+ /// The to use to get the current time.
+ /// The to use to get the current scope.
+ /// The options to control the behavior of the logger.
+ /// A cached or new instance of .
+ public static XUnitLogger CreateLogger(
+ IMessageSink messageSink,
+ TimeProvider timeProvider,
+ IExternalScopeProvider? scopeProvider = null,
+ IXUnitLoggerOptions? options = null
+ )
+ {
+ Argument.ThrowIfNull(messageSink);
+
+ return new XUnitLogger(messageSink, timeProvider, scopeProvider, options);
+ }
+
+ ///
+ /// Creates a new instance of .
+ ///
+ /// The type who's fullname is used as the category name for messages produced by the logger.
+ /// The to write the log messages to.
+ /// The to use to get the current time.
+ /// The to use to get the current scope.
+ /// The options to control the behavior of the logger.
+ /// A cached or new instance of .
+ public static XUnitLogger CreateLogger(
+ IMessageSink messageSink,
+ TimeProvider timeProvider,
+ IExternalScopeProvider? scopeProvider = null,
+ IXUnitLoggerOptions? options = null
+ )
+ where T : notnull => new XUnitLogger(messageSink, timeProvider, scopeProvider, options);
+
///
/// Creates a new instance of .
///
@@ -82,11 +121,31 @@ private protected XUnitLogger(
Argument.ThrowIfNull(timeProvider);
ScopeProvider = scopeProvider ?? NullExternalScopeProvider.Instance;
- _testOutputHelper = testOutputHelper;
_timeProvider = timeProvider;
_options = options ?? XUnitLoggerOptions.Default;
_loggedMessages = [];
+
+ _writeToLog = testOutputHelper.WriteLine;
+ }
+
+ private protected XUnitLogger(
+ IMessageSink messageSink,
+ TimeProvider timeProvider,
+ IExternalScopeProvider? scopeProvider,
+ IXUnitLoggerOptions? options
+ )
+ {
+ Argument.ThrowIfNull(messageSink);
+ Argument.ThrowIfNull(timeProvider);
+
+ ScopeProvider = scopeProvider ?? NullExternalScopeProvider.Instance;
+ _timeProvider = timeProvider;
+ _options = options ?? XUnitLoggerOptions.Default;
+
+ _loggedMessages = [];
+
+ _writeToLog = message => _ = messageSink.OnMessage(new DiagnosticMessage(message));
}
///
@@ -112,88 +171,84 @@ public void Log(
return;
}
- var builder = _builder;
- _builder = null;
- builder ??= new StringBuilder(DefaultCapacity);
-
try
{
var message = formatter(state, exception);
var now = _timeProvider.GetLocalNow();
- (builder, var scopes) = CreateMessage(
- logLevel,
- state,
- exception,
- builder,
- message,
- now
- );
+ var (fullMessage, scopes) = CreateMessage(logLevel, state, exception, message, now);
_loggedMessages.Add(
new LoggedMessage(now, logLevel, eventId, message, exception, scopes)
);
- _testOutputHelper.WriteLine(builder.ToString());
+
+ _writeToLog.Invoke(fullMessage);
}
catch
{
// Ignore exception.
// Unfortunately, this can happen if the process is terminated before the end of the test.
}
- finally
- {
- _ = builder.Clear();
- if (builder.Capacity > DefaultCapacity)
- {
- builder.Capacity = DefaultCapacity;
- }
- _builder = builder;
- }
}
- private (StringBuilder, List