Skip to content

Commit 10f8a0d

Browse files
authored
[sdk-logs] Update LogRecord to keep CategoryName and Logger in sync (#5317)
1 parent 8ff986b commit 10f8a0d

File tree

7 files changed

+106
-36
lines changed

7 files changed

+106
-36
lines changed

src/OpenTelemetry.Exporter.OpenTelemetryProtocol/Implementation/OtlpLogRecordTransformer.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ internal sealed class OtlpLogRecordTransformer
1818
{
1919
internal static readonly ConcurrentBag<OtlpLogs.ScopeLogs> LogListPool = new();
2020

21-
private const string DefaultScopeName = "";
22-
2321
private readonly SdkLimitOptions sdkLimitOptions;
2422
private readonly ExperimentalOptions experimentalOptions;
2523

@@ -49,7 +47,7 @@ internal OtlpCollector.ExportLogsServiceRequest BuildExportRequest(
4947
var otlpLogRecord = this.ToOtlpLog(logRecord);
5048
if (otlpLogRecord != null)
5149
{
52-
var scopeName = logRecord.CategoryName ?? logRecord.Logger?.Name ?? DefaultScopeName;
50+
var scopeName = logRecord.Logger.Name;
5351
if (!logsByCategory.TryGetValue(scopeName, out var scopeLogs))
5452
{
5553
scopeLogs = this.GetLogListFromPool(scopeName);

src/OpenTelemetry/.publicApi/Experimental/PublicAPI.Unshipped.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
OpenTelemetry.Logs.LoggerProviderBuilderExtensions
22
OpenTelemetry.Logs.LoggerProviderExtensions
3-
OpenTelemetry.Logs.LogRecord.Logger.get -> OpenTelemetry.Logs.Logger?
3+
OpenTelemetry.Logs.LogRecord.Logger.get -> OpenTelemetry.Logs.Logger!
44
OpenTelemetry.Logs.LogRecord.Severity.get -> OpenTelemetry.Logs.LogRecordSeverity?
55
OpenTelemetry.Logs.LogRecord.Severity.set -> void
66
OpenTelemetry.Logs.LogRecord.SeverityText.get -> string?

src/OpenTelemetry/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@
2424
when configuring a view.
2525
([#5312](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5312))
2626

27+
* Updated `LogRecord` to keep `CategoryName` and `Logger` in sync when using the
28+
experimental Log Bridge API.
29+
[#5317](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5317)
30+
2731
## 1.7.0
2832

2933
Released 2023-Dec-08
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
using System.Collections.Concurrent;
5+
using OpenTelemetry.Logs;
6+
7+
namespace OpenTelemetry.Internal;
8+
9+
internal sealed class InstrumentationScopeLogger : Logger
10+
{
11+
private static readonly ConcurrentDictionary<string, InstrumentationScopeLogger> Cache = new();
12+
13+
private InstrumentationScopeLogger(string name)
14+
: base(name)
15+
{
16+
}
17+
18+
public static InstrumentationScopeLogger Default { get; } = new(string.Empty);
19+
20+
public static InstrumentationScopeLogger GetInstrumentationScopeLoggerForName(string? name)
21+
{
22+
return string.IsNullOrWhiteSpace(name)
23+
? Default
24+
: Cache.GetOrAdd(name!, static n => new(n));
25+
}
26+
27+
public override void EmitLog(in LogRecordData data, in LogRecordAttributeList attributes)
28+
=> throw new NotSupportedException();
29+
}

src/OpenTelemetry/Logs/ILogger/OpenTelemetryLogger.cs

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ internal sealed class OpenTelemetryLogger : ILogger
2424

2525
private readonly LoggerProviderSdk provider;
2626
private readonly OpenTelemetryLoggerOptions options;
27-
private readonly string categoryName;
27+
private readonly InstrumentationScopeLogger instrumentationScope;
2828

2929
internal OpenTelemetryLogger(
3030
LoggerProviderSdk provider,
@@ -37,7 +37,7 @@ internal OpenTelemetryLogger(
3737

3838
this.provider = provider!;
3939
this.options = options!;
40-
this.categoryName = categoryName!;
40+
this.instrumentationScope = InstrumentationScopeLogger.GetInstrumentationScopeLoggerForName(categoryName);
4141
}
4242

4343
internal IExternalScopeProvider? ScopeProvider { get; set; }
@@ -65,7 +65,6 @@ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Except
6565
iloggerData.TraceState = this.options.IncludeTraceState && activity != null
6666
? activity.TraceStateString
6767
: null;
68-
iloggerData.CategoryName = this.categoryName;
6968
iloggerData.EventId = eventId;
7069
iloggerData.Exception = exception;
7170
iloggerData.ScopeProvider = this.options.IncludeScopes ? this.ScopeProvider : null;
@@ -97,7 +96,7 @@ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Except
9796
: null;
9897
}
9998

100-
record.Logger = LoggerInstrumentationScope.Instance;
99+
record.Logger = this.instrumentationScope;
101100

102101
processor.OnEnd(record);
103102

@@ -239,19 +238,4 @@ public void Dispose()
239238
{
240239
}
241240
}
242-
243-
private sealed class LoggerInstrumentationScope : Logger
244-
{
245-
private LoggerInstrumentationScope(string name, string version)
246-
: base(name)
247-
{
248-
this.SetInstrumentationScope(version);
249-
}
250-
251-
public static LoggerInstrumentationScope Instance { get; }
252-
= new("OpenTelemetry", Sdk.InformationalVersion);
253-
254-
public override void EmitLog(in LogRecordData data, in LogRecordAttributeList attributes)
255-
=> throw new NotSupportedException();
256-
}
257241
}

src/OpenTelemetry/Logs/LogRecord.cs

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ internal LogRecord(
6060
this.ILoggerData = new()
6161
{
6262
TraceState = activity?.TraceStateString,
63-
CategoryName = categoryName,
6463
FormattedMessage = formattedMessage,
6564
EventId = eventId,
6665
Exception = exception,
@@ -79,6 +78,8 @@ internal LogRecord(
7978

8079
this.AttributeData = stateValues;
8180
}
81+
82+
this.Logger = InstrumentationScopeLogger.GetInstrumentationScopeLoggerForName(categoryName);
8283
}
8384

8485
internal enum LogRecordSource
@@ -153,16 +154,31 @@ public string? TraceState
153154
set => this.ILoggerData.TraceState = value;
154155
}
155156

157+
#if EXPOSE_EXPERIMENTAL_FEATURES
156158
/// <summary>
157159
/// Gets or sets the log category name.
158160
/// </summary>
159161
/// <remarks>
160-
/// Note: <see cref="CategoryName"/> is only set when emitting logs through <see cref="ILogger"/>.
162+
/// Note: <see cref="CategoryName"/> is an alias for the <see
163+
/// cref="Logger.Name"/> accessed via the <see cref="Logger"/> property.
164+
/// Setting a new value for <see cref="CategoryName"/> will result in a new
165+
/// <see cref="Logger"/> being set.
161166
/// </remarks>
167+
#else
168+
/// <summary>
169+
/// Gets or sets the log category name.
170+
/// </summary>
171+
#endif
162172
public string? CategoryName
163173
{
164-
get => this.ILoggerData.CategoryName;
165-
set => this.ILoggerData.CategoryName = value;
174+
get => this.Logger.Name;
175+
set
176+
{
177+
if (this.Logger.Name != value)
178+
{
179+
this.Logger = InstrumentationScopeLogger.GetInstrumentationScopeLoggerForName(value);
180+
}
181+
}
166182
}
167183

168184
/// <summary>
@@ -379,18 +395,26 @@ public Exception? Exception
379395

380396
#if EXPOSE_EXPERIMENTAL_FEATURES
381397
/// <summary>
382-
/// Gets the <see cref="Logs.Logger"/> which emitted the <see cref="LogRecord"/>.
398+
/// Gets the <see cref="Logs.Logger"/> associated with the <see
399+
/// cref="LogRecord"/>.
383400
/// </summary>
384-
/// <remarks><inheritdoc cref="Sdk.CreateLoggerProviderBuilder" path="/remarks"/></remarks>
401+
/// <remarks>
402+
/// <para><inheritdoc cref="Sdk.CreateLoggerProviderBuilder" path="/remarks"/></para>
403+
/// Note: When using the Log Bridge API (for example <see
404+
/// cref="Logger.EmitLog(in LogRecordData)"/>) <see cref="Logger"/> is
405+
/// typically the <see cref="Logs.Logger"/> which emitted the <see
406+
/// cref="LogRecord"/> however the value may be different if <see
407+
/// cref="CategoryName"/> is modified.</remarks>
385408
#if NET8_0_OR_GREATER
386409
[Experimental(DiagnosticDefinitions.LogsBridgeExperimentalApi, UrlFormat = DiagnosticDefinitions.ExperimentalApiUrlFormat)]
387410
#endif
388-
public Logger? Logger { get; internal set; }
411+
public Logger Logger { get; internal set; } = InstrumentationScopeLogger.Default;
389412
#else
390413
/// <summary>
391-
/// Gets or sets the <see cref="Logs.Logger"/> which emitted the <see cref="LogRecord"/>.
414+
/// Gets or sets the <see cref="Logs.Logger"/> associated with the <see
415+
/// cref="LogRecord"/>.
392416
/// </summary>
393-
internal Logger? Logger { get; set; }
417+
internal Logger Logger { get; set; } = InstrumentationScopeLogger.Default;
394418
#endif
395419

396420
/// <summary>
@@ -523,7 +547,6 @@ private void BufferLogScopes()
523547
internal struct LogRecordILoggerData
524548
{
525549
public string? TraceState;
526-
public string? CategoryName;
527550
public EventId EventId;
528551
public string? FormattedMessage;
529552
public Exception? Exception;
@@ -536,7 +559,6 @@ public LogRecordILoggerData Copy()
536559
var copy = new LogRecordILoggerData
537560
{
538561
TraceState = this.TraceState,
539-
CategoryName = this.CategoryName,
540562
EventId = this.EventId,
541563
FormattedMessage = this.FormattedMessage,
542564
Exception = this.Exception,

test/OpenTelemetry.Tests/Logs/LogRecordTest.cs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -979,8 +979,41 @@ public void LogRecordInstrumentationScopeTest()
979979

980980
Assert.NotNull(logRecord);
981981
Assert.NotNull(logRecord.Logger);
982-
Assert.Equal("OpenTelemetry", logRecord.Logger.Name);
983-
Assert.Equal(Sdk.InformationalVersion, logRecord.Logger.Version);
982+
Assert.Equal("OpenTelemetry.Logs.Tests.LogRecordTest", logRecord.Logger.Name);
983+
Assert.Null(logRecord.Logger.Version);
984+
}
985+
986+
[Fact]
987+
public void LogRecordCategoryNameAliasForInstrumentationScopeTests()
988+
{
989+
LogRecord logRecord = new();
990+
991+
Assert.Equal(string.Empty, logRecord.CategoryName);
992+
Assert.Equal(logRecord.CategoryName, logRecord.Logger.Name);
993+
994+
logRecord.CategoryName = "Testing";
995+
996+
Assert.Equal("Testing", logRecord.CategoryName);
997+
Assert.Equal(logRecord.CategoryName, logRecord.Logger.Name);
998+
999+
logRecord.CategoryName = null;
1000+
1001+
Assert.Equal(string.Empty, logRecord.CategoryName);
1002+
Assert.Equal(logRecord.CategoryName, logRecord.Logger.Name);
1003+
1004+
var exportedItems = new List<LogRecord>();
1005+
using (var loggerProvider = Sdk.CreateLoggerProviderBuilder()
1006+
.AddProcessor(new BatchLogRecordExportProcessor(new InMemoryExporter<LogRecord>(exportedItems)))
1007+
.Build())
1008+
{
1009+
var logger = loggerProvider.GetLogger("TestName");
1010+
logger.EmitLog(default);
1011+
}
1012+
1013+
Assert.Single(exportedItems);
1014+
1015+
Assert.Equal("TestName", exportedItems[0].CategoryName);
1016+
Assert.Equal(exportedItems[0].CategoryName, exportedItems[0].Logger.Name);
9841017
}
9851018

9861019
private static ILoggerFactory InitializeLoggerFactory(out List<LogRecord> exportedItems, Action<OpenTelemetryLoggerOptions> configure = null)

0 commit comments

Comments
 (0)