diff --git a/src/NServiceBus.Core.Tests/ApprovalFiles/DiagnosticsWriterTests.ShouldWriteEntriesWithTypesUsingTheFullName.approved.txt b/src/NServiceBus.Core.Tests/ApprovalFiles/DiagnosticsWriterTests.ShouldWriteEntriesWithTypesUsingTheFullName.approved.txt new file mode 100644 index 00000000000..e1642480c52 --- /dev/null +++ b/src/NServiceBus.Core.Tests/ApprovalFiles/DiagnosticsWriterTests.ShouldWriteEntriesWithTypesUsingTheFullName.approved.txt @@ -0,0 +1 @@ +{"TypeIndicator":{"SomeType":"NServiceBus.Core.Tests.OpenTelemetry.DiagnosticsWriterTests"}} \ No newline at end of file diff --git a/src/NServiceBus.Core.Tests/OpenTelemetry/DiagnosticsWriterTests.cs b/src/NServiceBus.Core.Tests/OpenTelemetry/DiagnosticsWriterTests.cs index bbb9ce64ae6..e3e5685276a 100644 --- a/src/NServiceBus.Core.Tests/OpenTelemetry/DiagnosticsWriterTests.cs +++ b/src/NServiceBus.Core.Tests/OpenTelemetry/DiagnosticsWriterTests.cs @@ -29,5 +29,24 @@ public async Task ShouldWriteWhenDuplicateEntriesPresent() Approver.Verify(output); } + + [Test] + public async Task ShouldWriteEntriesWithTypesUsingTheFullName() + { + var output = string.Empty; + var testWriter = new Func((diagnosticOutput, _) => + { + output = diagnosticOutput; + return Task.CompletedTask; + }); + var diagnostics = new StartupDiagnosticEntries(); + diagnostics.Add("TypeIndicator", new { SomeType = typeof(DiagnosticsWriterTests) }); + + var writer = new HostStartupDiagnosticsWriter(testWriter, true); + + await writer.Write(diagnostics.entries); + + Approver.Verify(output); + } } } \ No newline at end of file diff --git a/src/NServiceBus.Core/Hosting/StartupDiagnostics/HostStartupDiagnosticsWriter.cs b/src/NServiceBus.Core/Hosting/StartupDiagnostics/HostStartupDiagnosticsWriter.cs index 4af9a7aa179..2cb6c51ef2e 100644 --- a/src/NServiceBus.Core/Hosting/StartupDiagnostics/HostStartupDiagnosticsWriter.cs +++ b/src/NServiceBus.Core/Hosting/StartupDiagnostics/HostStartupDiagnosticsWriter.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.Json; + using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; using Logging; @@ -27,7 +28,7 @@ public async Task Write(List en try { - data = JsonSerializer.Serialize(dictionary); + data = JsonSerializer.Serialize(dictionary, diagnosticsOptions); } catch (Exception exception) { @@ -80,6 +81,26 @@ await diagnosticsWriter(data, cancellationToken) readonly Func diagnosticsWriter; readonly bool isCustomWriter; + static readonly JsonSerializerOptions diagnosticsOptions = new() + { + Converters = { new TypeConverter() } + }; + + /// + /// By default System.Text.Json would throw with "Serialization and deserialization of 'System.Type' instances are not supported" which normally + /// would make sense because it can be considered unsafe to serialize and deserialize types. We add a custom converter here to make + /// sure when diagnostics entries accidentally use types it will just print the full name as a string. We never intent to read these things + /// back so this is a safe approach. + /// + sealed class TypeConverter : JsonConverter + { + // we never need to deserialize + public override Type Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException(); + + public override void Write(Utf8JsonWriter writer, Type value, JsonSerializerOptions options) => writer.WriteStringValue(value.FullName); + } + + static readonly ILog logger = LogManager.GetLogger(); } } \ No newline at end of file