forked from dotnet/yarp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathXunitLoggerProvider.cs
More file actions
134 lines (113 loc) · 4.48 KB
/
XunitLoggerProvider.cs
File metadata and controls
134 lines (113 loc) · 4.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using Xunit;
namespace Microsoft.Extensions.Logging.Testing;
public class XunitLoggerProvider : ILoggerProvider
{
// Used to distinguish when multiple apps are running as part of the same test.
private static int InstanceCount;
private readonly int _providerInstanceId = Interlocked.Increment(ref InstanceCount);
private readonly ITestOutputHelper _output;
private readonly LogLevel _minLevel;
private readonly DateTimeOffset? _logStart;
public XunitLoggerProvider(ITestOutputHelper output)
: this(output, LogLevel.Trace)
{
}
public XunitLoggerProvider(ITestOutputHelper output, LogLevel minLevel)
: this(output, minLevel, null)
{
}
public XunitLoggerProvider(ITestOutputHelper output, LogLevel minLevel, DateTimeOffset? logStart)
{
_output = output;
_minLevel = minLevel;
_logStart = logStart;
}
public ILogger CreateLogger(string categoryName)
{
return new XunitLogger(_output, categoryName, _minLevel, _logStart, _providerInstanceId);
}
public void Dispose()
{
}
}
public class XunitLogger : ILogger
{
private static readonly string[] NewLineChars = new[] { Environment.NewLine };
private readonly string _category;
private readonly LogLevel _minLogLevel;
private readonly ITestOutputHelper _output;
private readonly DateTimeOffset? _logStart;
private readonly int _providerInstanceId;
public XunitLogger(ITestOutputHelper output, string category, LogLevel minLogLevel, DateTimeOffset? logStart, int providerInstanceId)
{
_minLogLevel = minLogLevel;
_category = category;
_output = output;
_logStart = logStart;
_providerInstanceId = providerInstanceId;
}
public void Log<TState>(
LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (!IsEnabled(logLevel))
{
return;
}
// Buffer the message into a single string in order to avoid shearing the message when running across multiple threads.
var messageBuilder = new StringBuilder();
var timestamp = _logStart.HasValue ?
$"{(DateTimeOffset.UtcNow - _logStart.Value).TotalSeconds.ToString("N3", CultureInfo.InvariantCulture)}s" :
DateTimeOffset.UtcNow.ToString("s", CultureInfo.InvariantCulture);
var firstLinePrefix = $"| [{timestamp}] I:{_providerInstanceId} {_category} {logLevel}: ";
var lines = formatter(state, exception).Split(NewLineChars, StringSplitOptions.RemoveEmptyEntries);
messageBuilder.AppendLine(firstLinePrefix + lines.FirstOrDefault() ?? string.Empty);
var additionalLinePrefix = "|" + new string(' ', firstLinePrefix.Length - 1);
foreach (var line in lines.Skip(1))
{
messageBuilder.AppendLine(additionalLinePrefix + line);
}
if (exception != null)
{
lines = exception.ToString().Split(NewLineChars, StringSplitOptions.RemoveEmptyEntries);
additionalLinePrefix = "| ";
foreach (var line in lines)
{
messageBuilder.AppendLine(additionalLinePrefix + line);
}
}
// Remove the last line-break, because ITestOutputHelper only has WriteLine.
var message = messageBuilder.ToString();
if (message.EndsWith(Environment.NewLine, StringComparison.Ordinal))
{
message = message.Substring(0, message.Length - Environment.NewLine.Length);
}
try
{
_output.WriteLine(message);
}
catch (Exception)
{
// We could fail because we're on a background thread and our captured ITestOutputHelper is
// busted (if the test "completed" before the background thread fired).
// So, ignore this. There isn't really anything we can do but hope the
// caller has additional loggers registered
}
}
public bool IsEnabled(LogLevel logLevel)
=> logLevel >= _minLogLevel;
public IDisposable BeginScope<TState>(TState state)
=> new NullScope();
private sealed class NullScope : IDisposable
{
public void Dispose()
{
}
}
}