| File: Otlp\Model\OtlpLogEntry.cs | Web Access |
| Project: src\src\Aspire.Dashboard\Aspire.Dashboard.csproj (Aspire.Dashboard) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; using System.Globalization; using Aspire.Dashboard.Model.Otlp; namespace Aspire.Dashboard.Otlp.Model; [DebuggerDisplay("InternalId = {InternalId}, TimeStamp = {TimeStamp}, Severity = {Severity}, Message = {Message}")] public class OtlpLogEntry { public KeyValuePair<string, string>[] Attributes { get; } public DateTime TimeStamp { get; } public uint Flags { get; } public LogLevel Severity { get; } public int SeverityNumber { get; } public string Message { get; } public string SpanId { get; } public string TraceId { get; } public string ParentId { get; } public string? OriginalFormat { get; } public OtlpResourceView ResourceView { get; } public OtlpScope Scope { get; } public long InternalId { get; } public string? EventName { get; } public bool IsError => Severity is LogLevel.Error or LogLevel.Critical; public bool IsWarning => Severity is LogLevel.Warning; internal OtlpLogEntry( long internalId, DateTime timeStamp, uint flags, LogLevel severity, int severityNumber, string message, string spanId, string traceId, string parentId, string? originalFormat, OtlpResourceView resourceView, OtlpScope scope, KeyValuePair<string, string>[] attributes, string? eventName) { InternalId = internalId; TimeStamp = timeStamp; Flags = flags; Severity = severity; SeverityNumber = severityNumber; Message = message; SpanId = spanId; TraceId = traceId; ParentId = parentId; OriginalFormat = originalFormat; ResourceView = resourceView; Scope = scope; Attributes = attributes; EventName = eventName; } public static string? GetFieldValue(OtlpLogEntry log, string field) { return field switch { KnownStructuredLogFields.MessageField => log.Message, KnownStructuredLogFields.TraceIdField => log.TraceId, KnownStructuredLogFields.SpanIdField => log.SpanId, KnownStructuredLogFields.OriginalFormatField => log.OriginalFormat, KnownStructuredLogFields.CategoryField => log.Scope.Name, KnownStructuredLogFields.EventNameField => log.EventName, KnownStructuredLogFields.LevelField => log.Severity.ToString(), KnownStructuredLogFields.TimestampField => (log.TimeStamp.ToUniversalTime().Ticks / TimeSpan.TicksPerMillisecond).ToString(CultureInfo.InvariantCulture), KnownResourceFields.ServiceNameField => log.ResourceView.Resource.ResourceName, _ => log.Attributes.GetValue(field) }; } public const string ExceptionStackTraceField = "exception.stacktrace"; public const string ExceptionMessageField = "exception.message"; public const string ExceptionTypeField = "exception.type"; public static string? GetExceptionText(OtlpLogEntry logEntry) { // exception.stacktrace includes the exception message and type. // https://opentelemetry.io/docs/specs/semconv/attributes-registry/exception/ if (GetProperty(logEntry, ExceptionStackTraceField) is { Length: > 0 } stackTrace) { return stackTrace; } if (GetProperty(logEntry, ExceptionMessageField) is { Length: > 0 } message) { if (GetProperty(logEntry, ExceptionTypeField) is { Length: > 0 } type) { return $"{type}: {message}"; } return message; } return null; static string? GetProperty(OtlpLogEntry logEntry, string propertyName) { return logEntry.Attributes.GetValue(propertyName); } } }