@@ -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>
0 commit comments