Skip to content

Commit af83eb1

Browse files
authored
[sdk-logs] Keep LogRecord.State & LogRecord.Attributes in sync if either is updated by a log processor (#5169)
1 parent 7eeae5b commit af83eb1

File tree

5 files changed

+326
-13
lines changed

5 files changed

+326
-13
lines changed

src/OpenTelemetry/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
## Unreleased
44

5+
* Fixed an issue where `LogRecord.Attributes` (or `LogRecord.StateValues` alias)
6+
could become out of sync with `LogRecord.State` if either is set directly via
7+
the public setters. This was done to further mitigate issues introduced in
8+
1.5.0 causing attributes added using custom processor(s) to be missing after
9+
upgrading. For details see:
10+
[#5169](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5169)
11+
512
## 1.7.0
613

714
Released 2023-Dec-08

src/OpenTelemetry/Logs/ILogger/OpenTelemetryLogger.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Except
7979

8080
LogRecordData.SetActivityContext(ref data, activity);
8181

82-
var attributes = record.Attributes =
82+
var attributes = record.AttributeData =
8383
ProcessState(record, ref iloggerData, in state, this.options.IncludeAttributes, this.options.ParseStateValues);
8484

8585
if (!TryGetOriginalFormatFromAttributes(attributes, out var originalFormat))
@@ -133,7 +133,7 @@ internal static void SetLogRecordSeverityFields(ref LogRecordData logRecordData,
133133
}
134134
}
135135

136-
private static IReadOnlyList<KeyValuePair<string, object?>>? ProcessState<TState>(
136+
internal static IReadOnlyList<KeyValuePair<string, object?>>? ProcessState<TState>(
137137
LogRecord logRecord,
138138
ref LogRecord.LogRecordILoggerData iLoggerData,
139139
in TState state,

src/OpenTelemetry/Logs/LogRecord.cs

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public sealed class LogRecord
1818
{
1919
internal LogRecordData Data;
2020
internal LogRecordILoggerData ILoggerData;
21+
internal IReadOnlyList<KeyValuePair<string, object?>>? AttributeData;
2122
internal List<KeyValuePair<string, object?>>? AttributeStorage;
2223
internal List<object?>? ScopeStorage;
2324
internal int PoolReferenceCount = int.MaxValue;
@@ -75,7 +76,7 @@ internal LogRecord(
7576
this.Data.Body = template;
7677
}
7778

78-
this.Attributes = stateValues;
79+
this.AttributeData = stateValues;
7980
}
8081
}
8182

@@ -228,13 +229,30 @@ public string? Body
228229
/// through <see cref="ILogger"/>.</item>
229230
/// <item>Set to <see langword="null"/> when <see
230231
/// cref="OpenTelemetryLoggerOptions.ParseStateValues"/> is enabled.</item>
232+
/// <item><see cref="Attributes"/> are automatically updated if <see
233+
/// cref="State"/> is set directly.</item>
231234
/// </list>
232235
/// </remarks>
233236
[Obsolete("State cannot be accessed safely outside of an ILogger.Log call stack. Use Attributes instead to safely access the data attached to a LogRecord. State will be removed in a future version.")]
234237
public object? State
235238
{
236239
get => this.ILoggerData.State;
237-
set => this.ILoggerData.State = value;
240+
set
241+
{
242+
if (ReferenceEquals(this.ILoggerData.State, value))
243+
{
244+
return;
245+
}
246+
247+
if (this.AttributeData is not null)
248+
{
249+
this.AttributeData = OpenTelemetryLogger.ProcessState(this, ref this.ILoggerData, value, includeAttributes: true, parseStateValues: false);
250+
}
251+
else
252+
{
253+
this.ILoggerData.State = value;
254+
}
255+
}
238256
}
239257

240258
/// <summary>
@@ -252,15 +270,37 @@ public object? State
252270
/// Gets or sets the attributes attached to the log.
253271
/// </summary>
254272
/// <remarks>
255-
/// Note: Set when <see
256-
/// cref="OpenTelemetryLoggerOptions.IncludeAttributes"/> is enabled and
257-
/// log record state implements <see cref="IReadOnlyList{T}"/> or <see
273+
/// Notes:
274+
/// <list type="bullet">
275+
/// <item>Set when <see
276+
/// cref="OpenTelemetryLoggerOptions.IncludeAttributes"/> is enabled and log
277+
/// record state implements <see cref="IReadOnlyList{T}"/> or <see
258278
/// cref="IEnumerable{T}"/> of <see cref="KeyValuePair{TKey, TValue}"/>s
259279
/// (where TKey is <c>string</c> and TValue is <c>object</c>) or <see
260280
/// cref="OpenTelemetryLoggerOptions.ParseStateValues"/> is enabled
261-
/// otherwise <see langword="null"/>.
281+
/// otherwise <see langword="null"/>.</item>
282+
/// <item><see cref="State"/> is automatically updated if <see
283+
/// cref="Attributes"/> are set directly.</item>
284+
/// </list>
262285
/// </remarks>
263-
public IReadOnlyList<KeyValuePair<string, object?>>? Attributes { get; set; }
286+
public IReadOnlyList<KeyValuePair<string, object?>>? Attributes
287+
{
288+
get => this.AttributeData;
289+
set
290+
{
291+
if (ReferenceEquals(this.AttributeData, value))
292+
{
293+
return;
294+
}
295+
296+
if (this.ILoggerData.State is not null)
297+
{
298+
this.ILoggerData.State = value;
299+
}
300+
301+
this.AttributeData = value;
302+
}
303+
}
264304

265305
/// <summary>
266306
/// Gets or sets the log <see cref="System.Exception"/>.
@@ -411,7 +451,7 @@ internal LogRecord Copy()
411451
{
412452
Data = this.Data,
413453
ILoggerData = this.ILoggerData.Copy(),
414-
Attributes = this.Attributes == null ? null : new List<KeyValuePair<string, object?>>(this.Attributes),
454+
AttributeData = this.AttributeData is null ? null : new List<KeyValuePair<string, object?>>(this.AttributeData),
415455
Logger = this.Logger,
416456
};
417457
}
@@ -422,7 +462,7 @@ internal LogRecord Copy()
422462
/// </summary>
423463
private void BufferLogAttributes()
424464
{
425-
var attributes = this.Attributes;
465+
var attributes = this.AttributeData;
426466
if (attributes == null || attributes == this.AttributeStorage)
427467
{
428468
return;
@@ -437,7 +477,7 @@ private void BufferLogAttributes()
437477
// https://github.com/open-telemetry/opentelemetry-dotnet/issues/2905.
438478
attributeStorage.AddRange(attributes);
439479

440-
this.Attributes = attributeStorage;
480+
this.AttributeData = attributeStorage;
441481
}
442482

443483
/// <summary>

src/OpenTelemetry/Logs/LoggerSdk.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public override void EmitLog(in LogRecordData data, in LogRecordAttributeList at
3838

3939
logRecord.Logger = this;
4040

41-
logRecord.Attributes = attributes.Export(ref logRecord.AttributeStorage);
41+
logRecord.AttributeData = attributes.Export(ref logRecord.AttributeStorage);
4242

4343
processor.OnEnd(logRecord);
4444

0 commit comments

Comments
 (0)