using Serilog.Core; using Serilog.Events; using Serilog.Parsing; namespace WebApp.Logging { /// /// Custom Serilog sink that downgrades antiforgery token deserialization errors to Information level /// public class AntiforgeryLogEventSink : ILogEventSink { private readonly ILogEventSink _wrappedSink; private readonly MessageTemplateParser _parser; public AntiforgeryLogEventSink(ILogEventSink wrappedSink) { _wrappedSink = wrappedSink; _parser = new MessageTemplateParser(); } public void Emit(LogEvent logEvent) { // Check if this is an antiforgery token deserialization error (Event ID 7) if (IsTokenDeserializeException(logEvent)) { // Rewrite the log event at Information level with explanatory message var messageTemplate = _parser.Parse( "Antiforgery token validation failed - expected after container restart. " + "User will receive a fresh token on page refresh. Original message: {OriginalMessage}"); var rewrittenEvent = new LogEvent( logEvent.Timestamp, LogEventLevel.Information, null, // No exception at Info level messageTemplate, [ new LogEventProperty("OriginalMessage", new ScalarValue(logEvent.MessageTemplate.Render(logEvent.Properties))), new LogEventProperty("SourceContext", logEvent.Properties.GetValueOrDefault("SourceContext") ?? new ScalarValue("Unknown")), new LogEventProperty("RequestPath", logEvent.Properties.GetValueOrDefault("RequestPath") ?? new ScalarValue("Unknown")) ]); _wrappedSink.Emit(rewrittenEvent); } else { // Pass through all other log events unchanged _wrappedSink.Emit(logEvent); } } private bool IsTokenDeserializeException(LogEvent logEvent) { if (logEvent.Level != LogEventLevel.Error) return false; if (logEvent.Properties.TryGetValue("EventId", out var eventId)) { var eventIdString = eventId.ToString(); return eventIdString.Contains("\"Id\":7") && eventIdString.Contains("\"Name\":\"TokenDeserializeException\""); } return false; } } }