Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support UtcTimestamp token in output template (new since Serilog 4.0) #165

Open
wants to merge 6 commits into
base: dev
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,11 @@ public OutputTemplateRenderer(ConsoleTheme theme, string outputTemplate, IFormat
}
else if (pt.PropertyName == OutputProperties.TimestampPropertyName)
{
renderers.Add(new TimestampTokenRenderer(theme, pt, formatProvider));
renderers.Add(new TimestampTokenRenderer(theme, pt, formatProvider, convertToUtc: false));
}
else if (pt.PropertyName == OutputProperties.UtcTimestampPropertyName)
{
renderers.Add(new TimestampTokenRenderer(theme, pt, formatProvider, convertToUtc: true));
}
else if (pt.PropertyName == OutputProperties.PropertiesPropertyName)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,22 @@ class TimestampTokenRenderer : OutputTemplateTokenRenderer
readonly ConsoleTheme _theme;
readonly PropertyToken _token;
readonly IFormatProvider? _formatProvider;
readonly bool _convertToUtc;

public TimestampTokenRenderer(ConsoleTheme theme, PropertyToken token, IFormatProvider? formatProvider)
public TimestampTokenRenderer(ConsoleTheme theme, PropertyToken token, IFormatProvider? formatProvider, bool convertToUtc)
{
_theme = theme;
_token = token;
_formatProvider = formatProvider;
}
_convertToUtc = convertToUtc;
}

public override void Render(LogEvent logEvent, TextWriter output)
{
var sv = new DateTimeOffsetValue(logEvent.Timestamp);
var timestamp = _convertToUtc
? logEvent.Timestamp.ToUniversalTime()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately DateTimeOffset ISO formatting in UTC renders as the unanticipated T00:00 rather than the more common and compact Z notation. You should be able to see this by running the code with {UtcTimestamp} and no format string.

MessageTemplateTextFormatter gets around this using .UtcDateTime and renders the value as a DateTime with DateTimeKind.Utc:

https://github.com/serilog/serilog/blob/dev/src/Serilog/Formatting/Display/MessageTemplateTextFormatter.cs#L107

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing that out, I'll have a look into that and will address it in an update.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nblumhardt, the issue you pointed out is now addressed in the latest update. Rendering {UtcTimestamp} is now done as a DateTime.

: logEvent.Timestamp;
var sv = new DateTimeOffsetValue(timestamp);

var _ = 0;
using (_theme.Apply(output, ConsoleThemeStyle.SecondaryText, ref _))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -400,4 +400,28 @@ public void TraceAndSpanAreIncludedWhenPresent()
formatter.Format(evt, sw);
Assert.Equal($"{traceId}/{spanId}", sw.ToString());
}

[Fact]
public void TimestampTokenRendersLocalTime()
{
var logTimestampWithTimeZoneOffset = DateTimeOffset.Parse("2024-09-03T14:15:16.079+02:00", CultureInfo.InvariantCulture);
var formatter = new OutputTemplateRenderer(ConsoleTheme.None, "{Timestamp:yyyy-MM-dd HH:mm:ss}", CultureInfo.InvariantCulture);
var evt = new LogEvent(logTimestampWithTimeZoneOffset, LogEventLevel.Information, null, MessageTemplate.Empty, Array.Empty<LogEventProperty>());
var sw = new StringWriter();
formatter.Format(evt, sw);
// expect time in local time, unchanged from the input, the +02:00 offset should not affect the output
Assert.Equal("2024-09-03 14:15:16", sw.ToString());
}

[Fact]
public void UtcTimestampTokenRendersUtcTime()
{
var logTimestampWithTimeZoneOffset = DateTimeOffset.Parse("2024-09-03T14:15:16.079+02:00", CultureInfo.InvariantCulture);
var formatter = new OutputTemplateRenderer(ConsoleTheme.None, "{UtcTimestamp:yyyy-MM-dd HH:mm:ss}", CultureInfo.InvariantCulture);
var evt = new LogEvent(logTimestampWithTimeZoneOffset, LogEventLevel.Information, null, MessageTemplate.Empty, Array.Empty<LogEventProperty>());
var sw = new StringWriter();
formatter.Format(evt, sw);
// expect time in UTC, the +02:00 offset must be applied to adjust the hour
Assert.Equal("2024-09-03 12:15:16", sw.ToString());
}
}