diff --git a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpLogRecordTransformer.cs b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpLogRecordTransformer.cs index 4851816017c..97be1c2d9f6 100644 --- a/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpLogRecordTransformer.cs +++ b/src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpLogRecordTransformer.cs @@ -18,8 +18,6 @@ internal sealed class OtlpLogRecordTransformer { internal static readonly ConcurrentBag LogListPool = new(); - private const string DefaultScopeName = ""; - private readonly SdkLimitOptions sdkLimitOptions; private readonly ExperimentalOptions experimentalOptions; @@ -49,7 +47,7 @@ internal OtlpCollector.ExportLogsServiceRequest BuildExportRequest( var otlpLogRecord = this.ToOtlpLog(logRecord); if (otlpLogRecord != null) { - var scopeName = logRecord.CategoryName ?? logRecord.Logger?.Name ?? DefaultScopeName; + var scopeName = logRecord.Logger.Name; if (!logsByCategory.TryGetValue(scopeName, out var scopeLogs)) { scopeLogs = this.GetLogListFromPool(scopeName); diff --git a/src/OpenTelemetry/.publicApi/Experimental/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/Experimental/PublicAPI.Unshipped.txt index 1f80fea01eb..822863b4660 100644 --- a/src/OpenTelemetry/.publicApi/Experimental/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/Experimental/PublicAPI.Unshipped.txt @@ -1,6 +1,6 @@ OpenTelemetry.Logs.LoggerProviderBuilderExtensions OpenTelemetry.Logs.LoggerProviderExtensions -OpenTelemetry.Logs.LogRecord.Logger.get -> OpenTelemetry.Logs.Logger? +OpenTelemetry.Logs.LogRecord.Logger.get -> OpenTelemetry.Logs.Logger! OpenTelemetry.Logs.LogRecord.Severity.get -> OpenTelemetry.Logs.LogRecordSeverity? OpenTelemetry.Logs.LogRecord.Severity.set -> void OpenTelemetry.Logs.LogRecord.SeverityText.get -> string? diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index d6888194f10..414dd3fb849 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -24,6 +24,10 @@ when configuring a view. ([#5312](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5312)) +* Updated `LogRecord` to keep `CategoryName` and `Logger` in sync when using the + experimental Log Bridge API. + [#5317](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5317) + ## 1.7.0 Released 2023-Dec-08 diff --git a/src/OpenTelemetry/Internal/InstrumentationScopeLogger.cs b/src/OpenTelemetry/Internal/InstrumentationScopeLogger.cs new file mode 100644 index 00000000000..2e38f7ff041 --- /dev/null +++ b/src/OpenTelemetry/Internal/InstrumentationScopeLogger.cs @@ -0,0 +1,29 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +using System.Collections.Concurrent; +using OpenTelemetry.Logs; + +namespace OpenTelemetry.Internal; + +internal sealed class InstrumentationScopeLogger : Logger +{ + private static readonly ConcurrentDictionary Cache = new(); + + private InstrumentationScopeLogger(string name) + : base(name) + { + } + + public static InstrumentationScopeLogger Default { get; } = new(string.Empty); + + public static InstrumentationScopeLogger GetInstrumentationScopeLoggerForName(string? name) + { + return string.IsNullOrWhiteSpace(name) + ? Default + : Cache.GetOrAdd(name!, static n => new(n)); + } + + public override void EmitLog(in LogRecordData data, in LogRecordAttributeList attributes) + => throw new NotSupportedException(); +} diff --git a/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLogger.cs b/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLogger.cs index acdc08f4fdd..024671caa49 100644 --- a/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLogger.cs +++ b/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLogger.cs @@ -24,7 +24,7 @@ internal sealed class OpenTelemetryLogger : ILogger private readonly LoggerProviderSdk provider; private readonly OpenTelemetryLoggerOptions options; - private readonly string categoryName; + private readonly InstrumentationScopeLogger instrumentationScope; internal OpenTelemetryLogger( LoggerProviderSdk provider, @@ -37,7 +37,7 @@ internal OpenTelemetryLogger( this.provider = provider!; this.options = options!; - this.categoryName = categoryName!; + this.instrumentationScope = InstrumentationScopeLogger.GetInstrumentationScopeLoggerForName(categoryName); } internal IExternalScopeProvider? ScopeProvider { get; set; } @@ -65,7 +65,6 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except iloggerData.TraceState = this.options.IncludeTraceState && activity != null ? activity.TraceStateString : null; - iloggerData.CategoryName = this.categoryName; iloggerData.EventId = eventId; iloggerData.Exception = exception; iloggerData.ScopeProvider = this.options.IncludeScopes ? this.ScopeProvider : null; @@ -97,7 +96,7 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except : null; } - record.Logger = LoggerInstrumentationScope.Instance; + record.Logger = this.instrumentationScope; processor.OnEnd(record); @@ -239,19 +238,4 @@ public void Dispose() { } } - - private sealed class LoggerInstrumentationScope : Logger - { - private LoggerInstrumentationScope(string name, string version) - : base(name) - { - this.SetInstrumentationScope(version); - } - - public static LoggerInstrumentationScope Instance { get; } - = new("OpenTelemetry", Sdk.InformationalVersion); - - public override void EmitLog(in LogRecordData data, in LogRecordAttributeList attributes) - => throw new NotSupportedException(); - } } diff --git a/src/OpenTelemetry/Logs/LogRecord.cs b/src/OpenTelemetry/Logs/LogRecord.cs index f98363ab766..58d97c38edf 100644 --- a/src/OpenTelemetry/Logs/LogRecord.cs +++ b/src/OpenTelemetry/Logs/LogRecord.cs @@ -60,7 +60,6 @@ internal LogRecord( this.ILoggerData = new() { TraceState = activity?.TraceStateString, - CategoryName = categoryName, FormattedMessage = formattedMessage, EventId = eventId, Exception = exception, @@ -79,6 +78,8 @@ internal LogRecord( this.AttributeData = stateValues; } + + this.Logger = InstrumentationScopeLogger.GetInstrumentationScopeLoggerForName(categoryName); } internal enum LogRecordSource @@ -153,16 +154,31 @@ public string? TraceState set => this.ILoggerData.TraceState = value; } +#if EXPOSE_EXPERIMENTAL_FEATURES /// /// Gets or sets the log category name. /// /// - /// Note: is only set when emitting logs through . + /// Note: is an alias for the accessed via the property. + /// Setting a new value for will result in a new + /// being set. /// +#else + /// + /// Gets or sets the log category name. + /// +#endif public string? CategoryName { - get => this.ILoggerData.CategoryName; - set => this.ILoggerData.CategoryName = value; + get => this.Logger.Name; + set + { + if (this.Logger.Name != value) + { + this.Logger = InstrumentationScopeLogger.GetInstrumentationScopeLoggerForName(value); + } + } } /// @@ -379,18 +395,26 @@ public Exception? Exception #if EXPOSE_EXPERIMENTAL_FEATURES /// - /// Gets the which emitted the . + /// Gets the associated with the . /// - /// + /// + /// + /// Note: When using the Log Bridge API (for example ) is + /// typically the which emitted the however the value may be different if is modified. #if NET8_0_OR_GREATER [Experimental(DiagnosticDefinitions.LogsBridgeExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)] #endif - public Logger? Logger { get; internal set; } + public Logger Logger { get; internal set; } = InstrumentationScopeLogger.Default; #else /// - /// Gets or sets the which emitted the . + /// Gets or sets the associated with the . /// - internal Logger? Logger { get; set; } + internal Logger Logger { get; set; } = InstrumentationScopeLogger.Default; #endif /// @@ -523,7 +547,6 @@ private void BufferLogScopes() internal struct LogRecordILoggerData { public string? TraceState; - public string? CategoryName; public EventId EventId; public string? FormattedMessage; public Exception? Exception; @@ -536,7 +559,6 @@ public LogRecordILoggerData Copy() var copy = new LogRecordILoggerData { TraceState = this.TraceState, - CategoryName = this.CategoryName, EventId = this.EventId, FormattedMessage = this.FormattedMessage, Exception = this.Exception, diff --git a/test/OpenTelemetry.Tests/Logs/LogRecordTest.cs b/test/OpenTelemetry.Tests/Logs/LogRecordTest.cs index e77f476ce2c..138af7ac695 100644 --- a/test/OpenTelemetry.Tests/Logs/LogRecordTest.cs +++ b/test/OpenTelemetry.Tests/Logs/LogRecordTest.cs @@ -979,8 +979,41 @@ public void LogRecordInstrumentationScopeTest() Assert.NotNull(logRecord); Assert.NotNull(logRecord.Logger); - Assert.Equal("OpenTelemetry", logRecord.Logger.Name); - Assert.Equal(Sdk.InformationalVersion, logRecord.Logger.Version); + Assert.Equal("OpenTelemetry.Logs.Tests.LogRecordTest", logRecord.Logger.Name); + Assert.Null(logRecord.Logger.Version); + } + + [Fact] + public void LogRecordCategoryNameAliasForInstrumentationScopeTests() + { + LogRecord logRecord = new(); + + Assert.Equal(string.Empty, logRecord.CategoryName); + Assert.Equal(logRecord.CategoryName, logRecord.Logger.Name); + + logRecord.CategoryName = "Testing"; + + Assert.Equal("Testing", logRecord.CategoryName); + Assert.Equal(logRecord.CategoryName, logRecord.Logger.Name); + + logRecord.CategoryName = null; + + Assert.Equal(string.Empty, logRecord.CategoryName); + Assert.Equal(logRecord.CategoryName, logRecord.Logger.Name); + + var exportedItems = new List(); + using (var loggerProvider = Sdk.CreateLoggerProviderBuilder() + .AddProcessor(new BatchLogRecordExportProcessor(new InMemoryExporter(exportedItems))) + .Build()) + { + var logger = loggerProvider.GetLogger("TestName"); + logger.EmitLog(default); + } + + Assert.Single(exportedItems); + + Assert.Equal("TestName", exportedItems[0].CategoryName); + Assert.Equal(exportedItems[0].CategoryName, exportedItems[0].Logger.Name); } private static ILoggerFactory InitializeLoggerFactory(out List exportedItems, Action configure = null)