-
Notifications
You must be signed in to change notification settings - Fork 831
Description
Question
Use Github Discussions.
Currently when using Logging attributes in Scope and LogMessage I observed, that the dotnet exporter exports every attribute not just the latest occurrence.
using (logger.BeginScope(new[] { new KeyValuePair<string, object>("fooAttr", "abc - scope0") })) // scope 0
using (logger.BeginScope(new[] { new KeyValuePair<string, object>("fooAttr", "def - scope1") })) // scope 1
{
logger.LogInformation("Hello Log Message: '{fooAttr}' '{barAttr}'", "ghi - log msg", "barAttrValue");
}
In this example fooAttr
is added 3 times to the attribute list
Output of ConsoleLogExporter:
LogRecord.Timestamp: 2023-03-22T12:44:02.5096056Z LogRecord.CategoryName: Program LogRecord.LogLevel: Information LogRecord.FormattedMessage: Hello Log Message: 'ghi - log msg' 'barAttrValue' LogRecord.StateValues (Key:Value): fooAttr: ghi - log msg barAttr: barAttrValue OriginalFormat (a.k.a Body): Hello Log Message: '{fooAttr}' '{barAttr}' LogRecord.ScopeValues (Key:Value): [Scope.0]:fooAttr: abc - scope0 [Scope.1]:fooAttr: def - scope1
OpenTelemetry Collector receives list with duplicate attribute keys:
Timestamp: 2023-03-22 12:44:02.5096056 +0000 UTC SeverityText: Information SeverityNumber: Info(9) Body: Str(Hello Log Message: 'ghi - log msg' 'barAttrValue') Attributes: -> dotnet.ilogger.category: Str(Program) -> fooAttr: Str(ghi - log msg) -> barAttr: Str(barAttrValue) -> {OriginalFormat}: Str(Hello Log Message: '{fooAttr}' '{barAttr}') -> fooAttr: Str(abc - scope0) -> fooAttr: Str(def - scope1) Trace ID: Span ID: Flags: 0
This could be an unintentional side effect from the removal of the scope depth prefix (#3843).
It seems that during serialization, first all attributes from the log message then all attributes from the scopes are serialized (beginning with the oldest scope). But no deduplication is done during this process.
The specification describes Log-Attributes to be a 'map<string, any>' where attribute keys are unique (data-model.md). Currently the actual output seems to be only a serialized list.
Is this behavior intended and the specification incomplete or is this behavior a Bug?
Additional Context
Depending on a receiving backend tool the attribute list may be interpreted differently. I.e. in Loki only the last occurrence of an attribute is parsed -> fooAttr
is def - scope1
instead of ghi - log msg
Reproduce
Using net6.0
and the following OpenTelemetry NuGet-Packages
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.4.0" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.4.0" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol.Logs" Version="1.4.0-rc.4" />
// See https://aka.ms/new-console-template for more information
using Microsoft.Extensions.Logging;
using OpenTelemetry.Exporter;
using OpenTelemetry.Logs;
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddOpenTelemetry(options =>
{
options.AddConsoleExporter();
options.AddOtlpExporter(o =>
{
o.Protocol = OtlpExportProtocol.Grpc;
o.HttpClientFactory = () =>
{
var client = new HttpClient();
return client;
};
o.Endpoint = new Uri("http://localhost:4317/v1/logs");
});
options.IncludeScopes = true;
options.ParseStateValues = true;
options.IncludeFormattedMessage = true;
});
});
var logger = loggerFactory.CreateLogger<Program>();
using (logger.BeginScope(new[] { new KeyValuePair<string, object>("fooAttr", "abc - scope0") })) // scope 0
using (logger.BeginScope(new[] { new KeyValuePair<string, object>("fooAttr", "def - scope1") })) // scope 1
{
logger.LogInformation("Hello Log Message: '{fooAttr}' '{barAttr}'", "ghi - log msg", "barAttrValue");
}