Json serialization exception for standard AspNetCore log message, which results in broken log messages
We wanted to switch to ZLogger, but now immediatelly encountered this issue after trying it out:
ZLogger error: System.NotSupportedException: Serialization and deserialization of 'System.Reflection.RuntimeMethodInfo' instances is not supported. The unsupported member type is located on type 'System.Object'. Path: $.Metadata.
---> System.NotSupportedException: Serialization and deserialization of 'System.Reflection.RuntimeMethodInfo' instances is not supported.
at System.Text.Json.Serialization.Converters.UnsupportedTypeConverter`1.Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonConverter`1.TryWriteAsObject(Utf8JsonWriter writer, Object value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.Converters.IEnumerableDefaultConverter`2.OnWriteResume(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonCollectionConverter`2.OnTryWrite(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.GetMemberAndWriteJson(Object obj, WriteStack& state, Utf8JsonWriter writer)
at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.JsonConverter`1.WriteCore(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
--- End of inner exception stack trace ---
at System.Text.Json.ThrowHelper.ThrowNotSupportedException(WriteStack& state, Exception innerException)
at System.Text.Json.Serialization.JsonConverter`1.WriteCore(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.Serialize(Utf8JsonWriter writer, T& rootValue, Object rootValueBoxed)
at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.SerializeAsObject(Utf8JsonWriter writer, Object rootValue)
at System.Text.Json.JsonSerializer.Serialize(Utf8JsonWriter writer, Object value, Type inputType, JsonSerializerOptions options)
at ZLogger.LogStates.StringFormatterLogState`1.WriteJsonParameterKeyValues(Utf8JsonWriter jsonWriter, JsonSerializerOptions jsonSerializerOptions, IKeyNameMutator keyNameMutator)
at ZLogger.ZLoggerEntry`1.WriteJsonParameterKeyValues(Utf8JsonWriter jsonWriter, JsonSerializerOptions jsonSerializerOptions, IKeyNameMutator keyNameMutator)
at ZLogger.Formatters.SystemTextJsonZLoggerFormatter.FormatLogEntry(IBufferWriter`1 writer, IZLoggerEntry entry)
at ZLogger.ZLoggerEntry`1.FormatUtf8(IBufferWriter`1 writer, IZLoggerFormatter formatter)
at ZLogger.AsyncStreamLineMessageWriter.WriteLoop()
{
"Timestamp": "2025-08-06T09:50:41.6766875+02:00",
"LogLevel": "Information",
"Category": "Microsoft.AspNetCore.Routing.EndpointMiddleware",
"Message": "Executed endpoint 'HTTP: POST /validation/body => Handle'",
"SpanId": "926ae9e59ec8abfc",
"TraceId": "ececed91e60b6005911ce384052b4b56",
"ParentId": "0000000000000000",
"ConnectionId": "0HNEKNNOFC647",
"RequestId": "0HNEKNNOFC647:00000001",
"RequestPath": "/validation/body",
"User": "anonymous"{ // <---- here ------------------------------
"Timestamp": "2025-08-06T09:50:41.6820922+02:00",
"LogLevel": "Information",
"Category": "Microsoft.AspNetCore.Hosting.Diagnostics",
"Message": "Request finished HTTP/1.1 POST http://localhost:5097/validation/body - 400 - application/problem+json 318.6568ms",
"SpanId": "926ae9e59ec8abfc",
"TraceId": "ececed91e60b6005911ce384052b4b56",
"ParentId": "0000000000000000",
"ConnectionId": "0HNEKNNOFC647",
"RequestId": "0HNEKNNOFC647:00000001",
"RequestPath": "/validation/body",
"ElapsedMilliseconds": 318.6568,
"StatusCode": 400,
"ContentType": "application/problem+json",
"ContentLength": null,
"Protocol": "HTTP/1.1",
"Method": "POST",
"Scheme": "http",
"Host": "localhost:5097",
"PathBase": "",
"Path": "/validation/body",
"QueryString": ""
}
It just starts with the next log message without finishing the first one. But this is just a standard asp net core log message, so I expected ZLogger to be able to handle that.
Our ZLogger config is like this:
hostBuilder.ConfigureLogging(builder => builder
.ClearProviders()
.AddZLoggerConsole(o =>
{
o.IncludeScopes = true;
o.InternalErrorLogger = ex => Console.Error.WriteLine($"ZLogger error: {ex}");
o.UseJsonFormatter(formatter =>
{
formatter.JsonSerializerOptions.WriteIndented = true;
});
}));
And since it is exactly after the User property each time, maybe this is also important:
app.Use(async (ctx, next) =>
{
var logger = ctx.RequestServices.GetRequiredService<ILogger<Program>>();
using var loggerScope = logger.BeginScope(new Dictionary<string, object>
{
{"User", ctx.User.FindFirst("sub")?.Value ?? "anonymous" },
});
await next();
});
I figured out now, that the System.Reflection.RuntimeMethodInfo is the minimal api Microsoft.AspNetCore.Routing.RouteEndpoint that is passed to JsonSerializer.Serialize(jsonWriter, x.Value, valueType, jsonSerializerOptions); here and then throws.
Would it make sense to fall back to a simple .ToString(), if the JsonSerializer fails?
Edit: Nope, ToString results in the whole endpoint object being inserted as json.
hmm, while experimenting I also encountered this quite frequently:
ZLogger error: System.ObjectDisposedException: IFeatureCollection has been disposed.
Object name: 'Collection'.
at Microsoft.AspNetCore.Http.Features.FeatureReferences`1.ThrowContextDisposed()
at Microsoft.AspNetCore.Http.Features.FeatureReferences`1.ContextDisposed()
at Microsoft.AspNetCore.Http.Features.FeatureReferences`1.Fetch[TFeature](TFeature& cached, Func`2 factory)
at Microsoft.AspNetCore.Http.DefaultHttpResponse.get_StatusCode()
at Microsoft.AspNetCore.Hosting.HostingRequestFinishedLog.get_Item(Int32 index)
at ZLogger.LogStates.StringFormatterLogState`1.WriteJsonParameterKeyValues(Utf8JsonWriter jsonWriter, JsonSerializerOptions jsonSerializerOptions, IKeyNameMutator keyNameMutator) in C:\workspace\ZLogger\src\ZLogger\LogStates\StringFormatterLogState.cs:line 94
at ZLogger.ZLoggerEntry`1.WriteJsonParameterKeyValues(Utf8JsonWriter jsonWriter, JsonSerializerOptions jsonSerializerOptions, IKeyNameMutator keyNameMutator) in C:\workspace\ZLogger\src\ZLogger\ZLoggerEntry.cs:line 70
at ZLogger.Formatters.SystemTextJsonZLoggerFormatter.FormatLogEntry(IBufferWriter`1 writer, IZLoggerEntry entry) in C:\workspace\ZLogger\src\ZLogger\Formatters\SystemTextJsonZLoggerFormatter.cs:line 192
at ZLogger.ZLoggerEntry`1.FormatUtf8(IBufferWriter`1 writer, IZLoggerFormatter formatter) in C:\workspace\ZLogger\src\ZLogger\ZLoggerEntry.cs:line 76
at ZLogger.AsyncStreamLineMessageWriter.WriteLoop() in C:\workspace\ZLogger\src\ZLogger\AsyncStreamLineMessageWriter.cs:line 125
I run into this same issue when trying to use the json formatter. Been meaning to compare the approach here with Serilog's. Maybe need something like in JsonValueFormatter
I figured out that if you disable serialization of the logs State property (information, that was merged into the message), then it doesn't corrupt the logs.
.AddZLoggerConsole(o =>
{
o.IncludeScopes = true;
o.InternalErrorLogger = ex => Console.Error.WriteLine($"ZLogger error: {ex}");
o.UseJsonFormatter(f =>
{
f.IncludeProperties &= ~IncludeProperties.ParameterKeyValues; // This prevents the broken log messages
});
});
I made it reproducible with a little sample app here: https://github.com/saithis/ZLogger/commit/3cb6d471ffcf67dac4efbfd3e2ec9602de0f930e Unfortunatelly I'm on vacation for 3 weeks starting in a few hours, so I won't be able to continue to investigate further any time soon.
I made it reproducible with a little sample app here: saithis@3cb6d47 Unfortunatelly I'm on vacation for 3 weeks starting in a few hours, so I won't be able to continue to investigate further any time soon.
let me try to fix this issue
We wanted to switch to ZLogger, but now immediatelly encountered this issue after trying it out:
ZLogger error: System.NotSupportedException: Serialization and deserialization of 'System.Reflection.RuntimeMethodInfo' instances is not supported. The unsupported member type is located on type 'System.Object'. Path: $.Metadata. ---> System.NotSupportedException: Serialization and deserialization of 'System.Reflection.RuntimeMethodInfo' instances is not supported.
It just starts with the next log message without finishing the first one. But this is just a standard asp net core log message, so I expected ZLogger to be able to handle that. Our ZLogger config is like this:hostBuilder.ConfigureLogging(builder => builder .ClearProviders() .AddZLoggerConsole(o => { o.IncludeScopes = true; o.InternalErrorLogger = ex => Console.Error.WriteLine($"ZLogger error: {ex}"); o.UseJsonFormatter(formatter => { formatter.JsonSerializerOptions.WriteIndented = true; }); }));And since it is exactly after the User property each time, maybe this is also important:app.Use(async (ctx, next) => { var logger = ctx.RequestServices.GetRequiredService<ILogger<Program>>(); using var loggerScope = logger.BeginScope(new Dictionary<string, object> { {"User", ctx.User.FindFirst("sub")?.Value ?? "anonymous" }, }); await next(); });
@saithis, you need to add a JSON converter for Microsoft.AspNetCore.Routing.RouteEndpoint Resolving Codes as follows
builder.Logging
.ClearProviders()
.AddZLoggerConsole(o =>
{
o.IncludeScopes = true;
o.InternalErrorLogger = ex => Console.Error.WriteLine($"ZLogger error: {ex}");
o.UseJsonFormatter(f =>
{
f.JsonSerializerOptions.WriteIndented = true;
//f.IncludeProperties &= ~IncludeProperties.ParameterKeyValues; // This prevents the broken log messages
f.JsonSerializerOptions.Converters.Add(new RouteEndpointConverter());
});
});
public class RouteEndpointConverter : JsonConverter<Microsoft.AspNetCore.Routing.RouteEndpoint>
{
public override Microsoft.AspNetCore.Routing.RouteEndpoint? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotSupportedException();
}
public override void Write(Utf8JsonWriter writer, Microsoft.AspNetCore.Routing.RouteEndpoint value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
}
}
hmm, while experimenting I also encountered this quite frequently:
ZLogger error: System.ObjectDisposedException: IFeatureCollection has been disposed. Object name: 'Collection'. at Microsoft.AspNetCore.Http.Features.FeatureReferences`1.ThrowContextDisposed() at Microsoft.AspNetCore.Http.Features.FeatureReferences`1.ContextDisposed() at Microsoft.AspNetCore.Http.Features.FeatureReferences`1.Fetch[TFeature](TFeature& cached, Func`2 factory) at Microsoft.AspNetCore.Http.DefaultHttpResponse.get_StatusCode() at Microsoft.AspNetCore.Hosting.HostingRequestFinishedLog.get_Item(Int32 index) at ZLogger.LogStates.StringFormatterLogState`1.WriteJsonParameterKeyValues(Utf8JsonWriter jsonWriter, JsonSerializerOptions jsonSerializerOptions, IKeyNameMutator keyNameMutator) in C:\workspace\ZLogger\src\ZLogger\LogStates\StringFormatterLogState.cs:line 94 at ZLogger.ZLoggerEntry`1.WriteJsonParameterKeyValues(Utf8JsonWriter jsonWriter, JsonSerializerOptions jsonSerializerOptions, IKeyNameMutator keyNameMutator) in C:\workspace\ZLogger\src\ZLogger\ZLoggerEntry.cs:line 70 at ZLogger.Formatters.SystemTextJsonZLoggerFormatter.FormatLogEntry(IBufferWriter`1 writer, IZLoggerEntry entry) in C:\workspace\ZLogger\src\ZLogger\Formatters\SystemTextJsonZLoggerFormatter.cs:line 192 at ZLogger.ZLoggerEntry`1.FormatUtf8(IBufferWriter`1 writer, IZLoggerFormatter formatter) in C:\workspace\ZLogger\src\ZLogger\ZLoggerEntry.cs:line 76 at ZLogger.AsyncStreamLineMessageWriter.WriteLoop() in C:\workspace\ZLogger\src\ZLogger\AsyncStreamLineMessageWriter.cs:line 125
@saithis I have resolved this bug at PR
tl;dr I'm not confident zlogger should change, you can choose your own json behaviour
I came to this wanting to send logs to seq, so I copied the sample clef formatter, which is a nice example of how to implement an IZLoggerFormatter. It has this line, which ends up invoking StringFormatterLogState:
entry.WriteJsonParameterKeyValues(jsonWriter, JsonSerializerOptions);
It loops over the message template parameters like you'd expect. But then there's a decision to make! How do you serialize each message template parameter value? The choice made by StringFormatterLogState and SystemTextJsonZLoggerFormatter is to serialize the template value as json:
var valueType = GetParameterType(i);
JsonSerializer.Serialize(jsonWriter, x.Value, valueType, jsonSerializerOptions);
This is not usually what I want. I take care to only use simple types in template parameters, but the libraries I use don't. Sometimes the objects being used can't be serialized as json without writing custom converters. When I bungle the binding of a string to a TextBlock in Avalonia, it logs a warning, and the StyledProperty is passed as a template parameter. That type has properties that aren't serializable by default. And so I get:
System.NotSupportedException: Serialization and deserialization of 'System.Type' instances is not supported. Path: $.PropertyType.
---> System.NotSupportedException: Serialization and deserialization of 'System.Type' instances is not supported.
at System.Text.Json.Serialization.Converters.UnsupportedTypeConverter`1.Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
<snip>
at ZLogger.LogStates.StringFormatterLogState`1.WriteJsonParameterKeyValues(Utf8JsonWriter jsonWriter, JsonSerializerOptions jsonSerializerOptions, IKeyNameMutator keyNameMutator)
<snip>
What I want — possibly always — is for simple stringification of template parameters. When I have a more complex object I want to shove in a log event, I use scope state properties. So I changed the sample clef formatter. This needs to be improved quite a bit to avoid boxing ints and the like. But it's roughly how I want template parameters to be logged. You could copy/paste/adapt the SystemTextJsonZLoggerFormatter in the exact same way.
// Parameters
- entry.WriteJsonParameterKeyValues(jsonWriter, JsonSerializerOptions);
+ for (int i = 0; i < entry.ParameterCount; i++)
+ {
+ var k = entry.GetParameterKeyAsString(i);
+ var v = entry.GetParameterValue(i);
+
+ switch (v)
+ {
+ case null:
+ jsonWriter.WriteNull(k);
+ break;
+ case string:
+ jsonWriter.WriteString(k, (string)v);
+ break;
+ default:
+ jsonWriter.WriteString(k, v.ToString());
+ break;
+ }
+ }
This is the approach Serilog takes, except Serilog writes json with a TextWriter instead of a Utf8JsonWriter. And Serilog has special syntax for when to json a template parameter instead of stringing it.
@djeikyb yep, your solution can resolve the first problem of this issue, another problem about the ** System.ObjectDisposedException ** can not be resolved