如何在 xunit2 中捕获 log4net 输出
How to capture log4net output in xunit2
我读过 http://xunit.github.io/docs/capturing-output.html 它似乎适用于在测试期间使我的测试输出特定消息 运行 但我真的希望能够捕获已经存在的 log4net 输出集成到我正在测试的类
过去我将 log4net 设置为使用 TraceLogger,并且测试框架能够将输出与测试相关联。 (不同的测试框架)。我怎样才能以某种方式将 log4net 输出关联到 Xunit IOutputHelper?
这是我想出的答案
这是一个 class 我可以让我的测试 class 继承自:
public class LogOutputTester:IDisposable
{
private readonly IAppenderAttachable _attachable;
private TestOutputAppender _appender;
protected LogOutputTester(ITestOutputHelper output)
{
log4net.Config.XmlConfigurator.Configure();
var root = ((log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository()).Root;
_attachable = root;
_appender = new TestOutputAppender(output);
if (_attachable != null)
_attachable.AddAppender(_appender);
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
_attachable.RemoveAppender(_appender);
}
}
这是我在上面的帮助程序中引用的自定义 Appender:
public class TestOutputAppender : AppenderSkeleton
{
private readonly ITestOutputHelper _xunitTestOutputHelper;
public TestOutputAppender(ITestOutputHelper xunitTestOutputHelper)
{
_xunitTestOutputHelper = xunitTestOutputHelper;
Name = "TestOutputAppender";
Layout = new PatternLayout("%-5p %d %5rms %-22.22c{1} %-18.18M - %m%n");
}
protected override void Append(LoggingEvent loggingEvent)
{
_xunitTestOutputHelper.WriteLine(RenderLoggingEvent(loggingEvent));
}
}
这可以进一步自定义以采用自定义布局或其他...
最后 - 我只是让我的测试 class 继承自这个助手:
public class MyTestClass:LogOutputTester
{
public EdgeClientTests(ITestOutputHelper output):base(output)
{
}
...
您也可以让您的测试直接访问输出对象...
基于此处描述的解决方案 https://github.com/damianh/CapturingLogOutputWithXunit2AndParallelTests#capturing-test-specific-log-output-when-using-xunit-2x-parallel-testing,我使用 log4net 重写了它。
可能对某人有帮助。
public static class LogHelper
{
private static readonly Subject<LoggingEvent> LogEventSubject = new Subject<LoggingEvent>();
private const string CaptureCorrelationIdKey = "EventId";
private static readonly ILayout layout;
static LogHelper()
{
XmlConfigurator.Configure();
var root = ((log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository()).Root;
IAppenderAttachable attachable = root;
var appender = new XunitAppender(logEvent => LogEventSubject.OnNext(logEvent));
attachable?.AddAppender(appender);
layout = appender.Layout;
appender.ActivateOptions();
}
public static IDisposable Capture(ITestOutputHelper testOutputHelper)
{
var captureId = Guid.NewGuid();
Func<LoggingEvent, bool> filter = logEvent =>
logEvent.GetProperties().Contains(CaptureCorrelationIdKey) &&
logEvent.LookupProperty(CaptureCorrelationIdKey).ToString() == captureId.ToString();
var subscription = LogEventSubject.Where(filter).Subscribe(logEvent =>
{
using (var writer = new StringWriter())
{
layout.Format(writer, logEvent);
testOutputHelper.WriteLine(writer.ToString());
}
});
ThreadContext.Properties[CaptureCorrelationIdKey] = captureId.ToString();
return new DisposableAction(() =>
{
subscription.Dispose();
ThreadContext.Properties.Clear();
});
}
private class DisposableAction : IDisposable
{
private readonly Action _action;
public DisposableAction(Action action)
{
_action = action;
}
public void Dispose()
{
_action();
}
}
public sealed class XunitAppender : AppenderSkeleton
{
private readonly Action<LoggingEvent> _action;
public XunitAppender(Action<LoggingEvent> action)
{
_action = action;
Name = "XunitAppender";
Layout = new PatternLayout("%date %-5level %logger - %message");
}
protected override void Append(LoggingEvent loggingEvent)
{
_action(loggingEvent);
}
}
}
我读过 http://xunit.github.io/docs/capturing-output.html 它似乎适用于在测试期间使我的测试输出特定消息 运行 但我真的希望能够捕获已经存在的 log4net 输出集成到我正在测试的类
过去我将 log4net 设置为使用 TraceLogger,并且测试框架能够将输出与测试相关联。 (不同的测试框架)。我怎样才能以某种方式将 log4net 输出关联到 Xunit IOutputHelper?
这是我想出的答案
这是一个 class 我可以让我的测试 class 继承自:
public class LogOutputTester:IDisposable
{
private readonly IAppenderAttachable _attachable;
private TestOutputAppender _appender;
protected LogOutputTester(ITestOutputHelper output)
{
log4net.Config.XmlConfigurator.Configure();
var root = ((log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository()).Root;
_attachable = root;
_appender = new TestOutputAppender(output);
if (_attachable != null)
_attachable.AddAppender(_appender);
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
_attachable.RemoveAppender(_appender);
}
}
这是我在上面的帮助程序中引用的自定义 Appender:
public class TestOutputAppender : AppenderSkeleton
{
private readonly ITestOutputHelper _xunitTestOutputHelper;
public TestOutputAppender(ITestOutputHelper xunitTestOutputHelper)
{
_xunitTestOutputHelper = xunitTestOutputHelper;
Name = "TestOutputAppender";
Layout = new PatternLayout("%-5p %d %5rms %-22.22c{1} %-18.18M - %m%n");
}
protected override void Append(LoggingEvent loggingEvent)
{
_xunitTestOutputHelper.WriteLine(RenderLoggingEvent(loggingEvent));
}
}
这可以进一步自定义以采用自定义布局或其他...
最后 - 我只是让我的测试 class 继承自这个助手:
public class MyTestClass:LogOutputTester
{
public EdgeClientTests(ITestOutputHelper output):base(output)
{
}
...
您也可以让您的测试直接访问输出对象...
基于此处描述的解决方案 https://github.com/damianh/CapturingLogOutputWithXunit2AndParallelTests#capturing-test-specific-log-output-when-using-xunit-2x-parallel-testing,我使用 log4net 重写了它。 可能对某人有帮助。
public static class LogHelper
{
private static readonly Subject<LoggingEvent> LogEventSubject = new Subject<LoggingEvent>();
private const string CaptureCorrelationIdKey = "EventId";
private static readonly ILayout layout;
static LogHelper()
{
XmlConfigurator.Configure();
var root = ((log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository()).Root;
IAppenderAttachable attachable = root;
var appender = new XunitAppender(logEvent => LogEventSubject.OnNext(logEvent));
attachable?.AddAppender(appender);
layout = appender.Layout;
appender.ActivateOptions();
}
public static IDisposable Capture(ITestOutputHelper testOutputHelper)
{
var captureId = Guid.NewGuid();
Func<LoggingEvent, bool> filter = logEvent =>
logEvent.GetProperties().Contains(CaptureCorrelationIdKey) &&
logEvent.LookupProperty(CaptureCorrelationIdKey).ToString() == captureId.ToString();
var subscription = LogEventSubject.Where(filter).Subscribe(logEvent =>
{
using (var writer = new StringWriter())
{
layout.Format(writer, logEvent);
testOutputHelper.WriteLine(writer.ToString());
}
});
ThreadContext.Properties[CaptureCorrelationIdKey] = captureId.ToString();
return new DisposableAction(() =>
{
subscription.Dispose();
ThreadContext.Properties.Clear();
});
}
private class DisposableAction : IDisposable
{
private readonly Action _action;
public DisposableAction(Action action)
{
_action = action;
}
public void Dispose()
{
_action();
}
}
public sealed class XunitAppender : AppenderSkeleton
{
private readonly Action<LoggingEvent> _action;
public XunitAppender(Action<LoggingEvent> action)
{
_action = action;
Name = "XunitAppender";
Layout = new PatternLayout("%date %-5level %logger - %message");
}
protected override void Append(LoggingEvent loggingEvent)
{
_action(loggingEvent);
}
}
}