Skip to content

Commit c522f21

Browse files
Merge pull request #313 from martincostello/custom-timestamp-format
Custom timestamp format
2 parents e21b1a5 + 18a6af8 commit c522f21

File tree

6 files changed

+46
-30
lines changed

6 files changed

+46
-30
lines changed

Directory.Build.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@
3737
<SignAssembly>true</SignAssembly>
3838
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
3939
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
40-
<AssemblyVersion>0.2.0.0</AssemblyVersion>
41-
<VersionPrefix>0.2.2</VersionPrefix>
40+
<AssemblyVersion>0.3.0.0</AssemblyVersion>
41+
<VersionPrefix>0.3.0</VersionPrefix>
4242
<VersionSuffix Condition=" '$(VersionSuffix)' == '' AND '$(GITHUB_ACTIONS)' != '' ">beta$([System.Convert]::ToInt32(`$(GITHUB_RUN_NUMBER)`).ToString(`0000`))</VersionSuffix>
4343
<VersionPrefix Condition=" $(GITHUB_REF.StartsWith(`refs/tags/v`)) ">$(GITHUB_REF.Replace('refs/tags/v', ''))</VersionPrefix>
4444
<VersionSuffix Condition=" $(GITHUB_REF.StartsWith(`refs/tags/v`)) "></VersionSuffix>

src/Logging.XUnit/XUnitLogger.cs

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace MartinCostello.Logging.XUnit;
1313
/// </summary>
1414
public partial class XUnitLogger : ILogger
1515
{
16-
//// Based on https://github.com/aspnet/Logging/blob/master/src/Microsoft.Extensions.Logging.Console/ConsoleLogger.cs
16+
//// Based on https://github.com/dotnet/runtime/blob/65067052e433eda400c5e7cc9f7b21c84640f901/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLogger.cs#L41-L66
1717

1818
/// <summary>
1919
/// The padding to use for log levels.
@@ -36,6 +36,11 @@ public partial class XUnitLogger : ILogger
3636
[ThreadStatic]
3737
private static StringBuilder? _logBuilder;
3838

39+
/// <summary>
40+
/// The format string used to format the timestamp in log messages.
41+
/// </summary>
42+
private readonly string _timestampFormat;
43+
3944
/// <summary>
4045
/// Gets or sets the filter to use.
4146
/// </summary>
@@ -50,8 +55,9 @@ private XUnitLogger(string name, XUnitLoggerOptions? options)
5055
{
5156
Name = name ?? throw new ArgumentNullException(nameof(name));
5257

53-
_filter = options?.Filter ?? ((_, _) => true);
58+
_filter = options?.Filter ?? (static (_, _) => true);
5459
_messageSinkMessageFactory = options?.MessageSinkMessageFactory ?? (message => new DiagnosticMessage(message));
60+
_timestampFormat = options?.TimestampFormat ?? "u";
5561
IncludeScopes = options?.IncludeScopes ?? false;
5662
}
5763

@@ -63,8 +69,8 @@ private XUnitLogger(string name, XUnitLoggerOptions? options)
6369
/// </exception>
6470
public Func<string?, LogLevel, bool> Filter
6571
{
66-
get { return _filter; }
67-
set { _filter = value ?? throw new ArgumentNullException(nameof(value)); }
72+
get => _filter;
73+
set => _filter = value ?? throw new ArgumentNullException(nameof(value));
6874
}
6975

7076
/// <summary>
@@ -80,7 +86,7 @@ private XUnitLogger(string name, XUnitLoggerOptions? options)
8086
/// <summary>
8187
/// Gets or sets a delegate representing the system clock.
8288
/// </summary>
83-
internal Func<DateTimeOffset> Clock { get; set; } = () => DateTimeOffset.Now;
89+
internal Func<DateTimeOffset> Clock { get; set; } = static () => DateTimeOffset.Now;
8490

8591
/// <inheritdoc />
8692
public IDisposable BeginScope<TState>(TState state)
@@ -156,7 +162,8 @@ public virtual void WriteMessage(LogLevel logLevel, int eventId, string? message
156162
logBuilder.Append(Name);
157163
logBuilder.Append('[');
158164
logBuilder.Append(eventId);
159-
logBuilder.AppendLine("]");
165+
logBuilder.Append(']');
166+
logBuilder.AppendLine();
160167

161168
if (IncludeScopes)
162169
{
@@ -184,11 +191,17 @@ public virtual void WriteMessage(LogLevel logLevel, int eventId, string? message
184191
logBuilder.Append(exception.ToString());
185192
}
186193

187-
string formatted = logBuilder.ToString();
194+
// Prefix the formatted message so it renders like this:
195+
// [{timestamp}] {logLevelString}{message}
196+
logBuilder.Insert(0, logLevelString);
197+
logBuilder.Insert(0, "] ");
198+
logBuilder.Insert(0, Clock().ToString(_timestampFormat, CultureInfo.CurrentCulture));
199+
logBuilder.Insert(0, '[');
200+
201+
string line = logBuilder.ToString();
188202

189203
try
190204
{
191-
var line = $"[{Clock():u}] {logLevelString}{formatted}";
192205
if (outputHelper != null)
193206
{
194207
outputHelper.WriteLine(line);
@@ -228,17 +241,11 @@ private static string GetLogLevelString(LogLevel logLevel)
228241
return logLevel switch
229242
{
230243
LogLevel.Critical => "crit",
231-
232244
LogLevel.Debug => "dbug",
233-
234245
LogLevel.Error => "fail",
235-
236246
LogLevel.Information => "info",
237-
238247
LogLevel.Trace => "trce",
239-
240248
LogLevel.Warning => "warn",
241-
242249
_ => throw new ArgumentOutOfRangeException(nameof(logLevel)),
243250
};
244251
}
@@ -267,10 +274,10 @@ private static void GetScopeInformation(StringBuilder builder)
267274
foreach (var property in StringifyScope(elem))
268275
{
269276
builder.Append(MessagePadding)
270-
.Append(DepthPadding(depth))
271-
.Append("=> ")
272-
.Append(property)
273-
.AppendLine();
277+
.Append(DepthPadding(depth))
278+
.Append("=> ")
279+
.Append(property)
280+
.AppendLine();
274281
}
275282

276283
depth++;
@@ -288,7 +295,7 @@ private static IEnumerable<string> StringifyScope(XUnitLogScope scope)
288295
{
289296
foreach (var pair in pairs)
290297
{
291-
yield return pair.Key + ": " + pair.Value;
298+
yield return $"{pair.Key}: {pair.Value}";
292299
}
293300
}
294301
else if (scope.State is IEnumerable<string> entries)

src/Logging.XUnit/XUnitLoggerExtensions.IMessageSink.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, IMessageSin
3636
throw new ArgumentNullException(nameof(accessor));
3737
}
3838

39-
return builder.AddXUnit(accessor, (_) => { });
39+
return builder.AddXUnit(accessor, static (_) => { });
4040
}
4141

4242
/// <summary>
@@ -104,7 +104,7 @@ public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, IMessageSin
104104
throw new ArgumentNullException(nameof(messageSink));
105105
}
106106

107-
return builder.AddXUnit(messageSink, (_) => { });
107+
return builder.AddXUnit(messageSink, static (_) => { });
108108
}
109109

110110
/// <summary>
@@ -227,7 +227,7 @@ public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink
227227
throw new ArgumentNullException(nameof(messageSink));
228228
}
229229

230-
return factory.AddXUnit(messageSink, (_) => { });
230+
return factory.AddXUnit(messageSink, static (_) => { });
231231
}
232232

233233
/// <summary>

src/Logging.XUnit/XUnitLoggerExtensions.ITestOutputHelper.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder)
3131
throw new ArgumentNullException(nameof(builder));
3232
}
3333

34-
return builder.AddXUnit(new AmbientTestOutputHelperAccessor(), (_) => { });
34+
return builder.AddXUnit(new AmbientTestOutputHelperAccessor(), static (_) => { });
3535
}
3636

3737
/// <summary>
@@ -57,7 +57,7 @@ public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, ITestOutput
5757
throw new ArgumentNullException(nameof(accessor));
5858
}
5959

60-
return builder.AddXUnit(accessor, (_) => { });
60+
return builder.AddXUnit(accessor, static (_) => { });
6161
}
6262

6363
/// <summary>
@@ -125,7 +125,7 @@ public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, ITestOutput
125125
throw new ArgumentNullException(nameof(outputHelper));
126126
}
127127

128-
return builder.AddXUnit(outputHelper, (_) => { });
128+
return builder.AddXUnit(outputHelper, static (_) => { });
129129
}
130130

131131
/// <summary>
@@ -248,7 +248,7 @@ public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHe
248248
throw new ArgumentNullException(nameof(outputHelper));
249249
}
250250

251-
return factory.AddXUnit(outputHelper, (_) => { });
251+
return factory.AddXUnit(outputHelper, static (_) => { });
252252
}
253253

254254
/// <summary>

src/Logging.XUnit/XUnitLoggerOptions.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,23 @@ public XUnitLoggerOptions()
2222
/// <summary>
2323
/// Gets or sets the category filter to apply to logs.
2424
/// </summary>
25-
public Func<string?, LogLevel, bool> Filter { get; set; } = (c, l) => true; // By default log everything
25+
public Func<string?, LogLevel, bool> Filter { get; set; } = static (c, l) => true; // By default log everything
2626

2727
/// <summary>
2828
/// Gets or sets the message sink message factory to use when writing to a <see cref="IMessageSink"/>.
2929
/// </summary>
30-
public Func<string, IMessageSinkMessage> MessageSinkMessageFactory { get; set; } = m => new DiagnosticMessage(m);
30+
public Func<string, IMessageSinkMessage> MessageSinkMessageFactory { get; set; } = static (m) => new DiagnosticMessage(m);
3131

3232
/// <summary>
3333
/// Gets or sets a value indicating whether to include scopes.
3434
/// </summary>
3535
public bool IncludeScopes { get; set; }
36+
37+
/// <summary>
38+
/// Gets or sets format string used to format the timestamp in log messages. Defaults to <c>u</c>.
39+
/// </summary>
40+
#if NET7_0_OR_GREATER
41+
[StringSyntax(StringSyntaxAttribute.DateTimeFormat)] // TODO Light up after .NET 7 ships https://github.com/martincostello/xunit-logging/issues/315
42+
#endif
43+
public string? TimestampFormat { get; set; }
3644
}

tests/Logging.XUnit.Tests/IntegrationTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ public static void Can_Configure_xunit_For_ILoggerFactory_With_Options_Factory()
177177
var options = new XUnitLoggerOptions()
178178
{
179179
Filter = (_, level) => level >= LogLevel.Error,
180+
TimestampFormat = "yyyy-MM-dd HH:mm:ss.fff",
180181
};
181182

182183
var logger = BootstrapFactory((builder) => builder.AddXUnit(mock.Object, () => options));

0 commit comments

Comments
 (0)