csharp-sdk/tests/Common/Utils/XunitLoggerProvider.cs
Copilot 50d6d0b570
Fix XunitLoggerProvider crash when background work logs after test disposal (#1229)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
2026-01-30 14:56:12 -08:00

65 lines
2 KiB
C#

using Microsoft.Extensions.Logging;
using System.Globalization;
using System.Text;
namespace ModelContextProtocol.Tests.Utils;
public class XunitLoggerProvider(ITestOutputHelper output) : ILoggerProvider
{
public ILogger CreateLogger(string categoryName)
{
return new XunitLogger(output, categoryName);
}
public void Dispose()
{
}
private class XunitLogger(ITestOutputHelper output, string category) : ILogger
{
public void Log<TState>(
LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
var sb = new StringBuilder();
var timestamp = DateTimeOffset.UtcNow.ToString("s", CultureInfo.InvariantCulture);
var prefix = $"| [{timestamp}] {category} {logLevel}: ";
var lines = formatter(state, exception);
sb.Append(prefix);
sb.Append(lines);
if (exception is not null)
{
sb.AppendLine();
sb.Append(exception.ToString());
}
try
{
output.WriteLine(sb.ToString());
}
catch (InvalidOperationException)
{
// Ignore exceptions from xUnit's TestOutputHelper when the test has already completed.
// Background work may continue logging after xUnit has disposed the test context.
}
catch (NullReferenceException)
{
// xUnit v3 may throw NullReferenceException in TestOutputHelper.QueueTestOutput()
// when the internal queue has been torn down after test completion.
}
}
public bool IsEnabled(LogLevel logLevel) => true;
public IDisposable BeginScope<TState>(TState state) where TState : notnull
=> new NoopDisposable();
private sealed class NoopDisposable : IDisposable
{
public void Dispose()
{
}
}
}
}