From 274dc440e085155ad80890a83be6af619e4d86a2 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Fri, 17 Jun 2022 21:30:56 -0700 Subject: [PATCH] Logs: Add LogRecordData (#3378) * Add LogRecordData and hook up to LogRecord. * CHANGELOG update. * Code review. --- .../.publicApi/net462/PublicAPI.Unshipped.txt | 9 ++ .../.publicApi/net6.0/PublicAPI.Unshipped.txt | 9 ++ .../netstandard2.0/PublicAPI.Unshipped.txt | 9 ++ .../netstandard2.1/PublicAPI.Unshipped.txt | 9 ++ src/OpenTelemetry/CHANGELOG.md | 5 + src/OpenTelemetry/Logs/LogRecord.cs | 129 +++++++++++------ src/OpenTelemetry/Logs/LogRecordData.cs | 135 ++++++++++++++++++ 7 files changed, 263 insertions(+), 42 deletions(-) create mode 100644 src/OpenTelemetry/Logs/LogRecordData.cs diff --git a/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt index 7ead6b2aa97..3f5fd985f93 100644 --- a/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt @@ -1,3 +1,12 @@ +OpenTelemetry.Logs.LogRecord.CategoryName.set -> void +OpenTelemetry.Logs.LogRecord.EventId.set -> void +OpenTelemetry.Logs.LogRecord.Exception.set -> void +OpenTelemetry.Logs.LogRecord.LogLevel.set -> void +OpenTelemetry.Logs.LogRecord.SpanId.set -> void +OpenTelemetry.Logs.LogRecord.Timestamp.set -> void +OpenTelemetry.Logs.LogRecord.TraceFlags.set -> void +OpenTelemetry.Logs.LogRecord.TraceId.set -> void +OpenTelemetry.Logs.LogRecord.TraceState.set -> void OpenTelemetry.Logs.OpenTelemetryLoggerProvider.ForceFlush(int timeoutMilliseconds = -1) -> bool OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider() -> void OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider(System.Action! configure) -> void \ No newline at end of file diff --git a/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt index 7ead6b2aa97..3f5fd985f93 100644 --- a/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt @@ -1,3 +1,12 @@ +OpenTelemetry.Logs.LogRecord.CategoryName.set -> void +OpenTelemetry.Logs.LogRecord.EventId.set -> void +OpenTelemetry.Logs.LogRecord.Exception.set -> void +OpenTelemetry.Logs.LogRecord.LogLevel.set -> void +OpenTelemetry.Logs.LogRecord.SpanId.set -> void +OpenTelemetry.Logs.LogRecord.Timestamp.set -> void +OpenTelemetry.Logs.LogRecord.TraceFlags.set -> void +OpenTelemetry.Logs.LogRecord.TraceId.set -> void +OpenTelemetry.Logs.LogRecord.TraceState.set -> void OpenTelemetry.Logs.OpenTelemetryLoggerProvider.ForceFlush(int timeoutMilliseconds = -1) -> bool OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider() -> void OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider(System.Action! configure) -> void \ No newline at end of file diff --git a/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt index 7ead6b2aa97..3f5fd985f93 100644 --- a/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -1,3 +1,12 @@ +OpenTelemetry.Logs.LogRecord.CategoryName.set -> void +OpenTelemetry.Logs.LogRecord.EventId.set -> void +OpenTelemetry.Logs.LogRecord.Exception.set -> void +OpenTelemetry.Logs.LogRecord.LogLevel.set -> void +OpenTelemetry.Logs.LogRecord.SpanId.set -> void +OpenTelemetry.Logs.LogRecord.Timestamp.set -> void +OpenTelemetry.Logs.LogRecord.TraceFlags.set -> void +OpenTelemetry.Logs.LogRecord.TraceId.set -> void +OpenTelemetry.Logs.LogRecord.TraceState.set -> void OpenTelemetry.Logs.OpenTelemetryLoggerProvider.ForceFlush(int timeoutMilliseconds = -1) -> bool OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider() -> void OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider(System.Action! configure) -> void \ No newline at end of file diff --git a/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt index 7ead6b2aa97..3f5fd985f93 100644 --- a/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/netstandard2.1/PublicAPI.Unshipped.txt @@ -1,3 +1,12 @@ +OpenTelemetry.Logs.LogRecord.CategoryName.set -> void +OpenTelemetry.Logs.LogRecord.EventId.set -> void +OpenTelemetry.Logs.LogRecord.Exception.set -> void +OpenTelemetry.Logs.LogRecord.LogLevel.set -> void +OpenTelemetry.Logs.LogRecord.SpanId.set -> void +OpenTelemetry.Logs.LogRecord.Timestamp.set -> void +OpenTelemetry.Logs.LogRecord.TraceFlags.set -> void +OpenTelemetry.Logs.LogRecord.TraceId.set -> void +OpenTelemetry.Logs.LogRecord.TraceState.set -> void OpenTelemetry.Logs.OpenTelemetryLoggerProvider.ForceFlush(int timeoutMilliseconds = -1) -> bool OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider() -> void OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider(System.Action! configure) -> void \ No newline at end of file diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index b1d54a823ca..3b4cc4c70fe 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -17,6 +17,11 @@ * Added `ForceFlush` and helper ctors on `OpenTelemetryLoggerProvider` ([#3364](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3364)) +* `Timestamp`, `TraceId`, `SpanId`, `TraceFlags`, `TraceState`, `CategoryName`, + `LogLevel`, `EventId`, & `Exception` properties on `LogRecord` now expose + `set` methods + ([#3378](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3378)) + ## 1.3.0 Released 2022-Jun-03 diff --git a/src/OpenTelemetry/Logs/LogRecord.cs b/src/OpenTelemetry/Logs/LogRecord.cs index a753d9c4b4e..98c937e7244 100644 --- a/src/OpenTelemetry/Logs/LogRecord.cs +++ b/src/OpenTelemetry/Logs/LogRecord.cs @@ -29,6 +29,8 @@ namespace OpenTelemetry.Logs /// public sealed class LogRecord { + internal LogRecordData Data; + private static readonly Action> AddScopeToBufferedList = (object? scope, List state) => { state.Add(scope); @@ -36,6 +38,7 @@ public sealed class LogRecord private List? bufferedScopes; + // Note: Some users are calling this with reflection. Try not to change the signature to be nice. internal LogRecord( IExternalScopeProvider? scopeProvider, DateTime timestamp, @@ -47,71 +50,107 @@ internal LogRecord( Exception? exception, IReadOnlyList>? stateValues) { - this.ScopeProvider = scopeProvider; - - var activity = Activity.Current; - if (activity != null) + this.Data = new(Activity.Current) { - this.TraceId = activity.TraceId; - this.SpanId = activity.SpanId; - this.TraceState = activity.TraceStateString; - this.TraceFlags = activity.ActivityTraceFlags; - } + TimestampBacking = timestamp, - this.Timestamp = timestamp; - this.CategoryName = categoryName; - this.LogLevel = logLevel; - this.EventId = eventId; - this.FormattedMessage = formattedMessage; - this.State = state; + CategoryName = categoryName, + LogLevel = logLevel, + EventId = eventId, + Message = formattedMessage, + Exception = exception, + }; + + this.ScopeProvider = scopeProvider; this.StateValues = stateValues; - this.Exception = exception; + this.State = state; } /// - /// Gets the log timestamp. + /// Gets or sets the log timestamp. /// - public DateTime Timestamp { get; } + /// + /// Note: If is set to a value with it will be automatically converted to + /// UTC using . + /// + public DateTime Timestamp + { + get => this.Data.Timestamp; + set => this.Data.Timestamp = value; + } /// - /// Gets the log . + /// Gets or sets the log . /// - public ActivityTraceId TraceId { get; } + public ActivityTraceId TraceId + { + get => this.Data.TraceId; + set => this.Data.TraceId = value; + } /// - /// Gets the log . + /// Gets or sets the log . /// - public ActivitySpanId SpanId { get; } + public ActivitySpanId SpanId + { + get => this.Data.SpanId; + set => this.Data.SpanId = value; + } /// - /// Gets the log . + /// Gets or sets the log . /// - public ActivityTraceFlags TraceFlags { get; } + public ActivityTraceFlags TraceFlags + { + get => this.Data.TraceFlags; + set => this.Data.TraceFlags = value; + } /// - /// Gets the log trace state. + /// Gets or sets the log trace state. /// - public string? TraceState { get; } + public string? TraceState + { + get => this.Data.TraceState; + set => this.Data.TraceState = value; + } /// - /// Gets the log category name. + /// Gets or sets the log category name. /// - public string? CategoryName { get; } + public string? CategoryName + { + get => this.Data.CategoryName; + set => this.Data.CategoryName = value; + } /// - /// Gets the log . + /// Gets or sets the log . /// - public LogLevel LogLevel { get; } + public LogLevel LogLevel + { + get => this.Data.LogLevel; + set => this.Data.LogLevel = value; + } /// - /// Gets the log . + /// Gets or sets the log . /// - public EventId EventId { get; } + public EventId EventId + { + get => this.Data.EventId; + set => this.Data.EventId = value; + } /// /// Gets or sets the log formatted message. /// - public string? FormattedMessage { get; set; } + public string? FormattedMessage + { + get => this.Data.Message; + set => this.Data.Message = value; + } /// /// Gets or sets the raw state attached to the log. Set to >? StateValues { get; set; } /// - /// Gets the log . + /// Gets or sets the log . /// - public Exception? Exception { get; } + public Exception? Exception + { + get => this.Data.Exception; + set => this.Data.Exception = value; + } internal IExternalScopeProvider? ScopeProvider { get; set; } @@ -139,13 +182,6 @@ internal LogRecord( /// of creation. All callbacks are guaranteed to be called inline from /// this method. /// - /// - /// Note: Scopes are only available during the lifecycle of the log - /// message being written. If you need to capture scopes to be used - /// later (for example in batching scenarios), call to safely capture the values (incurs - /// allocation). - /// /// State. /// The callback to be executed for every scope object. /// The state object to be passed into the callback. @@ -168,6 +204,15 @@ public void ForEachScope(Action callback, TState } } + /// + /// Gets a reference to the for the log message. + /// + /// . + internal ref LogRecordData GetDataRef() + { + return ref this.Data; + } + /// /// Buffers the scopes attached to the log into a list so that they can /// be safely processed after the log message lifecycle has ended. diff --git a/src/OpenTelemetry/Logs/LogRecordData.cs b/src/OpenTelemetry/Logs/LogRecordData.cs new file mode 100644 index 00000000000..c2411c74d52 --- /dev/null +++ b/src/OpenTelemetry/Logs/LogRecordData.cs @@ -0,0 +1,135 @@ +// +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#nullable enable + +using System; +using System.Diagnostics; +using Microsoft.Extensions.Logging; + +namespace OpenTelemetry.Logs +{ + /// + /// Stores details about a log message. + /// + internal struct LogRecordData + { + internal DateTime TimestampBacking = DateTime.UtcNow; + + /// + /// Initializes a new instance of the struct. + /// + /// + /// Note: The property is initialized to automatically. + /// + /// Optional used to populate context fields. + public LogRecordData(Activity? activity = null) + { + if (activity != null) + { + this.TraceId = activity.TraceId; + this.SpanId = activity.SpanId; + this.TraceState = activity.TraceStateString; + this.TraceFlags = activity.ActivityTraceFlags; + } + else + { + this.TraceId = default; + this.SpanId = default; + this.TraceState = null; + this.TraceFlags = ActivityTraceFlags.None; + } + } + + /// + /// Gets or sets the log timestamp. + /// + /// + /// Note: If is set to a value with it will be automatically converted to + /// UTC using . + /// + public DateTime Timestamp + { + readonly get => this.TimestampBacking; + set { this.TimestampBacking = value.Kind == DateTimeKind.Local ? value.ToUniversalTime() : value; } + } + + /// + /// Gets or sets the log . + /// + public ActivityTraceId TraceId { get; set; } + + /// + /// Gets or sets the log . + /// + public ActivitySpanId SpanId { get; set; } + + /// + /// Gets or sets the log . + /// + public ActivityTraceFlags TraceFlags { get; set; } + + /// + /// Gets or sets the log trace state. + /// + public string? TraceState { get; set; } + + /// + /// Gets or sets the log category name. + /// + public string? CategoryName { get; set; } = null; + + /// + /// Gets or sets the log . + /// + public LogLevel LogLevel { get; set; } = LogLevel.Trace; + + /// + /// Gets or sets the log . + /// + public EventId EventId { get; set; } = default; + + /// + /// Gets or sets the log message. + /// + public string? Message { get; set; } = null; + + /// + /// Gets or sets the log . + /// + public Exception? Exception { get; set; } = null; + + internal static void SetActivityContext(ref LogRecordData data, Activity? activity = null) + { + if (activity != null) + { + data.TraceId = activity.TraceId; + data.SpanId = activity.SpanId; + data.TraceState = activity.TraceStateString; + data.TraceFlags = activity.ActivityTraceFlags; + } + else + { + data.TraceId = default; + data.SpanId = default; + data.TraceState = null; + data.TraceFlags = ActivityTraceFlags.None; + } + } + } +}