Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -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<OpenTelemetry.Logs.OpenTelemetryLoggerOptions!>! configure) -> void
9 changes: 9 additions & 0 deletions src/OpenTelemetry/.publicApi/net6.0/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -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<OpenTelemetry.Logs.OpenTelemetryLoggerOptions!>! configure) -> void
Original file line number Diff line number Diff line change
@@ -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<OpenTelemetry.Logs.OpenTelemetryLoggerOptions!>! configure) -> void
Original file line number Diff line number Diff line change
@@ -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<OpenTelemetry.Logs.OpenTelemetryLoggerOptions!>! configure) -> void
5 changes: 5 additions & 0 deletions src/OpenTelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
124 changes: 82 additions & 42 deletions src/OpenTelemetry/Logs/LogRecord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,16 @@ namespace OpenTelemetry.Logs
/// </summary>
public sealed class LogRecord
{
internal LogRecordData Data;

private static readonly Action<object?, List<object?>> AddScopeToBufferedList = (object? scope, List<object?> state) =>
{
state.Add(scope);
};

private List<object?>? bufferedScopes;

// Note: Some users are calling this with reflection. Try not to change the signature to be nice.
internal LogRecord(
IExternalScopeProvider? scopeProvider,
DateTime timestamp,
Expand All @@ -47,71 +50,102 @@ internal LogRecord(
Exception? exception,
IReadOnlyList<KeyValuePair<string, object?>>? 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;
}

/// <summary>
/// Gets the log timestamp.
/// Gets or sets the log timestamp.
/// </summary>
public DateTime Timestamp { get; }
public DateTime Timestamp
{
get => this.Data.Timestamp;
set => this.Data.Timestamp = value;
}

/// <summary>
/// Gets the log <see cref="ActivityTraceId"/>.
/// Gets or sets the log <see cref="ActivityTraceId"/>.
/// </summary>
public ActivityTraceId TraceId { get; }
public ActivityTraceId TraceId
{
get => this.Data.TraceId;
set => this.Data.TraceId = value;
}

/// <summary>
/// Gets the log <see cref="ActivitySpanId"/>.
/// Gets or sets the log <see cref="ActivitySpanId"/>.
/// </summary>
public ActivitySpanId SpanId { get; }
public ActivitySpanId SpanId
{
get => this.Data.SpanId;
set => this.Data.SpanId = value;
}

/// <summary>
/// Gets the log <see cref="ActivityTraceFlags"/>.
/// Gets or sets the log <see cref="ActivityTraceFlags"/>.
/// </summary>
public ActivityTraceFlags TraceFlags { get; }
public ActivityTraceFlags TraceFlags
{
get => this.Data.TraceFlags;
set => this.Data.TraceFlags = value;
}

/// <summary>
/// Gets the log trace state.
/// Gets or sets the log trace state.
/// </summary>
public string? TraceState { get; }
public string? TraceState
{
get => this.Data.TraceState;
set => this.Data.TraceState = value;
}

/// <summary>
/// Gets the log category name.
/// Gets or sets the log category name.
/// </summary>
public string? CategoryName { get; }
public string? CategoryName
{
get => this.Data.CategoryName;
set => this.Data.CategoryName = value;
}

/// <summary>
/// Gets the log <see cref="Microsoft.Extensions.Logging.LogLevel"/>.
/// Gets or sets the log <see cref="Microsoft.Extensions.Logging.LogLevel"/>.
/// </summary>
public LogLevel LogLevel { get; }
public LogLevel LogLevel
{
get => this.Data.LogLevel;
set => this.Data.LogLevel = value;
}

/// <summary>
/// Gets the log <see cref="EventId"/>.
/// Gets or sets the log <see cref="Microsoft.Extensions.Logging.EventId"/>.
/// </summary>
public EventId EventId { get; }
public EventId EventId
{
get => this.Data.EventId;
set => this.Data.EventId = value;
}

/// <summary>
/// Gets or sets the log formatted message.
/// </summary>
public string? FormattedMessage { get; set; }
public string? FormattedMessage
{
get => this.Data.Message;
set => this.Data.Message = value;
}

/// <summary>
/// Gets or sets the raw state attached to the log. Set to <see
Expand All @@ -128,9 +162,13 @@ internal LogRecord(
public IReadOnlyList<KeyValuePair<string, object?>>? StateValues { get; set; }

/// <summary>
/// Gets the log <see cref="System.Exception"/>.
/// Gets or sets the log <see cref="System.Exception"/>.
/// </summary>
public Exception? Exception { get; }
public Exception? Exception
{
get => this.Data.Exception;
set => this.Data.Exception = value;
}

internal IExternalScopeProvider? ScopeProvider { get; set; }

Expand All @@ -139,13 +177,6 @@ internal LogRecord(
/// of creation. All callbacks are guaranteed to be called inline from
/// this method.
/// </summary>
/// <remarks>
/// 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 <see
/// cref="BufferLogScopes"/> to safely capture the values (incurs
/// allocation).
/// </remarks>
/// <typeparam name="TState">State.</typeparam>
/// <param name="callback">The callback to be executed for every scope object.</param>
/// <param name="state">The state object to be passed into the callback.</param>
Expand All @@ -168,6 +199,15 @@ public void ForEachScope<TState>(Action<LogRecordScope, TState> callback, TState
}
}

/// <summary>
/// Gets a reference to the <see cref="LogRecordData"/> for the log message.
/// </summary>
/// <returns><see cref="LogRecordData"/>.</returns>
internal ref LogRecordData GetDataRef()
{
return ref this.Data;
}

/// <summary>
/// Buffers the scopes attached to the log into a list so that they can
/// be safely processed after the log message lifecycle has ended.
Expand Down
130 changes: 130 additions & 0 deletions src/OpenTelemetry/Logs/LogRecordData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// <copyright file="LogRecordData.cs" company="OpenTelemetry Authors">
// 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.
// </copyright>

#nullable enable

using System;
using System.Diagnostics;
using Microsoft.Extensions.Logging;

namespace OpenTelemetry.Logs
{
/// <summary>
/// Stores details about a log message.
/// </summary>
internal struct LogRecordData
{
internal DateTime TimestampBacking = DateTime.UtcNow;

/// <summary>
/// Initializes a new instance of the <see cref="LogRecordData"/> struct.
/// </summary>
/// <remarks>
/// Note: The <see cref="Timestamp"/> property is initialized to <see
/// cref="DateTime.UtcNow"/> automatically.
/// </remarks>
/// <param name="activity">Optional <see cref="Activity"/> used to populate context fields.</param>
public LogRecordData(Activity? activity = null)
{
if (activity != null)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the ctor here use the static method SetActivityContext to avoid code duplication?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question! Sadly, no. This is a weird quick of structs. The compiler doesn't let you access this until the struct has been fully assigned. The reason to call the helper would be to do that assignment so it violates the rule.

        public LogRecordData(Activity? activity = null)
        {
            ref LogRecordData instance = ref this; // Error CS0188  The 'this' object cannot be used before all of its fields have been assigned

            SetActivityContext(ref instance, activity);
        }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i missed that this was a struct. thanks for the explanation!

{
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;
}
}

/// <summary>
/// Gets or sets the log timestamp.
/// </summary>
public DateTime Timestamp
{
readonly get => this.TimestampBacking;
set { this.TimestampBacking = value.Kind == DateTimeKind.Local ? value.ToUniversalTime() : value; }
}

/// <summary>
/// Gets or sets the log <see cref="ActivityTraceId"/>.
/// </summary>
public ActivityTraceId TraceId { get; set; }

/// <summary>
/// Gets or sets the log <see cref="ActivitySpanId"/>.
/// </summary>
public ActivitySpanId SpanId { get; set; }

/// <summary>
/// Gets or sets the log <see cref="ActivityTraceFlags"/>.
/// </summary>
public ActivityTraceFlags TraceFlags { get; set; }

/// <summary>
/// Gets or sets the log trace state.
/// </summary>
public string? TraceState { get; set; }

/// <summary>
/// Gets or sets the log category name.
/// </summary>
public string? CategoryName { get; set; } = null;

/// <summary>
/// Gets or sets the log <see cref="Microsoft.Extensions.Logging.LogLevel"/>.
/// </summary>
public LogLevel LogLevel { get; set; } = LogLevel.Trace;

/// <summary>
/// Gets or sets the log <see cref="Microsoft.Extensions.Logging.EventId"/>.
/// </summary>
public EventId EventId { get; set; } = default;

/// <summary>
/// Gets or sets the log message.
/// </summary>
public string? Message { get; set; } = null;

/// <summary>
/// Gets or sets the log <see cref="System.Exception"/>.
/// </summary>
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;
}
}
}
}