当我使用 .NET 的 OpenTelemetry 包记录结构化对象时,复杂对象在 Aspire 仪表板中显示为 JSON 字符串。
当我使用 Serilog OpenTelemetrySink 记录复杂对象时,我看到类似的内容
我知道 OpenTelemetry 指定以这种格式发送复杂对象,但收集器应该将其解码回对象或至少是 JSON 字符串。
我试图弄清楚这是否是我的配置错误,或者是否是 OpenTelemetrySink 中的错误
我的 Serilog 配置
services.AddLogging(
loggingBuilder =>
{
//Create Serilog logger from AppSettings.json properties.
LoggerConfiguration loggingConfiguration = new LoggerConfiguration()
.ReadFrom.Configuration(configuration);
string? otelExporterEndpoint = Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"];
if (otelExporterEndpoint != null)
{
loggingConfiguration.WriteTo.OpenTelemetry(
opt =>
{
opt.Endpoint = otelExporterEndpoint;
opt.Protocol = OtlpProtocol.Grpc;
opt.ResourceAttributes = _otelResourceAttributes;
opt.OnBeginSuppressInstrumentation =
SuppressInstrumentationScope.Begin;
opt.IncludedData = IncludedData.TemplateBody | IncludedData.SpanIdField |
IncludedData.TraceIdField | IncludedData.SourceContextAttribute;
});
}
_logger = loggingConfiguration.CreateLogger();
loggingBuilder.AddSerilog(_logger, dispose: true);
// Enable selflog to debug serilog configuration
// SelfLog.Enable(Console.Error);
});
OtlpLogEntry
类型 使用 string
表示属性值:
public class OtlpLogEntry
{
public KeyValuePair<string, string>[] Attributes { get; }
...
转换入站 OTLP 属性时,无论属性类型如何,都使用
GetString()
:
var value = TruncateString(attribute.Value.GetString(), options.MaxAttributeLength);
GetString()
只是按照协议格式逐字字符串化结构化值:
AnyValue.ValueOneofCase.KvlistValue => ConvertAnyValue(value)!.ToJsonString(s_jsonSerializerOptions),
最好的修复方法是为 Aspire 的
OtlpLogEntry
类型添加对非字符串属性类型的支持。这需要对 dotnet/aspire
进行更改,但好处是类型化/结构化数据的用户体验会更好。
我对收集器行为差异的猜测是,这是因为许多 OpenTelemetry 数据源不会在事件属性中发出丰富的结构化值。例如。 OpenTelemetry .NET SDK 似乎还不支持在日志记录属性中向 OTLP 发送结构化对象,因此所有内容都会在客户端转换为字符串。其他地方的情况很可能也会类似。