From 753524882a6915c2f15681555845025fd9b4e854 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Tue, 19 Nov 2024 10:31:57 +0800 Subject: [PATCH 1/2] Fix error adding log entry with no body --- .../Otlp/Model/OtlpLogEntry.cs | 4 +- .../TelemetryRepositoryTests/LogTests.cs | 41 +++++++++++++++++++ .../Shared/Telemetry/TelemetryTestHelpers.cs | 4 +- 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/Aspire.Dashboard/Otlp/Model/OtlpLogEntry.cs b/src/Aspire.Dashboard/Otlp/Model/OtlpLogEntry.cs index d725cc02d7..7e8c48ea83 100644 --- a/src/Aspire.Dashboard/Otlp/Model/OtlpLogEntry.cs +++ b/src/Aspire.Dashboard/Otlp/Model/OtlpLogEntry.cs @@ -52,7 +52,9 @@ public OtlpLogEntry(LogRecord record, OtlpApplicationView logApp, OtlpScope scop Flags = record.Flags; Severity = MapSeverity(record.SeverityNumber); - Message = OtlpHelpers.TruncateString(record.Body.GetString(), context.Options.MaxAttributeLength); + Message = record.Body is { } body + ? OtlpHelpers.TruncateString(body.GetString(), context.Options.MaxAttributeLength) + : string.Empty; OriginalFormat = originalFormat; SpanId = record.SpanId.ToHexString(); TraceId = record.TraceId.ToHexString(); diff --git a/tests/Aspire.Dashboard.Tests/TelemetryRepositoryTests/LogTests.cs b/tests/Aspire.Dashboard.Tests/TelemetryRepositoryTests/LogTests.cs index 5d30846c64..324325444e 100644 --- a/tests/Aspire.Dashboard.Tests/TelemetryRepositoryTests/LogTests.cs +++ b/tests/Aspire.Dashboard.Tests/TelemetryRepositoryTests/LogTests.cs @@ -90,6 +90,47 @@ public void AddLogs() s => Assert.Equal("Log", s)); } + [Fact] + public void AddLogs_NoBody_EmptyMessage() + { + // Arrange + var repository = CreateRepository(); + + // Act + var addContext = new AddContext(); + repository.AddLogs(addContext, new RepeatedField() + { + new ResourceLogs + { + Resource = CreateResource(), + ScopeLogs = + { + new ScopeLogs + { + Scope = CreateScope("TestLogger"), + LogRecords = { CreateLogRecord(skipBody: true) } + } + } + } + }); + + // Assert + Assert.Equal(0, addContext.FailureCount); + + var logs = repository.GetLogs(new GetLogsContext + { + ApplicationKey = null, + StartIndex = 0, + Count = 10, + Filters = [] + }); + Assert.Collection(logs.Items, + app => + { + Assert.Equal("", app.Message); + }); + } + [Fact] public void AddLogs_MultipleOutOfOrder() { diff --git a/tests/Shared/Telemetry/TelemetryTestHelpers.cs b/tests/Shared/Telemetry/TelemetryTestHelpers.cs index b6d6a76f5c..9460c7ceb3 100644 --- a/tests/Shared/Telemetry/TelemetryTestHelpers.cs +++ b/tests/Shared/Telemetry/TelemetryTestHelpers.cs @@ -174,13 +174,13 @@ public static Span CreateSpan(string traceId, string spanId, DateTime startTime, return span; } - public static LogRecord CreateLogRecord(DateTime? time = null, string? message = null, SeverityNumber? severity = null, IEnumerable>? attributes = null) + public static LogRecord CreateLogRecord(DateTime? time = null, string? message = null, SeverityNumber? severity = null, IEnumerable>? attributes = null, bool? skipBody = null) { attributes ??= [new KeyValuePair("{OriginalFormat}", "Test {Log}"), new KeyValuePair("Log", "Value!")]; var logRecord = new LogRecord { - Body = new AnyValue { StringValue = message ?? "Test Value!" }, + Body = (skipBody ?? false) ? null : new AnyValue { StringValue = message ?? "Test Value!" }, TraceId = ByteString.CopyFrom(Convert.FromHexString("5465737454726163654964")), SpanId = ByteString.CopyFrom(Convert.FromHexString("546573745370616e4964")), TimeUnixNano = time != null ? DateTimeToUnixNanoseconds(time.Value) : 1000, From 2454b2ebfdfa08906cd0989b1be3ced5f2b1202e Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 22 Nov 2024 14:03:54 +0800 Subject: [PATCH 2/2] Add manual test code --- .../Stress/Stress.ApiService/Program.cs | 56 +++++++++++++++++++ .../Stress.ApiService.csproj | 7 +++ 2 files changed, 63 insertions(+) diff --git a/playground/Stress/Stress.ApiService/Program.cs b/playground/Stress/Stress.ApiService/Program.cs index 4b408bd51d..7cb7b3803f 100644 --- a/playground/Stress/Stress.ApiService/Program.cs +++ b/playground/Stress/Stress.ApiService/Program.cs @@ -4,7 +4,13 @@ using System.Diagnostics; using System.Text; using System.Threading.Channels; +using Grpc.Core; +using Grpc.Net.Client; using Microsoft.AspNetCore.Mvc; +using OpenTelemetry.Proto.Collector.Logs.V1; +using OpenTelemetry.Proto.Common.V1; +using OpenTelemetry.Proto.Logs.V1; +using OpenTelemetry.Proto.Resource.V1; using Stress.ApiService; var builder = WebApplication.CreateBuilder(args); @@ -225,4 +231,54 @@ async IAsyncEnumerable WriteOutput() return $"Created duplicate span IDs."; }); +app.MapGet("/log-entry-nobody", async (IConfiguration configuration) => +{ + var channel = GrpcChannel.ForAddress(configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]!); + + var resource = new Resource(); + resource.Attributes.Add(new KeyValue { Key = "service.name", Value = new AnyValue { StringValue = configuration["OTEL_SERVICE_NAME"]! } }); + + foreach (var item in configuration["OTEL_RESOURCE_ATTRIBUTES"]!.Split(';')) + { + var headerParts = item.Split('='); + + resource.Attributes.Add(new KeyValue { Key = headerParts[0], Value = new AnyValue { StringValue = headerParts[1] } }); + } + + var metadata = new Metadata(); + foreach (var item in configuration["OTEL_EXPORTER_OTLP_HEADERS"]!.Split(';')) + { + var headerParts = item.Split('='); + + metadata.Add(headerParts[0], headerParts[1]); + } + + var client = new LogsService.LogsServiceClient(channel); + var response = await client.ExportAsync(new ExportLogsServiceRequest + { + ResourceLogs = + { + new ResourceLogs + { + Resource = resource, + ScopeLogs = + { + new ScopeLogs + { + Scope = new InstrumentationScope { Name = "NoBodySource" }, + LogRecords = + { + new LogRecord + { + } + } + } + } + } + } + }, metadata); + + return response.PartialSuccess?.RejectedLogRecords > 0 ? "Failure" : "Success"; +}); + app.Run(); diff --git a/playground/Stress/Stress.ApiService/Stress.ApiService.csproj b/playground/Stress/Stress.ApiService/Stress.ApiService.csproj index a1e9c07d5b..f6f2241049 100644 --- a/playground/Stress/Stress.ApiService/Stress.ApiService.csproj +++ b/playground/Stress/Stress.ApiService/Stress.ApiService.csproj @@ -16,4 +16,11 @@ + + + + ..\..\..\src\Aspire.Dashboard\Otlp + + +