[api-logs] Add internal artifacts for OTel spec log bridge API (#4422)
This commit is contained in:
parent
ef0c921708
commit
6a64dd2bc5
|
|
@ -0,0 +1,65 @@
|
|||
// <copyright file="InstrumentationScope.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
|
||||
|
||||
namespace OpenTelemetry;
|
||||
|
||||
/// <summary>
|
||||
/// Contains details about the library emitting telemetry.
|
||||
/// </summary>
|
||||
internal sealed class InstrumentationScope
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InstrumentationScope"/> class.
|
||||
/// </summary>
|
||||
public InstrumentationScope()
|
||||
: this(name: null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InstrumentationScope"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">Optional name identifying the instrumentation library.</param>
|
||||
public InstrumentationScope(string? name)
|
||||
{
|
||||
this.Name = string.IsNullOrWhiteSpace(name)
|
||||
? string.Empty
|
||||
: name!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name identifying the instrumentation library.
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the version of the instrumentation library.
|
||||
/// </summary>
|
||||
public string? Version { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the schema url of the instrumentation library.
|
||||
/// </summary>
|
||||
public string? SchemaUrl { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the attributes which should be associated with log records created
|
||||
/// by the instrumentation library.
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<string, object>? Attributes { get; init; }
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
// <copyright file="IDeferredLoggerProviderBuilder.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
|
||||
|
||||
namespace OpenTelemetry.Logs;
|
||||
|
||||
/// <summary>
|
||||
/// Describes a logger provider builder that supports deferred
|
||||
/// initialization using an <see cref="IServiceProvider"/> to perform
|
||||
/// dependency injection.
|
||||
/// </summary>
|
||||
internal interface IDeferredLoggerProviderBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Register a callback action to configure the <see
|
||||
/// cref="LoggerProviderBuilder"/> once the application <see
|
||||
/// cref="IServiceProvider"/> is available.
|
||||
/// </summary>
|
||||
/// <param name="configure">Configuration callback.</param>
|
||||
/// <returns>The supplied <see cref="LoggerProviderBuilder"/> for chaining.</returns>
|
||||
LoggerProviderBuilder Configure(Action<IServiceProvider, LoggerProviderBuilder> configure);
|
||||
}
|
||||
|
|
@ -0,0 +1,333 @@
|
|||
// <copyright file="LogRecordAttributeList.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.Collections;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using OpenTelemetry.Internal;
|
||||
using OpenTelemetry.Trace;
|
||||
|
||||
namespace OpenTelemetry.Logs;
|
||||
|
||||
/// <summary>
|
||||
/// Stores attributes to be added to a log message.
|
||||
/// </summary>
|
||||
internal struct LogRecordAttributeList : IReadOnlyList<KeyValuePair<string, object?>>
|
||||
{
|
||||
internal const int OverflowMaxCount = 8;
|
||||
internal const int OverflowAdditionalCapacity = 16;
|
||||
internal List<KeyValuePair<string, object?>>? OverflowAttributes;
|
||||
private KeyValuePair<string, object?> attribute1;
|
||||
private KeyValuePair<string, object?> attribute2;
|
||||
private KeyValuePair<string, object?> attribute3;
|
||||
private KeyValuePair<string, object?> attribute4;
|
||||
private KeyValuePair<string, object?> attribute5;
|
||||
private KeyValuePair<string, object?> attribute6;
|
||||
private KeyValuePair<string, object?> attribute7;
|
||||
private KeyValuePair<string, object?> attribute8;
|
||||
private int count;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public readonly int Count => this.count;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public KeyValuePair<string, object?> this[int index]
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (this.OverflowAttributes is not null)
|
||||
{
|
||||
Debug.Assert(index < this.OverflowAttributes.Count, "Invalid index accessed.");
|
||||
return this.OverflowAttributes[index];
|
||||
}
|
||||
|
||||
if ((uint)index >= (uint)this.count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
|
||||
return index switch
|
||||
{
|
||||
0 => this.attribute1,
|
||||
1 => this.attribute2,
|
||||
2 => this.attribute3,
|
||||
3 => this.attribute4,
|
||||
4 => this.attribute5,
|
||||
5 => this.attribute6,
|
||||
6 => this.attribute7,
|
||||
7 => this.attribute8,
|
||||
_ => default, // we shouldn't come here anyway.
|
||||
};
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (this.OverflowAttributes is not null)
|
||||
{
|
||||
Debug.Assert(index < this.OverflowAttributes.Count, "Invalid index accessed.");
|
||||
this.OverflowAttributes[index] = value;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((uint)index >= (uint)this.count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
|
||||
switch (index)
|
||||
{
|
||||
case 0: this.attribute1 = value; break;
|
||||
case 1: this.attribute2 = value; break;
|
||||
case 2: this.attribute3 = value; break;
|
||||
case 3: this.attribute4 = value; break;
|
||||
case 4: this.attribute5 = value; break;
|
||||
case 5: this.attribute6 = value; break;
|
||||
case 6: this.attribute7 = value; break;
|
||||
case 7: this.attribute8 = value; break;
|
||||
default:
|
||||
Debug.Assert(false, "Unreachable code executed.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an attribute.
|
||||
/// </summary>
|
||||
/// <param name="key">Attribute name.</param>
|
||||
/// <returns>Attribute value.</returns>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public object? this[string key]
|
||||
{
|
||||
// Note: This only exists to enable collection initializer syntax
|
||||
// like { ["key"] = value }.
|
||||
set => this.Add(new KeyValuePair<string, object?>(key, value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="LogRecordAttributeList"/> collection from an enumerable.
|
||||
/// </summary>
|
||||
/// <param name="attributes">Source attributes.</param>
|
||||
/// <returns><see cref="LogRecordAttributeList"/>.</returns>
|
||||
public static LogRecordAttributeList CreateFromEnumerable(IEnumerable<KeyValuePair<string, object?>> attributes)
|
||||
{
|
||||
Guard.ThrowIfNull(attributes);
|
||||
|
||||
LogRecordAttributeList logRecordAttributes = default;
|
||||
logRecordAttributes.OverflowAttributes = new(attributes);
|
||||
logRecordAttributes.count = logRecordAttributes.OverflowAttributes.Count;
|
||||
return logRecordAttributes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an attribute.
|
||||
/// </summary>
|
||||
/// <param name="key">Attribute name.</param>
|
||||
/// <param name="value">Attribute value.</param>
|
||||
public void Add(string key, object? value)
|
||||
=> this.Add(new KeyValuePair<string, object?>(key, value));
|
||||
|
||||
/// <summary>
|
||||
/// Add an attribute.
|
||||
/// </summary>
|
||||
/// <param name="attribute">Attribute.</param>
|
||||
public void Add(KeyValuePair<string, object?> attribute)
|
||||
{
|
||||
var count = this.count++;
|
||||
|
||||
if (count <= OverflowMaxCount)
|
||||
{
|
||||
switch (count)
|
||||
{
|
||||
case 0: this.attribute1 = attribute; return;
|
||||
case 1: this.attribute2 = attribute; return;
|
||||
case 2: this.attribute3 = attribute; return;
|
||||
case 3: this.attribute4 = attribute; return;
|
||||
case 4: this.attribute5 = attribute; return;
|
||||
case 5: this.attribute6 = attribute; return;
|
||||
case 6: this.attribute7 = attribute; return;
|
||||
case 7: this.attribute8 = attribute; return;
|
||||
case 8:
|
||||
this.MoveAttributesToTheOverflowList();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Assert(this.OverflowAttributes is not null, "Overflow attributes creation failure.");
|
||||
this.OverflowAttributes!.Add(attribute);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all elements from the <see cref="LogRecordAttributeList"/>.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
this.count = 0;
|
||||
this.OverflowAttributes?.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds attributes representing an <see cref="Exception"/> to the list.
|
||||
/// </summary>
|
||||
/// <param name="exception"><see cref="Exception"/>.</param>
|
||||
public void RecordException(Exception exception)
|
||||
{
|
||||
Guard.ThrowIfNull(exception);
|
||||
|
||||
this.Add(SemanticConventions.AttributeExceptionType, exception.GetType().Name);
|
||||
this.Add(SemanticConventions.AttributeExceptionMessage, exception.Message);
|
||||
this.Add(SemanticConventions.AttributeExceptionStacktrace, exception.ToInvariantString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates through the <see cref="LogRecordAttributeList"/>.
|
||||
/// </summary>
|
||||
/// <returns><see cref="Enumerator"/>.</returns>
|
||||
public readonly Enumerator GetEnumerator()
|
||||
=> new(in this);
|
||||
|
||||
/// <inheritdoc/>
|
||||
readonly IEnumerator<KeyValuePair<string, object?>> IEnumerable<KeyValuePair<string, object?>>.GetEnumerator() => this.GetEnumerator();
|
||||
|
||||
/// <inheritdoc/>
|
||||
readonly IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
|
||||
|
||||
internal readonly List<KeyValuePair<string, object?>>? Export(ref List<KeyValuePair<string, object?>>? attributeStorage)
|
||||
{
|
||||
int count = this.count;
|
||||
if (count <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var overflowAttributes = this.OverflowAttributes;
|
||||
if (overflowAttributes != null)
|
||||
{
|
||||
// An allocation has already occurred, just use the list.
|
||||
return overflowAttributes;
|
||||
}
|
||||
|
||||
Debug.Assert(count <= 8, "Invalid size detected.");
|
||||
|
||||
attributeStorage ??= new List<KeyValuePair<string, object?>>(OverflowAdditionalCapacity);
|
||||
|
||||
// TODO: Perf test this, adjust as needed.
|
||||
if (count > 0)
|
||||
{
|
||||
attributeStorage.Add(this.attribute1);
|
||||
if (count == 1)
|
||||
{
|
||||
return attributeStorage;
|
||||
}
|
||||
|
||||
attributeStorage.Add(this.attribute2);
|
||||
if (count == 2)
|
||||
{
|
||||
return attributeStorage;
|
||||
}
|
||||
|
||||
attributeStorage.Add(this.attribute3);
|
||||
if (count == 3)
|
||||
{
|
||||
return attributeStorage;
|
||||
}
|
||||
|
||||
attributeStorage.Add(this.attribute4);
|
||||
if (count == 4)
|
||||
{
|
||||
return attributeStorage;
|
||||
}
|
||||
|
||||
attributeStorage.Add(this.attribute5);
|
||||
if (count == 5)
|
||||
{
|
||||
return attributeStorage;
|
||||
}
|
||||
|
||||
attributeStorage.Add(this.attribute6);
|
||||
if (count == 6)
|
||||
{
|
||||
return attributeStorage;
|
||||
}
|
||||
|
||||
attributeStorage.Add(this.attribute7);
|
||||
if (count == 7)
|
||||
{
|
||||
return attributeStorage;
|
||||
}
|
||||
|
||||
attributeStorage.Add(this.attribute8);
|
||||
}
|
||||
|
||||
return attributeStorage;
|
||||
}
|
||||
|
||||
private void MoveAttributesToTheOverflowList()
|
||||
{
|
||||
Debug.Assert(this.count - 1 == OverflowMaxCount, "count did not match OverflowMaxCount");
|
||||
|
||||
var attributes = this.OverflowAttributes ??= new(OverflowAdditionalCapacity);
|
||||
|
||||
attributes.Add(this.attribute1);
|
||||
attributes.Add(this.attribute2);
|
||||
attributes.Add(this.attribute3);
|
||||
attributes.Add(this.attribute4);
|
||||
attributes.Add(this.attribute5);
|
||||
attributes.Add(this.attribute6);
|
||||
attributes.Add(this.attribute7);
|
||||
attributes.Add(this.attribute8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the elements of a <see cref="LogRecordAttributeList"/>.
|
||||
/// </summary>
|
||||
public struct Enumerator : IEnumerator<KeyValuePair<string, object?>>, IEnumerator
|
||||
{
|
||||
private LogRecordAttributeList attributes;
|
||||
private int index;
|
||||
|
||||
internal Enumerator(in LogRecordAttributeList attributes)
|
||||
{
|
||||
this.index = -1;
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public readonly KeyValuePair<string, object?> Current
|
||||
=> this.attributes[this.index];
|
||||
|
||||
/// <inheritdoc/>
|
||||
readonly object IEnumerator.Current => this.Current;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool MoveNext()
|
||||
{
|
||||
this.index++;
|
||||
return this.index < this.attributes.Count;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public readonly void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
readonly void IEnumerator.Reset()
|
||||
=> throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
// <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.Diagnostics;
|
||||
|
||||
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>
|
||||
/// Notes:
|
||||
/// <list type="bullet">
|
||||
/// <item>The <see cref="Timestamp"/> property is initialized to <see
|
||||
/// cref="DateTime.UtcNow"/> automatically.</item>
|
||||
/// <item>The <see cref="TraceId"/>, <see cref="SpanId"/>, and <see
|
||||
/// cref="TraceFlags"/> properties will be set using the <see
|
||||
/// cref="Activity.Current"/> instance.</item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public LogRecordData()
|
||||
: this(Activity.Current?.Context ?? default)
|
||||
{
|
||||
}
|
||||
|
||||
/// <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
|
||||
/// trace context properties (<see cref="TraceId"/>, <see cref="SpanId"/>,
|
||||
/// and <see cref="TraceFlags"/>).</param>
|
||||
public LogRecordData(Activity? activity)
|
||||
: this(activity?.Context ?? default)
|
||||
{
|
||||
}
|
||||
|
||||
/// <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="activityContext"><see cref="ActivityContext"/> used to
|
||||
/// populate trace context properties (<see cref="TraceId"/>, <see
|
||||
/// cref="SpanId"/>, and <see cref="TraceFlags"/>).</param>
|
||||
public LogRecordData(in ActivityContext activityContext)
|
||||
{
|
||||
this.TraceId = activityContext.TraceId;
|
||||
this.SpanId = activityContext.SpanId;
|
||||
this.TraceFlags = activityContext.TraceFlags;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the log timestamp.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note: If <see cref="Timestamp"/> is set to a value with <see
|
||||
/// cref="DateTimeKind.Local"/> it will be automatically converted to
|
||||
/// UTC using <see cref="DateTime.ToUniversalTime"/>.
|
||||
/// </remarks>
|
||||
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 original string representation of the severity as it is
|
||||
/// known at the source.
|
||||
/// </summary>
|
||||
public string? SeverityText { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the log severity.
|
||||
/// </summary>
|
||||
public LogRecordSeverity? Severity { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the log body.
|
||||
/// </summary>
|
||||
public string? Body { get; set; } = null;
|
||||
|
||||
internal static void SetActivityContext(ref LogRecordData data, Activity? activity)
|
||||
{
|
||||
if (activity != null)
|
||||
{
|
||||
data.TraceId = activity.TraceId;
|
||||
data.SpanId = activity.SpanId;
|
||||
data.TraceFlags = activity.ActivityTraceFlags;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.TraceId = default;
|
||||
data.SpanId = default;
|
||||
data.TraceFlags = ActivityTraceFlags.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
// <copyright file="LogRecordSeverity.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
|
||||
|
||||
namespace OpenTelemetry.Logs;
|
||||
|
||||
/// <summary>
|
||||
/// Describes the severity level of a log record.
|
||||
/// </summary>
|
||||
internal enum LogRecordSeverity
|
||||
{
|
||||
/// <summary>Trace severity.</summary>
|
||||
Trace,
|
||||
|
||||
/// <summary>Debug severity.</summary>
|
||||
Debug,
|
||||
|
||||
/// <summary>Information severity.</summary>
|
||||
Information,
|
||||
|
||||
/// <summary>Warning severity.</summary>
|
||||
Warning,
|
||||
|
||||
/// <summary>Error severity.</summary>
|
||||
Error,
|
||||
|
||||
/// <summary>Fatal severity.</summary>
|
||||
Fatal,
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// <copyright file="Logger.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 OpenTelemetry.Internal;
|
||||
|
||||
namespace OpenTelemetry.Logs;
|
||||
|
||||
/// <summary>
|
||||
/// Logger is the class responsible for creating log records.
|
||||
/// </summary>
|
||||
internal abstract class Logger
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Logger"/> class.
|
||||
/// </summary>
|
||||
/// <param name="instrumentationScope"><see
|
||||
/// cref="OpenTelemetry.InstrumentationScope"/>.</param>
|
||||
protected Logger(InstrumentationScope instrumentationScope)
|
||||
{
|
||||
Guard.ThrowIfNull(instrumentationScope);
|
||||
|
||||
this.InstrumentationScope = instrumentationScope;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="OpenTelemetry.InstrumentationScope"/> associated
|
||||
/// with the logger.
|
||||
/// </summary>
|
||||
public InstrumentationScope InstrumentationScope { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Emit a log.
|
||||
/// </summary>
|
||||
/// <param name="data"><see cref="LogRecordData"/>.</param>
|
||||
/// <param name="attributes"><see cref="LogRecordAttributeList"/>.</param>
|
||||
public abstract void EmitLog(
|
||||
in LogRecordData data,
|
||||
in LogRecordAttributeList attributes = default);
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
// <copyright file="LoggerProvider.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
|
||||
|
||||
namespace OpenTelemetry.Logs;
|
||||
|
||||
/// <summary>
|
||||
/// LoggerProvider is the entry point of the OpenTelemetry API. It provides access to <see cref="Logger"/>.
|
||||
/// </summary>
|
||||
internal class LoggerProvider : BaseProvider
|
||||
{
|
||||
private static readonly NoopLogger NoopLogger = new();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LoggerProvider"/> class.
|
||||
/// </summary>
|
||||
protected LoggerProvider()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a logger.
|
||||
/// </summary>
|
||||
/// <returns><see cref="Logger"/> instance.</returns>
|
||||
public Logger GetLogger()
|
||||
=> this.GetLogger(new InstrumentationScope());
|
||||
|
||||
/// <summary>
|
||||
/// Gets a logger with the given name.
|
||||
/// </summary>
|
||||
/// <param name="name">Optional name identifying the instrumentation library.</param>
|
||||
/// <returns><see cref="Logger"/> instance.</returns>
|
||||
public Logger GetLogger(string name)
|
||||
=> this.GetLogger(new InstrumentationScope(name));
|
||||
|
||||
/// <summary>
|
||||
/// Gets a logger with given instrumentation scope.
|
||||
/// </summary>
|
||||
/// <param name="instrumentationScope"><see cref="InstrumentationScope"/>.</param>
|
||||
/// <returns><see cref="Logger"/>.</returns>
|
||||
public virtual Logger GetLogger(InstrumentationScope instrumentationScope)
|
||||
=> NoopLogger;
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
// <copyright file="LoggerProviderBuilder.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
|
||||
|
||||
namespace OpenTelemetry.Logs;
|
||||
|
||||
/// <summary>
|
||||
/// LoggerProviderBuilder base class.
|
||||
/// </summary>
|
||||
internal abstract class LoggerProviderBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LoggerProviderBuilder"/> class.
|
||||
/// </summary>
|
||||
protected LoggerProviderBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds instrumentation to the provider.
|
||||
/// </summary>
|
||||
/// <typeparam name="TInstrumentation">Type of instrumentation class.</typeparam>
|
||||
/// <param name="instrumentationFactory">Function that builds instrumentation.</param>
|
||||
/// <returns>Returns <see cref="LoggerProviderBuilder"/> for chaining.</returns>
|
||||
public abstract LoggerProviderBuilder AddInstrumentation<TInstrumentation>(
|
||||
Func<TInstrumentation> instrumentationFactory)
|
||||
where TInstrumentation : class;
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// <copyright file="NoopLogger.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
|
||||
|
||||
namespace OpenTelemetry.Logs;
|
||||
|
||||
internal sealed class NoopLogger : Logger
|
||||
{
|
||||
public NoopLogger()
|
||||
: base(instrumentationScope: new())
|
||||
{
|
||||
}
|
||||
|
||||
public override void EmitLog(
|
||||
in LogRecordData data,
|
||||
in LogRecordAttributeList attributes = default)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,324 +0,0 @@
|
|||
// <copyright file="LogRecordAttributeList.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.Collections;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using OpenTelemetry.Internal;
|
||||
|
||||
namespace OpenTelemetry.Logs
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores attributes to be added to a log message.
|
||||
/// </summary>
|
||||
internal struct LogRecordAttributeList : IReadOnlyList<KeyValuePair<string, object?>>
|
||||
{
|
||||
internal const int OverflowAdditionalCapacity = 8;
|
||||
internal List<KeyValuePair<string, object?>>? OverflowAttributes;
|
||||
private KeyValuePair<string, object?> attribute1;
|
||||
private KeyValuePair<string, object?> attribute2;
|
||||
private KeyValuePair<string, object?> attribute3;
|
||||
private KeyValuePair<string, object?> attribute4;
|
||||
private KeyValuePair<string, object?> attribute5;
|
||||
private KeyValuePair<string, object?> attribute6;
|
||||
private KeyValuePair<string, object?> attribute7;
|
||||
private KeyValuePair<string, object?> attribute8;
|
||||
private int count;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public readonly int Count => this.count;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public KeyValuePair<string, object?> this[int index]
|
||||
{
|
||||
readonly get
|
||||
{
|
||||
if (this.OverflowAttributes is not null)
|
||||
{
|
||||
Debug.Assert(index < this.OverflowAttributes.Count, "Invalid index accessed.");
|
||||
return this.OverflowAttributes[index];
|
||||
}
|
||||
|
||||
if ((uint)index >= (uint)this.count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
|
||||
return index switch
|
||||
{
|
||||
0 => this.attribute1,
|
||||
1 => this.attribute2,
|
||||
2 => this.attribute3,
|
||||
3 => this.attribute4,
|
||||
4 => this.attribute5,
|
||||
5 => this.attribute6,
|
||||
6 => this.attribute7,
|
||||
7 => this.attribute8,
|
||||
_ => default, // we shouldn't come here anyway.
|
||||
};
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (this.OverflowAttributes is not null)
|
||||
{
|
||||
Debug.Assert(index < this.OverflowAttributes.Count, "Invalid index accessed.");
|
||||
this.OverflowAttributes[index] = value;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((uint)index >= (uint)this.count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(index));
|
||||
}
|
||||
|
||||
switch (index)
|
||||
{
|
||||
case 0: this.attribute1 = value; break;
|
||||
case 1: this.attribute2 = value; break;
|
||||
case 2: this.attribute3 = value; break;
|
||||
case 3: this.attribute4 = value; break;
|
||||
case 4: this.attribute5 = value; break;
|
||||
case 5: this.attribute6 = value; break;
|
||||
case 6: this.attribute7 = value; break;
|
||||
case 7: this.attribute8 = value; break;
|
||||
default:
|
||||
Debug.Assert(false, "Unreachable code executed.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an attribute.
|
||||
/// </summary>
|
||||
/// <param name="key">Attribute name.</param>
|
||||
/// <returns>Attribute value.</returns>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public object? this[string key]
|
||||
{
|
||||
// Note: This only exists to enable collection initializer syntax
|
||||
// like { ["key"] = value }.
|
||||
set => this.Add(new KeyValuePair<string, object?>(key, value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="LogRecordAttributeList"/> collection from an enumerable.
|
||||
/// </summary>
|
||||
/// <param name="attributes">Source attributes.</param>
|
||||
/// <returns><see cref="LogRecordAttributeList"/>.</returns>
|
||||
public static LogRecordAttributeList CreateFromEnumerable(IEnumerable<KeyValuePair<string, object?>> attributes)
|
||||
{
|
||||
Guard.ThrowIfNull(attributes);
|
||||
|
||||
LogRecordAttributeList logRecordAttributes = default;
|
||||
logRecordAttributes.OverflowAttributes = new(attributes);
|
||||
logRecordAttributes.count = logRecordAttributes.OverflowAttributes.Count;
|
||||
return logRecordAttributes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add an attribute.
|
||||
/// </summary>
|
||||
/// <param name="key">Attribute name.</param>
|
||||
/// <param name="value">Attribute value.</param>
|
||||
public void Add(string key, object? value)
|
||||
=> this.Add(new KeyValuePair<string, object?>(key, value));
|
||||
|
||||
/// <summary>
|
||||
/// Add an attribute.
|
||||
/// </summary>
|
||||
/// <param name="attribute">Attribute.</param>
|
||||
public void Add(KeyValuePair<string, object?> attribute)
|
||||
{
|
||||
if (this.OverflowAttributes is not null)
|
||||
{
|
||||
this.OverflowAttributes.Add(attribute);
|
||||
this.count++;
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Assert(this.count <= 8, "Item added beyond struct capacity.");
|
||||
|
||||
switch (this.count)
|
||||
{
|
||||
case 0: this.attribute1 = attribute; break;
|
||||
case 1: this.attribute2 = attribute; break;
|
||||
case 2: this.attribute3 = attribute; break;
|
||||
case 3: this.attribute4 = attribute; break;
|
||||
case 4: this.attribute5 = attribute; break;
|
||||
case 5: this.attribute6 = attribute; break;
|
||||
case 6: this.attribute7 = attribute; break;
|
||||
case 7: this.attribute8 = attribute; break;
|
||||
case 8:
|
||||
Debug.Assert(this.OverflowAttributes is null, "Overflow attributes already created.");
|
||||
this.MoveAttributesToTheOverflowList();
|
||||
Debug.Assert(this.OverflowAttributes is not null, "Overflow attributes creation failure.");
|
||||
this.OverflowAttributes!.Add(attribute);
|
||||
break;
|
||||
default:
|
||||
// We shouldn't come here.
|
||||
Debug.Assert(this.OverflowAttributes is null, "Unreachable code executed.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.count++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates through the <see cref="LogRecordAttributeList"/>.
|
||||
/// </summary>
|
||||
/// <returns><see cref="Enumerator"/>.</returns>
|
||||
public readonly Enumerator GetEnumerator()
|
||||
=> new(in this);
|
||||
|
||||
/// <inheritdoc/>
|
||||
readonly IEnumerator<KeyValuePair<string, object?>> IEnumerable<KeyValuePair<string, object?>>.GetEnumerator() => this.GetEnumerator();
|
||||
|
||||
/// <inheritdoc/>
|
||||
readonly IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
|
||||
|
||||
internal readonly void ApplyToLogRecord(LogRecord logRecord)
|
||||
{
|
||||
int count = this.count;
|
||||
if (count <= 0)
|
||||
{
|
||||
logRecord.StateValues = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var overflowAttributes = this.OverflowAttributes;
|
||||
if (overflowAttributes != null)
|
||||
{
|
||||
// An allocation has already occurred, just use the buffer.
|
||||
logRecord.StateValues = overflowAttributes;
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Assert(count <= 8, "Invalid size detected.");
|
||||
|
||||
var attributeStorage = logRecord.AttributeStorage ??= new List<KeyValuePair<string, object?>>(OverflowAdditionalCapacity);
|
||||
|
||||
try
|
||||
{
|
||||
// TODO: Perf test this, adjust as needed.
|
||||
|
||||
attributeStorage.Add(this.attribute1);
|
||||
if (count == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
attributeStorage.Add(this.attribute2);
|
||||
if (count == 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
attributeStorage.Add(this.attribute3);
|
||||
if (count == 3)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
attributeStorage.Add(this.attribute4);
|
||||
if (count == 4)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
attributeStorage.Add(this.attribute5);
|
||||
if (count == 5)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
attributeStorage.Add(this.attribute6);
|
||||
if (count == 6)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
attributeStorage.Add(this.attribute7);
|
||||
if (count == 7)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
attributeStorage.Add(this.attribute8);
|
||||
}
|
||||
finally
|
||||
{
|
||||
logRecord.StateValues = attributeStorage;
|
||||
}
|
||||
}
|
||||
|
||||
private void MoveAttributesToTheOverflowList()
|
||||
{
|
||||
this.OverflowAttributes = new(16)
|
||||
{
|
||||
{ this.attribute1 },
|
||||
{ this.attribute2 },
|
||||
{ this.attribute3 },
|
||||
{ this.attribute4 },
|
||||
{ this.attribute5 },
|
||||
{ this.attribute6 },
|
||||
{ this.attribute7 },
|
||||
{ this.attribute8 },
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the elements of a <see cref="LogRecordAttributeList"/>.
|
||||
/// </summary>
|
||||
public struct Enumerator : IEnumerator<KeyValuePair<string, object?>>, IEnumerator
|
||||
{
|
||||
private LogRecordAttributeList attributes;
|
||||
private int index;
|
||||
|
||||
internal Enumerator(in LogRecordAttributeList attributes)
|
||||
{
|
||||
this.index = -1;
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public readonly KeyValuePair<string, object?> Current
|
||||
=> this.attributes[this.index];
|
||||
|
||||
/// <inheritdoc/>
|
||||
readonly object IEnumerator.Current => this.Current;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool MoveNext()
|
||||
{
|
||||
this.index++;
|
||||
return this.index < this.attributes.Count;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public readonly void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
readonly void IEnumerator.Reset()
|
||||
=> throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
// <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.Diagnostics;
|
||||
|
||||
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>
|
||||
public LogRecordData()
|
||||
: this(activity: null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
if (activity != null)
|
||||
{
|
||||
this.TraceId = activity.TraceId;
|
||||
this.SpanId = activity.SpanId;
|
||||
this.TraceFlags = activity.ActivityTraceFlags;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.TraceId = default;
|
||||
this.SpanId = default;
|
||||
this.TraceFlags = ActivityTraceFlags.None;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the log timestamp.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note: If <see cref="Timestamp"/> is set to a value with <see
|
||||
/// cref="DateTimeKind.Local"/> it will be automatically converted to
|
||||
/// UTC using <see cref="DateTime.ToUniversalTime"/>.
|
||||
/// </remarks>
|
||||
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; }
|
||||
|
||||
/* TODO: Add these when log api/sdk spec is stable.
|
||||
/// <summary>
|
||||
/// Gets or sets the original string representation of the severity as it is
|
||||
/// known at the source.
|
||||
/// </summary>
|
||||
public string? SeverityText { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the log severity.
|
||||
/// </summary>
|
||||
public LogRecordSeverity? Severity { get; set; } = null;
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the log body.
|
||||
/// </summary>
|
||||
public string? Body { 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.TraceFlags = activity.ActivityTraceFlags;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.TraceId = default;
|
||||
data.SpanId = default;
|
||||
data.TraceFlags = ActivityTraceFlags.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -125,9 +125,14 @@ namespace OpenTelemetry.Logs
|
|||
/* TODO: Enable this if/when LogRecordAttributeList becomes public.
|
||||
if (typeof(TState) == typeof(LogRecordAttributeList))
|
||||
{
|
||||
// Note: This cast looks strange, but it is meant for the JIT to optimize/remove.
|
||||
((LogRecordAttributeList)(object)state!).ApplyToLogRecord(logRecord);
|
||||
return logRecord.AttributeStorage!;
|
||||
// Note: This block is written to be elided by the JIT when
|
||||
// TState is not LogRecordAttributeList or optimized when it is.
|
||||
// For users that pass LogRecordAttributeList as TState to
|
||||
// ILogger.Log this will avoid boxing the struct.
|
||||
|
||||
var logRecordAttributes = (LogRecordAttributeList)(object)state!;
|
||||
|
||||
return logRecordAttributes.Export(ref logRecord.AttributeStorage);
|
||||
}
|
||||
else */
|
||||
if (state is IReadOnlyList<KeyValuePair<string, object?>> stateList)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,145 @@
|
|||
// <copyright file="LogRecordAttributeListTests.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>
|
||||
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Logs.Tests;
|
||||
|
||||
public sealed class LogRecordAttributeListTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(1)]
|
||||
[InlineData(8)]
|
||||
[InlineData(9)]
|
||||
[InlineData(64)]
|
||||
public void ReadWriteTest(int numberOfItems)
|
||||
{
|
||||
LogRecordAttributeList attributes = default;
|
||||
|
||||
for (int i = 0; i < numberOfItems; i++)
|
||||
{
|
||||
attributes.Add($"key{i}", i);
|
||||
}
|
||||
|
||||
Assert.Equal(numberOfItems, attributes.Count);
|
||||
|
||||
for (int i = 0; i < numberOfItems; i++)
|
||||
{
|
||||
var item = attributes[i];
|
||||
|
||||
Assert.Equal($"key{i}", item.Key);
|
||||
Assert.Equal(i, (int)item.Value);
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
foreach (KeyValuePair<string, object> item in attributes)
|
||||
{
|
||||
Assert.Equal($"key{index}", item.Key);
|
||||
Assert.Equal(index, (int)item.Value);
|
||||
index++;
|
||||
}
|
||||
|
||||
if (attributes.Count <= LogRecordAttributeList.OverflowMaxCount)
|
||||
{
|
||||
Assert.Null(attributes.OverflowAttributes);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.NotNull(attributes.OverflowAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(1)]
|
||||
[InlineData(8)]
|
||||
[InlineData(9)]
|
||||
[InlineData(64)]
|
||||
public void ClearTest(int numberOfItems)
|
||||
{
|
||||
LogRecordAttributeList attributes = default;
|
||||
|
||||
for (int c = 0; c <= 1; c++)
|
||||
{
|
||||
for (int i = 0; i < numberOfItems; i++)
|
||||
{
|
||||
attributes.Add($"key{i}", i);
|
||||
}
|
||||
|
||||
Assert.Equal(numberOfItems, attributes.Count);
|
||||
|
||||
for (int i = 0; i < numberOfItems; i++)
|
||||
{
|
||||
var item = attributes[i];
|
||||
|
||||
Assert.Equal($"key{i}", item.Key);
|
||||
Assert.Equal(i, (int)item.Value);
|
||||
}
|
||||
|
||||
attributes.Clear();
|
||||
|
||||
Assert.Empty(attributes);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(1)]
|
||||
[InlineData(8)]
|
||||
[InlineData(9)]
|
||||
[InlineData(64)]
|
||||
public void ExportTest(int numberOfItems)
|
||||
{
|
||||
LogRecordAttributeList attributes = default;
|
||||
|
||||
for (int i = 0; i < numberOfItems; i++)
|
||||
{
|
||||
attributes.Add($"key{i}", i);
|
||||
}
|
||||
|
||||
List<KeyValuePair<string, object>> storage = null;
|
||||
|
||||
var exportedAttributes = attributes.Export(ref storage);
|
||||
|
||||
if (numberOfItems == 0)
|
||||
{
|
||||
Assert.Null(exportedAttributes);
|
||||
Assert.Null(storage);
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.NotNull(exportedAttributes);
|
||||
|
||||
if (numberOfItems <= LogRecordAttributeList.OverflowMaxCount)
|
||||
{
|
||||
Assert.NotNull(storage);
|
||||
Assert.True(ReferenceEquals(storage, exportedAttributes));
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Null(storage);
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
foreach (KeyValuePair<string, object> item in exportedAttributes)
|
||||
{
|
||||
Assert.Equal($"key{index}", item.Key);
|
||||
Assert.Equal(index, (int)item.Value);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
// <copyright file="LogRecordDataTests.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>
|
||||
|
||||
using System.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Logs.Tests;
|
||||
|
||||
public sealed class LogRecordDataTests
|
||||
{
|
||||
[Fact]
|
||||
public void ParameterlessConstructorWithActiveActivityTest()
|
||||
{
|
||||
using var activity = new Activity("Test");
|
||||
activity.ActivityTraceFlags = ActivityTraceFlags.Recorded;
|
||||
activity.Start();
|
||||
|
||||
var record = new LogRecordData();
|
||||
|
||||
Assert.Equal(activity.TraceId, record.TraceId);
|
||||
Assert.Equal(activity.SpanId, record.SpanId);
|
||||
Assert.Equal(activity.ActivityTraceFlags, record.TraceFlags);
|
||||
|
||||
record = default;
|
||||
|
||||
Assert.Equal(default, record.TraceId);
|
||||
Assert.Equal(default, record.SpanId);
|
||||
Assert.Equal(default, record.TraceFlags);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParameterlessConstructorWithoutActiveActivityTest()
|
||||
{
|
||||
var record = new LogRecordData();
|
||||
|
||||
Assert.Equal(default, record.TraceId);
|
||||
Assert.Equal(default, record.SpanId);
|
||||
Assert.Equal(default, record.TraceFlags);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ActivityConstructorTest()
|
||||
{
|
||||
using var activity = new Activity("Test");
|
||||
activity.ActivityTraceFlags = ActivityTraceFlags.Recorded;
|
||||
activity.Start();
|
||||
|
||||
Activity.Current = null;
|
||||
|
||||
var record = new LogRecordData(activity);
|
||||
|
||||
Assert.Equal(activity.TraceId, record.TraceId);
|
||||
Assert.Equal(activity.SpanId, record.SpanId);
|
||||
Assert.Equal(activity.ActivityTraceFlags, record.TraceFlags);
|
||||
|
||||
record = new LogRecordData(activity: null);
|
||||
|
||||
Assert.Equal(default, record.TraceId);
|
||||
Assert.Equal(default, record.SpanId);
|
||||
Assert.Equal(default, record.TraceFlags);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ActivityContextConstructorTest()
|
||||
{
|
||||
var context = new ActivityContext(
|
||||
ActivityTraceId.CreateRandom(),
|
||||
ActivitySpanId.CreateRandom(),
|
||||
ActivityTraceFlags.Recorded,
|
||||
traceState: null,
|
||||
isRemote: true);
|
||||
|
||||
var record = new LogRecordData(in context);
|
||||
|
||||
Assert.Equal(context.TraceId, record.TraceId);
|
||||
Assert.Equal(context.SpanId, record.SpanId);
|
||||
Assert.Equal(context.TraceFlags, record.TraceFlags);
|
||||
|
||||
record = new LogRecordData(activityContext: default);
|
||||
|
||||
Assert.Equal(default, record.TraceId);
|
||||
Assert.Equal(default, record.SpanId);
|
||||
Assert.Equal(default, record.TraceFlags);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TimestampTest()
|
||||
{
|
||||
var nowUtc = DateTime.UtcNow;
|
||||
|
||||
var record = new LogRecordData();
|
||||
Assert.True(record.Timestamp >= nowUtc);
|
||||
|
||||
record = default;
|
||||
Assert.Equal(DateTime.MinValue, record.Timestamp);
|
||||
|
||||
var now = DateTime.Now;
|
||||
|
||||
record.Timestamp = now;
|
||||
|
||||
Assert.Equal(DateTimeKind.Utc, record.Timestamp.Kind);
|
||||
Assert.Equal(now.ToUniversalTime(), record.Timestamp);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetActivityContextTest()
|
||||
{
|
||||
LogRecordData record = default;
|
||||
|
||||
using var activity = new Activity("Test");
|
||||
activity.ActivityTraceFlags = ActivityTraceFlags.Recorded;
|
||||
activity.Start();
|
||||
|
||||
LogRecordData.SetActivityContext(ref record, activity);
|
||||
|
||||
Assert.Equal(activity.TraceId, record.TraceId);
|
||||
Assert.Equal(activity.SpanId, record.SpanId);
|
||||
Assert.Equal(activity.ActivityTraceFlags, record.TraceFlags);
|
||||
|
||||
LogRecordData.SetActivityContext(ref record, activity: null);
|
||||
|
||||
Assert.Equal(default, record.TraceId);
|
||||
Assert.Equal(default, record.SpanId);
|
||||
Assert.Equal(default, record.TraceFlags);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
// <copyright file="LogRecordAttributeListTests.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>
|
||||
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Logs.Tests
|
||||
{
|
||||
public sealed class LogRecordAttributeListTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(1)]
|
||||
[InlineData(8)]
|
||||
[InlineData(9)]
|
||||
[InlineData(64)]
|
||||
public void ReadWriteTest(int numberOfItems)
|
||||
{
|
||||
LogRecordAttributeList attributes = default;
|
||||
|
||||
for (int i = 0; i < numberOfItems; i++)
|
||||
{
|
||||
attributes.Add($"key{i}", i);
|
||||
}
|
||||
|
||||
Assert.Equal(numberOfItems, attributes.Count);
|
||||
|
||||
for (int i = 0; i < numberOfItems; i++)
|
||||
{
|
||||
var item = attributes[i];
|
||||
|
||||
Assert.Equal($"key{i}", item.Key);
|
||||
Assert.Equal(i, (int)item.Value);
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
foreach (KeyValuePair<string, object> item in attributes)
|
||||
{
|
||||
Assert.Equal($"key{index}", item.Key);
|
||||
Assert.Equal(index, (int)item.Value);
|
||||
index++;
|
||||
}
|
||||
|
||||
if (attributes.Count <= LogRecordAttributeList.OverflowAdditionalCapacity)
|
||||
{
|
||||
Assert.Null(attributes.OverflowAttributes);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.NotNull(attributes.OverflowAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(1)]
|
||||
[InlineData(8)]
|
||||
[InlineData(9)]
|
||||
[InlineData(64)]
|
||||
public void ApplyToLogRecordTest(int numberOfItems)
|
||||
{
|
||||
LogRecordAttributeList attributes = default;
|
||||
|
||||
for (int i = 0; i < numberOfItems; i++)
|
||||
{
|
||||
attributes.Add($"key{i}", i);
|
||||
}
|
||||
|
||||
LogRecord logRecord = new();
|
||||
|
||||
attributes.ApplyToLogRecord(logRecord);
|
||||
|
||||
if (numberOfItems == 0)
|
||||
{
|
||||
Assert.Null(logRecord.StateValues);
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.NotNull(logRecord.StateValues);
|
||||
|
||||
int index = 0;
|
||||
foreach (KeyValuePair<string, object> item in logRecord.StateValues)
|
||||
{
|
||||
Assert.Equal($"key{index}", item.Key);
|
||||
Assert.Equal(index, (int)item.Value);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue