From 365ce4f4fcb0e4c4a74472c4cca418537b6b36e8 Mon Sep 17 00:00:00 2001 From: Mikel Blanchard Date: Wed, 10 May 2023 12:00:50 -0700 Subject: [PATCH] Update log source files in SDK to use file scoped namespaces. --- .../Logs/BatchLogRecordExportProcessor.cs | 75 +- .../ILogger/OpenTelemetryLoggerOptions.cs | 177 +++-- .../ILogger/OpenTelemetryLoggerProvider.cs | 191 +++-- .../ILogger/OpenTelemetryLoggingExtensions.cs | 117 ++- src/OpenTelemetry/Logs/LogRecord.cs | 739 +++++++++--------- src/OpenTelemetry/Logs/LogRecordScope.cs | 127 ++- src/OpenTelemetry/Logs/Pool/ILogRecordPool.cs | 11 +- .../Logs/Pool/LogRecordPoolHelper.cs | 63 +- .../Logs/Pool/LogRecordSharedPool.cs | 187 +++-- .../Logs/Pool/LogRecordThreadStaticPool.cs | 47 +- .../Logs/SimpleLogRecordExportProcessor.cs | 21 +- src/OpenTelemetry/ProviderExtensions.cs | 125 ++- 12 files changed, 934 insertions(+), 946 deletions(-) diff --git a/src/OpenTelemetry/Logs/BatchLogRecordExportProcessor.cs b/src/OpenTelemetry/Logs/BatchLogRecordExportProcessor.cs index 644f160dbb0..94448dfa817 100644 --- a/src/OpenTelemetry/Logs/BatchLogRecordExportProcessor.cs +++ b/src/OpenTelemetry/Logs/BatchLogRecordExportProcessor.cs @@ -19,52 +19,51 @@ using System.Diagnostics; using OpenTelemetry.Logs; -namespace OpenTelemetry +namespace OpenTelemetry; + +/// +/// Implements a batch log record export processor. +/// +public class BatchLogRecordExportProcessor : BatchExportProcessor { /// - /// Implements a batch log record export processor. + /// Initializes a new instance of the class. /// - public class BatchLogRecordExportProcessor : BatchExportProcessor + /// Log record exporter. + /// The maximum queue size. After the size is reached data are dropped. The default value is 2048. + /// The delay interval in milliseconds between two consecutive exports. The default value is 5000. + /// How long the export can run before it is cancelled. The default value is 30000. + /// The maximum batch size of every export. It must be smaller or equal to maxQueueSize. The default value is 512. + public BatchLogRecordExportProcessor( + BaseExporter exporter, + int maxQueueSize = DefaultMaxQueueSize, + int scheduledDelayMilliseconds = DefaultScheduledDelayMilliseconds, + int exporterTimeoutMilliseconds = DefaultExporterTimeoutMilliseconds, + int maxExportBatchSize = DefaultMaxExportBatchSize) + : base( + exporter, + maxQueueSize, + scheduledDelayMilliseconds, + exporterTimeoutMilliseconds, + maxExportBatchSize) { - /// - /// Initializes a new instance of the class. - /// - /// Log record exporter. - /// The maximum queue size. After the size is reached data are dropped. The default value is 2048. - /// The delay interval in milliseconds between two consecutive exports. The default value is 5000. - /// How long the export can run before it is cancelled. The default value is 30000. - /// The maximum batch size of every export. It must be smaller or equal to maxQueueSize. The default value is 512. - public BatchLogRecordExportProcessor( - BaseExporter exporter, - int maxQueueSize = DefaultMaxQueueSize, - int scheduledDelayMilliseconds = DefaultScheduledDelayMilliseconds, - int exporterTimeoutMilliseconds = DefaultExporterTimeoutMilliseconds, - int maxExportBatchSize = DefaultMaxExportBatchSize) - : base( - exporter, - maxQueueSize, - scheduledDelayMilliseconds, - exporterTimeoutMilliseconds, - maxExportBatchSize) - { - } + } - /// - public override void OnEnd(LogRecord data) - { - // Note: Intentionally doing a Debug.Assert here and not a - // Guard.ThrowIfNull to save prod cycles. Null should really never - // happen here. - Debug.Assert(data != null, "LogRecord was null."); + /// + public override void OnEnd(LogRecord data) + { + // Note: Intentionally doing a Debug.Assert here and not a + // Guard.ThrowIfNull to save prod cycles. Null should really never + // happen here. + Debug.Assert(data != null, "LogRecord was null."); - data!.Buffer(); + data!.Buffer(); - data.AddReference(); + data.AddReference(); - if (!this.TryExport(data)) - { - LogRecordSharedPool.Current.Return(data); - } + if (!this.TryExport(data)) + { + LogRecordSharedPool.Current.Return(data); } } } diff --git a/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLoggerOptions.cs b/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLoggerOptions.cs index 8155458625d..f2a392e52e7 100644 --- a/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLoggerOptions.cs +++ b/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLoggerOptions.cs @@ -20,108 +20,107 @@ using OpenTelemetry.Internal; using OpenTelemetry.Resources; -namespace OpenTelemetry.Logs +namespace OpenTelemetry.Logs; + +/// +/// Contains OpenTelemetry logging options. +/// +public class OpenTelemetryLoggerOptions { + internal readonly List> Processors = new(); + internal ResourceBuilder? ResourceBuilder; + /// - /// Contains OpenTelemetry logging options. + /// Gets or sets a value indicating whether or not formatted log message + /// should be included on generated s. Default + /// value: . /// - public class OpenTelemetryLoggerOptions - { - internal readonly List> Processors = new(); - internal ResourceBuilder? ResourceBuilder; + /// + /// Note: When set to a formatted log message + /// will not be included if a message template can be found. If a + /// message template is not found, a formatted log message is always + /// included. + /// + public bool IncludeFormattedMessage { get; set; } - /// - /// Gets or sets a value indicating whether or not formatted log message - /// should be included on generated s. Default - /// value: . - /// - /// - /// Note: When set to a formatted log message - /// will not be included if a message template can be found. If a - /// message template is not found, a formatted log message is always - /// included. - /// - public bool IncludeFormattedMessage { get; set; } - - /// - /// Gets or sets a value indicating whether or not log scopes should be - /// included on generated s. Default value: - /// . - /// - public bool IncludeScopes { get; set; } + /// + /// Gets or sets a value indicating whether or not log scopes should be + /// included on generated s. Default value: + /// . + /// + public bool IncludeScopes { get; set; } - /// - /// Gets or sets a value indicating whether or not log state should be - /// parsed into on generated s. Default value: . - /// - /// - /// Notes: - /// - /// Parsing is only executed when the state logged does NOT - /// implement or where T is KeyValuePair<string, - /// object>. - /// When is set to will always be . - /// - /// - public bool ParseStateValues { get; set; } + /// + /// Gets or sets a value indicating whether or not log state should be + /// parsed into on generated s. Default value: . + /// + /// + /// Notes: + /// + /// Parsing is only executed when the state logged does NOT + /// implement or where T is KeyValuePair<string, + /// object>. + /// When is set to will always be . + /// + /// + public bool ParseStateValues { get; set; } - /// - /// Gets or sets a value indicating whether or not attributes specified - /// via log state should be included on generated s. Default value: . - /// - internal bool IncludeAttributes { get; set; } = true; + /// + /// Gets or sets a value indicating whether or not attributes specified + /// via log state should be included on generated s. Default value: . + /// + internal bool IncludeAttributes { get; set; } = true; - /// - /// Gets or sets a value indicating whether or not the for the current should be included on generated s. Default value: . - /// - internal bool IncludeTraceState { get; set; } + /// + /// Gets or sets a value indicating whether or not the for the current should be included on generated s. Default value: . + /// + internal bool IncludeTraceState { get; set; } - /// - /// Adds processor to the options. - /// - /// Log processor to add. - /// Returns for chaining. - public OpenTelemetryLoggerOptions AddProcessor(BaseProcessor processor) - { - Guard.ThrowIfNull(processor); + /// + /// Adds processor to the options. + /// + /// Log processor to add. + /// Returns for chaining. + public OpenTelemetryLoggerOptions AddProcessor(BaseProcessor processor) + { + Guard.ThrowIfNull(processor); - this.Processors.Add(processor); + this.Processors.Add(processor); - return this; - } + return this; + } - /// - /// Sets the from which the Resource associated with - /// this provider is built from. Overwrites currently set ResourceBuilder. - /// - /// from which Resource will be built. - /// Returns for chaining. - public OpenTelemetryLoggerOptions SetResourceBuilder(ResourceBuilder resourceBuilder) - { - Guard.ThrowIfNull(resourceBuilder); + /// + /// Sets the from which the Resource associated with + /// this provider is built from. Overwrites currently set ResourceBuilder. + /// + /// from which Resource will be built. + /// Returns for chaining. + public OpenTelemetryLoggerOptions SetResourceBuilder(ResourceBuilder resourceBuilder) + { + Guard.ThrowIfNull(resourceBuilder); - this.ResourceBuilder = resourceBuilder; - return this; - } + this.ResourceBuilder = resourceBuilder; + return this; + } - internal OpenTelemetryLoggerOptions Copy() + internal OpenTelemetryLoggerOptions Copy() + { + return new() { - return new() - { - IncludeFormattedMessage = this.IncludeFormattedMessage, - IncludeScopes = this.IncludeScopes, - ParseStateValues = this.ParseStateValues, - IncludeAttributes = this.IncludeAttributes, - IncludeTraceState = this.IncludeTraceState, - }; - } + IncludeFormattedMessage = this.IncludeFormattedMessage, + IncludeScopes = this.IncludeScopes, + ParseStateValues = this.ParseStateValues, + IncludeAttributes = this.IncludeAttributes, + IncludeTraceState = this.IncludeTraceState, + }; } } diff --git a/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLoggerProvider.cs b/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLoggerProvider.cs index 368f3e7ec4f..2744b617d24 100644 --- a/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLoggerProvider.cs +++ b/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLoggerProvider.cs @@ -23,140 +23,139 @@ using Microsoft.Extensions.Options; using OpenTelemetry.Internal; -namespace OpenTelemetry.Logs +namespace OpenTelemetry.Logs; + +/// +/// An implementation for exporting logs using OpenTelemetry. +/// +[ProviderAlias("OpenTelemetry")] +public class OpenTelemetryLoggerProvider : BaseProvider, ILoggerProvider, ISupportExternalScope { + internal readonly LoggerProvider Provider; + private readonly bool ownsProvider; + private readonly Hashtable loggers = new(); + private bool disposed; + + static OpenTelemetryLoggerProvider() + { + // Accessing Sdk class is just to trigger its static ctor, + // which sets default Propagators and default Activity Id format + _ = Sdk.SuppressInstrumentation; + } + /// - /// An implementation for exporting logs using OpenTelemetry. + /// Initializes a new instance of the class. /// - [ProviderAlias("OpenTelemetry")] - public class OpenTelemetryLoggerProvider : BaseProvider, ILoggerProvider, ISupportExternalScope + /// . + // todo: [Obsolete("Use the Sdk.CreateLoggerProviderBuilder method instead this ctor will be removed in a future version.")] + public OpenTelemetryLoggerProvider(IOptionsMonitor options) { - internal readonly LoggerProvider Provider; - private readonly bool ownsProvider; - private readonly Hashtable loggers = new(); - private bool disposed; + Guard.ThrowIfNull(options); - static OpenTelemetryLoggerProvider() - { - // Accessing Sdk class is just to trigger its static ctor, - // which sets default Propagators and default Activity Id format - _ = Sdk.SuppressInstrumentation; - } - - /// - /// Initializes a new instance of the class. - /// - /// . - // todo: [Obsolete("Use the Sdk.CreateLoggerProviderBuilder method instead this ctor will be removed in a future version.")] - public OpenTelemetryLoggerProvider(IOptionsMonitor options) - { - Guard.ThrowIfNull(options); + var optionsInstance = options.CurrentValue; - var optionsInstance = options.CurrentValue; + this.Provider = Sdk + .CreateLoggerProviderBuilder() + .ConfigureBuilder((sp, builder) => + { + if (optionsInstance.ResourceBuilder != null) + { + builder.SetResourceBuilder(optionsInstance.ResourceBuilder); + } - this.Provider = Sdk - .CreateLoggerProviderBuilder() - .ConfigureBuilder((sp, builder) => + foreach (var processor in optionsInstance.Processors) { - if (optionsInstance.ResourceBuilder != null) - { - builder.SetResourceBuilder(optionsInstance.ResourceBuilder); - } + builder.AddProcessor(processor); + } + }) + .Build(); - foreach (var processor in optionsInstance.Processors) - { - builder.AddProcessor(processor); - } - }) - .Build(); + this.Options = optionsInstance.Copy(); + this.ownsProvider = true; + } - this.Options = optionsInstance.Copy(); - this.ownsProvider = true; - } + internal OpenTelemetryLoggerProvider( + LoggerProvider loggerProvider, + OpenTelemetryLoggerOptions options, + bool disposeProvider) + { + Debug.Assert(loggerProvider != null, "loggerProvider was null"); + Debug.Assert(options != null, "options was null"); - internal OpenTelemetryLoggerProvider( - LoggerProvider loggerProvider, - OpenTelemetryLoggerOptions options, - bool disposeProvider) - { - Debug.Assert(loggerProvider != null, "loggerProvider was null"); - Debug.Assert(options != null, "options was null"); + this.Provider = loggerProvider!; + this.Options = options!.Copy(); + this.ownsProvider = disposeProvider; + } - this.Provider = loggerProvider!; - this.Options = options!.Copy(); - this.ownsProvider = disposeProvider; - } + internal OpenTelemetryLoggerOptions Options { get; } - internal OpenTelemetryLoggerOptions Options { get; } + internal IExternalScopeProvider? ScopeProvider { get; private set; } - internal IExternalScopeProvider? ScopeProvider { get; private set; } + /// + void ISupportExternalScope.SetScopeProvider(IExternalScopeProvider scopeProvider) + { + this.ScopeProvider = scopeProvider; - /// - void ISupportExternalScope.SetScopeProvider(IExternalScopeProvider scopeProvider) + lock (this.loggers) { - this.ScopeProvider = scopeProvider; - - lock (this.loggers) + foreach (DictionaryEntry entry in this.loggers) { - foreach (DictionaryEntry entry in this.loggers) + if (entry.Value is OpenTelemetryLogger logger) { - if (entry.Value is OpenTelemetryLogger logger) - { - logger.ScopeProvider = scopeProvider; - } + logger.ScopeProvider = scopeProvider; } } } + } - /// - public ILogger CreateLogger(string categoryName) + /// + public ILogger CreateLogger(string categoryName) + { + if (this.loggers[categoryName] is not ILogger logger) { - if (this.loggers[categoryName] is not ILogger logger) + lock (this.loggers) { - lock (this.loggers) + logger = (this.loggers[categoryName] as ILogger)!; + if (logger == null) { - logger = (this.loggers[categoryName] as ILogger)!; - if (logger == null) + var loggerProviderSdk = this.Provider as LoggerProviderSdk; + if (loggerProviderSdk == null) { - var loggerProviderSdk = this.Provider as LoggerProviderSdk; - if (loggerProviderSdk == null) - { - logger = NullLogger.Instance; - } - else + logger = NullLogger.Instance; + } + else + { + logger = new OpenTelemetryLogger(loggerProviderSdk, this.Options, categoryName) { - logger = new OpenTelemetryLogger(loggerProviderSdk, this.Options, categoryName) - { - ScopeProvider = this.ScopeProvider, - }; - } - - this.loggers[categoryName] = logger; + ScopeProvider = this.ScopeProvider, + }; } + + this.loggers[categoryName] = logger; } } - - return logger; } - /// - protected override void Dispose(bool disposing) + return logger; + } + + /// + protected override void Dispose(bool disposing) + { + if (!this.disposed) { - if (!this.disposed) + if (disposing) { - if (disposing) + if (this.ownsProvider) { - if (this.ownsProvider) - { - this.Provider.Dispose(); - } + this.Provider.Dispose(); } - - this.disposed = true; - OpenTelemetrySdkEventSource.Log.ProviderDisposed(nameof(OpenTelemetryLoggerProvider)); } - base.Dispose(disposing); + this.disposed = true; + OpenTelemetrySdkEventSource.Log.ProviderDisposed(nameof(OpenTelemetryLoggerProvider)); } + + base.Dispose(disposing); } } diff --git a/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLoggingExtensions.cs b/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLoggingExtensions.cs index e61e031ed91..6f63fce672a 100644 --- a/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLoggingExtensions.cs +++ b/src/OpenTelemetry/Logs/ILogger/OpenTelemetryLoggingExtensions.cs @@ -24,80 +24,79 @@ using OpenTelemetry.Internal; using OpenTelemetry.Logs; -namespace Microsoft.Extensions.Logging +namespace Microsoft.Extensions.Logging; + +/// +/// Contains extension methods for registering into a instance. +/// +public static class OpenTelemetryLoggingExtensions { /// - /// Contains extension methods for registering into a instance. + /// Adds an OpenTelemetry logger named 'OpenTelemetry' to the . /// - public static class OpenTelemetryLoggingExtensions + /// + /// Note: This is safe to be called multiple times and by library + /// authors. Only a single + /// will be created for a given . + /// + /// The to use. + /// The supplied for call chaining. + public static ILoggingBuilder AddOpenTelemetry( + this ILoggingBuilder builder) { - /// - /// Adds an OpenTelemetry logger named 'OpenTelemetry' to the . - /// - /// - /// Note: This is safe to be called multiple times and by library - /// authors. Only a single - /// will be created for a given . - /// - /// The to use. - /// The supplied for call chaining. - public static ILoggingBuilder AddOpenTelemetry( - this ILoggingBuilder builder) - { - Guard.ThrowIfNull(builder); + Guard.ThrowIfNull(builder); - builder.AddConfiguration(); + builder.AddConfiguration(); - // Note: This will bind logger options element (eg "Logging:OpenTelemetry") to OpenTelemetryLoggerOptions - LoggerProviderOptions.RegisterProviderOptions(builder.Services); + // Note: This will bind logger options element (eg "Logging:OpenTelemetry") to OpenTelemetryLoggerOptions + LoggerProviderOptions.RegisterProviderOptions(builder.Services); - new LoggerProviderServiceCollectionBuilder(builder.Services).ConfigureBuilder( - (sp, logging) => - { - var options = sp.GetRequiredService>().CurrentValue; + new LoggerProviderServiceCollectionBuilder(builder.Services).ConfigureBuilder( + (sp, logging) => + { + var options = sp.GetRequiredService>().CurrentValue; - if (options.ResourceBuilder != null) - { - logging.SetResourceBuilder(options.ResourceBuilder); + if (options.ResourceBuilder != null) + { + logging.SetResourceBuilder(options.ResourceBuilder); - options.ResourceBuilder = null; - } + options.ResourceBuilder = null; + } - foreach (var processor in options.Processors) - { - logging.AddProcessor(processor); - } + foreach (var processor in options.Processors) + { + logging.AddProcessor(processor); + } - options.Processors.Clear(); - }); + options.Processors.Clear(); + }); - builder.Services.TryAddEnumerable( - ServiceDescriptor.Singleton( - sp => new OpenTelemetryLoggerProvider( - sp.GetRequiredService(), - sp.GetRequiredService>().CurrentValue, - disposeProvider: false))); + builder.Services.TryAddEnumerable( + ServiceDescriptor.Singleton( + sp => new OpenTelemetryLoggerProvider( + sp.GetRequiredService(), + sp.GetRequiredService>().CurrentValue, + disposeProvider: false))); - return builder; - } + return builder; + } - /// - /// Adds an OpenTelemetry logger named 'OpenTelemetry' to the . - /// - /// - /// The to use. - /// Optional configuration action. - /// The supplied for call chaining. - public static ILoggingBuilder AddOpenTelemetry( - this ILoggingBuilder builder, - Action? configure) + /// + /// Adds an OpenTelemetry logger named 'OpenTelemetry' to the . + /// + /// + /// The to use. + /// Optional configuration action. + /// The supplied for call chaining. + public static ILoggingBuilder AddOpenTelemetry( + this ILoggingBuilder builder, + Action? configure) + { + if (configure != null) { - if (configure != null) - { - builder.Services.Configure(configure); - } - - return AddOpenTelemetry(builder); + builder.Services.Configure(configure); } + + return AddOpenTelemetry(builder); } } diff --git a/src/OpenTelemetry/Logs/LogRecord.cs b/src/OpenTelemetry/Logs/LogRecord.cs index 14a6722f544..4ff15d58494 100644 --- a/src/OpenTelemetry/Logs/LogRecord.cs +++ b/src/OpenTelemetry/Logs/LogRecord.cs @@ -21,451 +21,450 @@ using Microsoft.Extensions.Logging; using OpenTelemetry.Internal; -namespace OpenTelemetry.Logs +namespace OpenTelemetry.Logs; + +/// +/// Stores details about a log message. +/// +public sealed class LogRecord { - /// - /// Stores details about a log message. - /// - public sealed class LogRecord + internal LogRecordData Data; + internal LogRecordILoggerData ILoggerData; + internal List>? AttributeStorage; + internal List? ScopeStorage; + internal int PoolReferenceCount = int.MaxValue; + + private static readonly Action> AddScopeToBufferedList = (object? scope, List state) => { - internal LogRecordData Data; - internal LogRecordILoggerData ILoggerData; - internal List>? AttributeStorage; - internal List? ScopeStorage; - internal int PoolReferenceCount = int.MaxValue; + state.Add(scope); + }; - private static readonly Action> AddScopeToBufferedList = (object? scope, List state) => - { - state.Add(scope); - }; + internal LogRecord() + { + } - internal LogRecord() - { - } + // Note: Some users are calling this with reflection. Try not to change the signature to be nice. + [Obsolete("Call LogRecordPool.Rent instead.")] + internal LogRecord( + IExternalScopeProvider? scopeProvider, + DateTime timestamp, + string categoryName, + LogLevel logLevel, + EventId eventId, + string? formattedMessage, + object? state, + Exception? exception, + IReadOnlyList>? stateValues) + { + var activity = Activity.Current; - // Note: Some users are calling this with reflection. Try not to change the signature to be nice. - [Obsolete("Call LogRecordPool.Rent instead.")] - internal LogRecord( - IExternalScopeProvider? scopeProvider, - DateTime timestamp, - string categoryName, - LogLevel logLevel, - EventId eventId, - string? formattedMessage, - object? state, - Exception? exception, - IReadOnlyList>? stateValues) + this.Data = new(activity) { - var activity = Activity.Current; - - this.Data = new(activity) - { - TimestampBacking = timestamp, + TimestampBacking = timestamp, - Body = formattedMessage, - }; + Body = formattedMessage, + }; - OpenTelemetryLogger.SetLogRecordSeverityFields(ref this.Data, logLevel); + OpenTelemetryLogger.SetLogRecordSeverityFields(ref this.Data, logLevel); - this.ILoggerData = new() - { - TraceState = activity?.TraceStateString, - CategoryName = categoryName, - FormattedMessage = formattedMessage, - EventId = eventId, - Exception = exception, - State = state, - ScopeProvider = scopeProvider, - }; + this.ILoggerData = new() + { + TraceState = activity?.TraceStateString, + CategoryName = categoryName, + FormattedMessage = formattedMessage, + EventId = eventId, + Exception = exception, + State = state, + ScopeProvider = scopeProvider, + }; - if (stateValues != null && stateValues.Count > 0) + if (stateValues != null && stateValues.Count > 0) + { + var lastAttribute = stateValues[stateValues.Count - 1]; + if (lastAttribute.Key == "{OriginalFormat}" + && lastAttribute.Value is string template) { - var lastAttribute = stateValues[stateValues.Count - 1]; - if (lastAttribute.Key == "{OriginalFormat}" - && lastAttribute.Value is string template) - { - this.Data.Body = template; - } - - this.Attributes = stateValues; + this.Data.Body = template; } - } - /// - /// Gets or sets the log timestamp. - /// - /// - /// Note: If is set to a value with it will be automatically converted to - /// UTC using . - /// - public DateTime Timestamp - { - get => this.Data.Timestamp; - set => this.Data.Timestamp = value; + this.Attributes = stateValues; } + } - /// - /// Gets or sets the log . - /// - public ActivityTraceId TraceId - { - get => this.Data.TraceId; - set => this.Data.TraceId = value; - } + /// + /// Gets or sets the log timestamp. + /// + /// + /// Note: If is set to a value with it will be automatically converted to + /// UTC using . + /// + public DateTime Timestamp + { + get => this.Data.Timestamp; + set => this.Data.Timestamp = value; + } - /// - /// Gets or sets the log . - /// - public ActivitySpanId SpanId - { - get => this.Data.SpanId; - set => this.Data.SpanId = value; - } + /// + /// Gets or sets the log . + /// + public ActivityTraceId TraceId + { + get => this.Data.TraceId; + set => this.Data.TraceId = value; + } - /// - /// Gets or sets the log . - /// - public ActivityTraceFlags TraceFlags - { - get => this.Data.TraceFlags; - set => this.Data.TraceFlags = value; - } + /// + /// Gets or sets the log . + /// + public ActivitySpanId SpanId + { + get => this.Data.SpanId; + set => this.Data.SpanId = value; + } - /// - /// Gets or sets the log trace state. - /// - /// - /// Note: Only set if is enabled. - /// - public string? TraceState - { - get => this.ILoggerData.TraceState; - set => this.ILoggerData.TraceState = value; - } + /// + /// Gets or sets the log . + /// + public ActivityTraceFlags TraceFlags + { + get => this.Data.TraceFlags; + set => this.Data.TraceFlags = value; + } - /// - /// Gets or sets the log category name. - /// - public string? CategoryName - { - get => this.ILoggerData.CategoryName; - set => this.ILoggerData.CategoryName = value; - } + /// + /// Gets or sets the log trace state. + /// + /// + /// Note: Only set if is enabled. + /// + public string? TraceState + { + get => this.ILoggerData.TraceState; + set => this.ILoggerData.TraceState = value; + } + + /// + /// Gets or sets the log category name. + /// + public string? CategoryName + { + get => this.ILoggerData.CategoryName; + set => this.ILoggerData.CategoryName = value; + } - /// - /// Gets or sets the log . - /// - // todo: [Obsolete("Use Severity instead LogLevel will be removed in a future version.")] - public LogLevel LogLevel + /// + /// Gets or sets the log . + /// + // todo: [Obsolete("Use Severity instead LogLevel will be removed in a future version.")] + public LogLevel LogLevel + { + get { - get + if (this.Data.Severity.HasValue) { - if (this.Data.Severity.HasValue) + uint severity = (uint)this.Data.Severity.Value; + if (severity >= 1 && severity <= 24) { - uint severity = (uint)this.Data.Severity.Value; - if (severity >= 1 && severity <= 24) - { - return (LogLevel)((severity - 1) / 4); - } + return (LogLevel)((severity - 1) / 4); } - - return LogLevel.Trace; } - set - { - OpenTelemetryLogger.SetLogRecordSeverityFields(ref this.Data, value); - } + return LogLevel.Trace; } - /// - /// Gets or sets the log . - /// - public EventId EventId + set { - get => this.ILoggerData.EventId; - set => this.ILoggerData.EventId = value; + OpenTelemetryLogger.SetLogRecordSeverityFields(ref this.Data, value); } + } - /// - /// Gets or sets the log formatted message. - /// - /// - /// Note: Set if is - /// enabled or {OriginalFormat} attribute (message template) is - /// not found. - /// - public string? FormattedMessage - { - get => this.ILoggerData.FormattedMessage; - set => this.ILoggerData.FormattedMessage = value; - } + /// + /// Gets or sets the log . + /// + public EventId EventId + { + get => this.ILoggerData.EventId; + set => this.ILoggerData.EventId = value; + } - /// - /// Gets or sets the log body. - /// - /// - /// Note: Set to the {OriginalFormat} attribute (message - /// template) if found otherwise the formatted log message. - /// - public string? Body - { - get => this.Data.Body; - set => this.Data.Body = value; - } + /// + /// Gets or sets the log formatted message. + /// + /// + /// Note: Set if is + /// enabled or {OriginalFormat} attribute (message template) is + /// not found. + /// + public string? FormattedMessage + { + get => this.ILoggerData.FormattedMessage; + set => this.ILoggerData.FormattedMessage = value; + } - /// - /// Gets or sets the raw state attached to the log. - /// - /// - /// Note: Set to when is enabled. - /// - [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.")] - public object? State - { - get => this.ILoggerData.State; - set => this.ILoggerData.State = value; - } + /// + /// Gets or sets the log body. + /// + /// + /// Note: Set to the {OriginalFormat} attribute (message + /// template) if found otherwise the formatted log message. + /// + public string? Body + { + get => this.Data.Body; + set => this.Data.Body = value; + } - /// - /// Gets or sets the state values attached to the log. - /// - /// - [Obsolete("Use Attributes instead StateValues will be removed in a future version.")] - public IReadOnlyList>? StateValues - { - get => this.Attributes; - set => this.Attributes = value; - } + /// + /// Gets or sets the raw state attached to the log. + /// + /// + /// Note: Set to when is enabled. + /// + [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.")] + public object? State + { + get => this.ILoggerData.State; + set => this.ILoggerData.State = value; + } - /// - /// Gets or sets the attributes attached to the log. - /// - /// - /// Note: Set when is enabled and - /// log record state implements or of s - /// (where TKey is string and TValue is object) or is enabled - /// otherwise . - /// - public IReadOnlyList>? Attributes { get; set; } - - /// - /// Gets or sets the log . - /// - public Exception? Exception - { - get => this.ILoggerData.Exception; - set => this.ILoggerData.Exception = value; - } + /// + /// Gets or sets the state values attached to the log. + /// + /// + [Obsolete("Use Attributes instead StateValues will be removed in a future version.")] + public IReadOnlyList>? StateValues + { + get => this.Attributes; + set => this.Attributes = value; + } - /// - /// Gets or sets the original string representation of the severity as it is - /// known at the source. - /// - internal string? SeverityText - { - get => this.Data.SeverityText; - set => this.Data.SeverityText = value; - } + /// + /// Gets or sets the attributes attached to the log. + /// + /// + /// Note: Set when is enabled and + /// log record state implements or of s + /// (where TKey is string and TValue is object) or is enabled + /// otherwise . + /// + public IReadOnlyList>? Attributes { get; set; } - /// - /// Gets or sets the log . - /// - internal LogRecordSeverity? Severity - { - get => this.Data.Severity; - set => this.Data.Severity = value; - } + /// + /// Gets or sets the log . + /// + public Exception? Exception + { + get => this.ILoggerData.Exception; + set => this.ILoggerData.Exception = value; + } - /// - /// Gets or sets the which emitted the . - /// - internal Logger? Logger { get; /*todo: internal*/ set; } - - /// - /// Executes callback for each currently active scope objects in order - /// of creation. All callbacks are guaranteed to be called inline from - /// this method. - /// - /// State. - /// The callback to be executed for every scope object. - /// The state object to be passed into the callback. - public void ForEachScope(Action callback, TState state) - { - Guard.ThrowIfNull(callback); + /// + /// Gets or sets the original string representation of the severity as it is + /// known at the source. + /// + internal string? SeverityText + { + get => this.Data.SeverityText; + set => this.Data.SeverityText = value; + } - var forEachScopeState = new ScopeForEachState(callback, state); + /// + /// Gets or sets the log . + /// + internal LogRecordSeverity? Severity + { + get => this.Data.Severity; + set => this.Data.Severity = value; + } - var bufferedScopes = this.ILoggerData.BufferedScopes; - if (bufferedScopes != null) - { - foreach (object? scope in bufferedScopes) - { - ScopeForEachState.ForEachScope(scope, forEachScopeState); - } - } - else + /// + /// Gets or sets the which emitted the . + /// + internal Logger? Logger { get; /*todo: internal*/ set; } + + /// + /// Executes callback for each currently active scope objects in order + /// of creation. All callbacks are guaranteed to be called inline from + /// this method. + /// + /// State. + /// The callback to be executed for every scope object. + /// The state object to be passed into the callback. + public void ForEachScope(Action callback, TState state) + { + Guard.ThrowIfNull(callback); + + var forEachScopeState = new ScopeForEachState(callback, state); + + var bufferedScopes = this.ILoggerData.BufferedScopes; + if (bufferedScopes != null) + { + foreach (object? scope in bufferedScopes) { - this.ILoggerData.ScopeProvider?.ForEachScope(ScopeForEachState.ForEachScope, forEachScopeState); + ScopeForEachState.ForEachScope(scope, forEachScopeState); } } - - /// - /// Gets a reference to the for the log message. - /// - /// . - internal ref LogRecordData GetDataRef() + else { - return ref this.Data; + this.ILoggerData.ScopeProvider?.ForEachScope(ScopeForEachState.ForEachScope, forEachScopeState); } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void ResetReferenceCount() - { - this.PoolReferenceCount = 1; - } + /// + /// Gets a reference to the for the log message. + /// + /// . + internal ref LogRecordData GetDataRef() + { + return ref this.Data; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void AddReference() - { - Interlocked.Increment(ref this.PoolReferenceCount); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ResetReferenceCount() + { + this.PoolReferenceCount = 1; + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal int RemoveReference() - { - return Interlocked.Decrement(ref this.PoolReferenceCount); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void AddReference() + { + Interlocked.Increment(ref this.PoolReferenceCount); + } - // Note: Typically called when LogRecords are added into a batch so they - // can be safely processed outside of the log call chain. - internal void Buffer() - { - // Note: Attributes are buffered because some states are not safe to - // access outside of the log call chain. See: - // https://github.com/open-telemetry/opentelemetry-dotnet/issues/2905 - this.BufferLogAttributes(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal int RemoveReference() + { + return Interlocked.Decrement(ref this.PoolReferenceCount); + } - this.BufferLogScopes(); - } + // Note: Typically called when LogRecords are added into a batch so they + // can be safely processed outside of the log call chain. + internal void Buffer() + { + // Note: Attributes are buffered because some states are not safe to + // access outside of the log call chain. See: + // https://github.com/open-telemetry/opentelemetry-dotnet/issues/2905 + this.BufferLogAttributes(); - internal LogRecord Copy() - { - // Note: We only buffer scopes here because attributes are copied - // directly below. - this.BufferLogScopes(); + this.BufferLogScopes(); + } - return new() - { - Data = this.Data, - ILoggerData = this.ILoggerData.Copy(), - Attributes = this.Attributes == null ? null : new List>(this.Attributes), - }; - } + internal LogRecord Copy() + { + // Note: We only buffer scopes here because attributes are copied + // directly below. + this.BufferLogScopes(); - /// - /// Buffers the attributes attached to the log into a list so that they - /// can be safely processed after the log message lifecycle has ended. - /// - private void BufferLogAttributes() + return new() { - var attributes = this.Attributes; - if (attributes == null || attributes == this.AttributeStorage) - { - return; - } + Data = this.Data, + ILoggerData = this.ILoggerData.Copy(), + Attributes = this.Attributes == null ? null : new List>(this.Attributes), + }; + } + + /// + /// Buffers the attributes attached to the log into a list so that they + /// can be safely processed after the log message lifecycle has ended. + /// + private void BufferLogAttributes() + { + var attributes = this.Attributes; + if (attributes == null || attributes == this.AttributeStorage) + { + return; + } - var attributeStorage = this.AttributeStorage ??= new List>(attributes.Count); + var attributeStorage = this.AttributeStorage ??= new List>(attributes.Count); - // Note: AddRange here will copy all of the KeyValuePairs from - // attributes to AttributeStorage. This "captures" the state and - // fixes issues where the values are generated at enumeration time - // like - // https://github.com/open-telemetry/opentelemetry-dotnet/issues/2905. - attributeStorage.AddRange(attributes); + // Note: AddRange here will copy all of the KeyValuePairs from + // attributes to AttributeStorage. This "captures" the state and + // fixes issues where the values are generated at enumeration time + // like + // https://github.com/open-telemetry/opentelemetry-dotnet/issues/2905. + attributeStorage.AddRange(attributes); - this.Attributes = attributeStorage; - } + this.Attributes = attributeStorage; + } - /// - /// Buffers the scopes attached to the log into a list so that they can - /// be safely processed after the log message lifecycle has ended. - /// - private void BufferLogScopes() + /// + /// Buffers the scopes attached to the log into a list so that they can + /// be safely processed after the log message lifecycle has ended. + /// + private void BufferLogScopes() + { + var scopeProvider = this.ILoggerData.ScopeProvider; + if (scopeProvider == null) { - var scopeProvider = this.ILoggerData.ScopeProvider; - if (scopeProvider == null) - { - return; - } + return; + } - var scopeStorage = this.ScopeStorage ??= new List(LogRecordPoolHelper.DefaultMaxNumberOfScopes); + var scopeStorage = this.ScopeStorage ??= new List(LogRecordPoolHelper.DefaultMaxNumberOfScopes); - scopeProvider.ForEachScope(AddScopeToBufferedList, scopeStorage); + scopeProvider.ForEachScope(AddScopeToBufferedList, scopeStorage); - this.ILoggerData.ScopeProvider = null; + this.ILoggerData.ScopeProvider = null; - this.ILoggerData.BufferedScopes = scopeStorage; - } + this.ILoggerData.BufferedScopes = scopeStorage; + } - internal struct LogRecordILoggerData + internal struct LogRecordILoggerData + { + public string? TraceState; + public string? CategoryName; + public EventId EventId; + public string? FormattedMessage; + public Exception? Exception; + public object? State; + public IExternalScopeProvider? ScopeProvider; + public List? BufferedScopes; + + public LogRecordILoggerData Copy() { - public string? TraceState; - public string? CategoryName; - public EventId EventId; - public string? FormattedMessage; - public Exception? Exception; - public object? State; - public IExternalScopeProvider? ScopeProvider; - public List? BufferedScopes; - - public LogRecordILoggerData Copy() + var copy = new LogRecordILoggerData { - var copy = new LogRecordILoggerData - { - TraceState = this.TraceState, - CategoryName = this.CategoryName, - EventId = this.EventId, - FormattedMessage = this.FormattedMessage, - Exception = this.Exception, - State = this.State, - }; - - var bufferedScopes = this.BufferedScopes; - if (bufferedScopes != null) - { - copy.BufferedScopes = new List(bufferedScopes); - } + TraceState = this.TraceState, + CategoryName = this.CategoryName, + EventId = this.EventId, + FormattedMessage = this.FormattedMessage, + Exception = this.Exception, + State = this.State, + }; - return copy; + var bufferedScopes = this.BufferedScopes; + if (bufferedScopes != null) + { + copy.BufferedScopes = new List(bufferedScopes); } + + return copy; } + } - private readonly struct ScopeForEachState + private readonly struct ScopeForEachState + { + public static readonly Action> ForEachScope = (object? scope, ScopeForEachState state) => { - public static readonly Action> ForEachScope = (object? scope, ScopeForEachState state) => - { - LogRecordScope logRecordScope = new LogRecordScope(scope); + LogRecordScope logRecordScope = new LogRecordScope(scope); - state.Callback(logRecordScope, state.UserState); - }; + state.Callback(logRecordScope, state.UserState); + }; - public readonly Action Callback; + public readonly Action Callback; - public readonly TState UserState; + public readonly TState UserState; - public ScopeForEachState(Action callback, TState state) - { - this.Callback = callback; - this.UserState = state; - } + public ScopeForEachState(Action callback, TState state) + { + this.Callback = callback; + this.UserState = state; } } } diff --git a/src/OpenTelemetry/Logs/LogRecordScope.cs b/src/OpenTelemetry/Logs/LogRecordScope.cs index 03ea85873fc..89f944c72b0 100644 --- a/src/OpenTelemetry/Logs/LogRecordScope.cs +++ b/src/OpenTelemetry/Logs/LogRecordScope.cs @@ -18,89 +18,88 @@ using System.Collections; -namespace OpenTelemetry.Logs +namespace OpenTelemetry.Logs; + +/// +/// Stores details about a scope attached to a log message. +/// +public readonly struct LogRecordScope { + internal LogRecordScope(object? scope) + { + this.Scope = scope; + } + /// - /// Stores details about a scope attached to a log message. + /// Gets the raw scope value. /// - public readonly struct LogRecordScope - { - internal LogRecordScope(object? scope) - { - this.Scope = scope; - } + public object? Scope { get; } - /// - /// Gets the raw scope value. - /// - public object? Scope { get; } + /// + /// Gets an for looping over the inner values + /// of the scope. + /// + /// . + public Enumerator GetEnumerator() => new(this.Scope); - /// - /// Gets an for looping over the inner values - /// of the scope. - /// - /// . - public Enumerator GetEnumerator() => new(this.Scope); + /// + /// LogRecordScope enumerator. + /// + public struct Enumerator : IEnumerator> + { + private readonly IReadOnlyList> scope; + private int position; /// - /// LogRecordScope enumerator. + /// Initializes a new instance of the struct. /// - public struct Enumerator : IEnumerator> + /// Scope. + public Enumerator(object? scope) { - private readonly IReadOnlyList> scope; - private int position; - - /// - /// Initializes a new instance of the struct. - /// - /// Scope. - public Enumerator(object? scope) + if (scope is IReadOnlyList> scopeList) { - if (scope is IReadOnlyList> scopeList) - { - this.scope = scopeList; - } - else if (scope is IEnumerable> scopeEnumerable) - { - this.scope = new List>(scopeEnumerable); - } - else + this.scope = scopeList; + } + else if (scope is IEnumerable> scopeEnumerable) + { + this.scope = new List>(scopeEnumerable); + } + else + { + this.scope = new List> { - this.scope = new List> - { - new KeyValuePair(string.Empty, scope), - }; - } - - this.position = 0; - this.Current = default; + new KeyValuePair(string.Empty, scope), + }; } - /// - public KeyValuePair Current { get; private set; } + this.position = 0; + this.Current = default; + } - object IEnumerator.Current => this.Current; + /// + public KeyValuePair Current { get; private set; } - /// - public bool MoveNext() - { - if (this.position < this.scope.Count) - { - this.Current = this.scope[this.position++]; - return true; - } - - return false; - } + object IEnumerator.Current => this.Current; - /// - public void Dispose() + /// + public bool MoveNext() + { + if (this.position < this.scope.Count) { + this.Current = this.scope[this.position++]; + return true; } - /// - public void Reset() - => throw new NotSupportedException(); + return false; } + + /// + public void Dispose() + { + } + + /// + public void Reset() + => throw new NotSupportedException(); } } diff --git a/src/OpenTelemetry/Logs/Pool/ILogRecordPool.cs b/src/OpenTelemetry/Logs/Pool/ILogRecordPool.cs index 14d12cc6e36..5cbfb20eb85 100644 --- a/src/OpenTelemetry/Logs/Pool/ILogRecordPool.cs +++ b/src/OpenTelemetry/Logs/Pool/ILogRecordPool.cs @@ -16,12 +16,11 @@ #nullable enable -namespace OpenTelemetry.Logs +namespace OpenTelemetry.Logs; + +internal interface ILogRecordPool { - internal interface ILogRecordPool - { - LogRecord Rent(); + LogRecord Rent(); - void Return(LogRecord logRecord); - } + void Return(LogRecord logRecord); } diff --git a/src/OpenTelemetry/Logs/Pool/LogRecordPoolHelper.cs b/src/OpenTelemetry/Logs/Pool/LogRecordPoolHelper.cs index e024e72063e..076060558d5 100644 --- a/src/OpenTelemetry/Logs/Pool/LogRecordPoolHelper.cs +++ b/src/OpenTelemetry/Logs/Pool/LogRecordPoolHelper.cs @@ -16,45 +16,44 @@ #nullable enable -namespace OpenTelemetry.Logs +namespace OpenTelemetry.Logs; + +internal static class LogRecordPoolHelper { - internal static class LogRecordPoolHelper - { - public const int DefaultMaxNumberOfAttributes = 64; - public const int DefaultMaxNumberOfScopes = 16; + public const int DefaultMaxNumberOfAttributes = 64; + public const int DefaultMaxNumberOfScopes = 16; - public static void Clear(LogRecord logRecord) + public static void Clear(LogRecord logRecord) + { + var attributeStorage = logRecord.AttributeStorage; + if (attributeStorage != null) { - var attributeStorage = logRecord.AttributeStorage; - if (attributeStorage != null) + if (attributeStorage.Count > DefaultMaxNumberOfAttributes) + { + // Don't allow the pool to grow unconstained. + logRecord.AttributeStorage = null; + } + else { - if (attributeStorage.Count > DefaultMaxNumberOfAttributes) - { - // Don't allow the pool to grow unconstained. - logRecord.AttributeStorage = null; - } - else - { - /* List.Clear sets the count/size to 0 but it maintains the - underlying array (capacity). */ - attributeStorage.Clear(); - } + /* List.Clear sets the count/size to 0 but it maintains the + underlying array (capacity). */ + attributeStorage.Clear(); } + } - var scopeStorage = logRecord.ScopeStorage; - if (scopeStorage != null) + var scopeStorage = logRecord.ScopeStorage; + if (scopeStorage != null) + { + if (scopeStorage.Count > DefaultMaxNumberOfScopes) + { + // Don't allow the pool to grow unconstained. + logRecord.ScopeStorage = null; + } + else { - if (scopeStorage.Count > DefaultMaxNumberOfScopes) - { - // Don't allow the pool to grow unconstained. - logRecord.ScopeStorage = null; - } - else - { - /* List.Clear sets the count/size to 0 but it maintains the - underlying array (capacity). */ - scopeStorage.Clear(); - } + /* List.Clear sets the count/size to 0 but it maintains the + underlying array (capacity). */ + scopeStorage.Clear(); } } } diff --git a/src/OpenTelemetry/Logs/Pool/LogRecordSharedPool.cs b/src/OpenTelemetry/Logs/Pool/LogRecordSharedPool.cs index 725d5cc1a2d..65dc5a7a8c0 100644 --- a/src/OpenTelemetry/Logs/Pool/LogRecordSharedPool.cs +++ b/src/OpenTelemetry/Logs/Pool/LogRecordSharedPool.cs @@ -19,130 +19,129 @@ using System.Diagnostics.CodeAnalysis; using OpenTelemetry.Internal; -namespace OpenTelemetry.Logs +namespace OpenTelemetry.Logs; + +internal sealed class LogRecordSharedPool : ILogRecordPool { - internal sealed class LogRecordSharedPool : ILogRecordPool - { - public const int DefaultMaxPoolSize = 2048; + public const int DefaultMaxPoolSize = 2048; - public static LogRecordSharedPool Current = new(DefaultMaxPoolSize); + public static LogRecordSharedPool Current = new(DefaultMaxPoolSize); - public readonly int Capacity; - private readonly LogRecord?[] pool; - private long rentIndex; - private long returnIndex; + public readonly int Capacity; + private readonly LogRecord?[] pool; + private long rentIndex; + private long returnIndex; - public LogRecordSharedPool(int capacity) - { - this.Capacity = capacity; - this.pool = new LogRecord?[capacity]; - } + public LogRecordSharedPool(int capacity) + { + this.Capacity = capacity; + this.pool = new LogRecord?[capacity]; + } - public int Count => (int)(Volatile.Read(ref this.returnIndex) - Volatile.Read(ref this.rentIndex)); + public int Count => (int)(Volatile.Read(ref this.returnIndex) - Volatile.Read(ref this.rentIndex)); - // Note: It might make sense to expose this (somehow) in the future. - // Ideal config is shared pool capacity == max batch size. - public static void Resize(int capacity) - { - Guard.ThrowIfOutOfRange(capacity, min: 1); + // Note: It might make sense to expose this (somehow) in the future. + // Ideal config is shared pool capacity == max batch size. + public static void Resize(int capacity) + { + Guard.ThrowIfOutOfRange(capacity, min: 1); - Current = new(capacity); - } + Current = new(capacity); + } - public LogRecord Rent() + public LogRecord Rent() + { + while (true) { - while (true) + var rentSnapshot = Volatile.Read(ref this.rentIndex); + var returnSnapshot = Volatile.Read(ref this.returnIndex); + + if (rentSnapshot >= returnSnapshot) { - var rentSnapshot = Volatile.Read(ref this.rentIndex); - var returnSnapshot = Volatile.Read(ref this.returnIndex); + break; // buffer is empty + } - if (rentSnapshot >= returnSnapshot) + if (Interlocked.CompareExchange(ref this.rentIndex, rentSnapshot + 1, rentSnapshot) == rentSnapshot) + { + var logRecord = Interlocked.Exchange(ref this.pool[rentSnapshot % this.Capacity], null); + if (logRecord == null && !this.TryRentCoreRare(rentSnapshot, out logRecord)) { - break; // buffer is empty + continue; } - if (Interlocked.CompareExchange(ref this.rentIndex, rentSnapshot + 1, rentSnapshot) == rentSnapshot) - { - var logRecord = Interlocked.Exchange(ref this.pool[rentSnapshot % this.Capacity], null); - if (logRecord == null && !this.TryRentCoreRare(rentSnapshot, out logRecord)) - { - continue; - } - - logRecord.ResetReferenceCount(); - return logRecord; - } + logRecord.ResetReferenceCount(); + return logRecord; } + } - var newLogRecord = new LogRecord(); - newLogRecord.ResetReferenceCount(); - return newLogRecord; + var newLogRecord = new LogRecord(); + newLogRecord.ResetReferenceCount(); + return newLogRecord; + } + + public void Return(LogRecord logRecord) + { + if (logRecord.RemoveReference() != 0) + { + return; } - public void Return(LogRecord logRecord) + LogRecordPoolHelper.Clear(logRecord); + + while (true) { - if (logRecord.RemoveReference() != 0) + var rentSnapshot = Volatile.Read(ref this.rentIndex); + var returnSnapshot = Volatile.Read(ref this.returnIndex); + + if (returnSnapshot - rentSnapshot >= this.Capacity) { - return; + return; // buffer is full } - LogRecordPoolHelper.Clear(logRecord); - - while (true) + if (Interlocked.CompareExchange(ref this.returnIndex, returnSnapshot + 1, returnSnapshot) == returnSnapshot) { - var rentSnapshot = Volatile.Read(ref this.rentIndex); - var returnSnapshot = Volatile.Read(ref this.returnIndex); - - if (returnSnapshot - rentSnapshot >= this.Capacity) - { - return; // buffer is full - } - - if (Interlocked.CompareExchange(ref this.returnIndex, returnSnapshot + 1, returnSnapshot) == returnSnapshot) - { - // If many threads are hammering rent/return it is possible - // for two threads to write to the same index. In that case - // only one of the logRecords will make it back into the - // pool. Anything lost in the race will collected by the GC - // and the pool will issue new instances as needed. This - // could be abated by an Interlocked.CompareExchange here - // but for the general use case of an exporter returning - // records one-by-one, better to keep this fast and not pay - // for Interlocked.CompareExchange. The race is more - // theoretical. - this.pool[returnSnapshot % this.Capacity] = logRecord; - return; - } + // If many threads are hammering rent/return it is possible + // for two threads to write to the same index. In that case + // only one of the logRecords will make it back into the + // pool. Anything lost in the race will collected by the GC + // and the pool will issue new instances as needed. This + // could be abated by an Interlocked.CompareExchange here + // but for the general use case of an exporter returning + // records one-by-one, better to keep this fast and not pay + // for Interlocked.CompareExchange. The race is more + // theoretical. + this.pool[returnSnapshot % this.Capacity] = logRecord; + return; } } + } - private bool TryRentCoreRare(long rentSnapshot, [NotNullWhen(true)] out LogRecord? logRecord) + private bool TryRentCoreRare(long rentSnapshot, [NotNullWhen(true)] out LogRecord? logRecord) + { + SpinWait wait = default; + while (true) { - SpinWait wait = default; - while (true) + if (wait.NextSpinWillYield) { - if (wait.NextSpinWillYield) - { - // Super rare case. If many threads are hammering - // rent/return it is possible a read was issued an index and - // then yielded while other threads caused the pointers to - // wrap around. When the yielded thread wakes up its read - // index could have been stolen by another thread. To - // prevent deadlock, bail out of read after spinning. This - // will cause either a successful rent from another index, - // or a new record to be created - logRecord = null; - return false; - } + // Super rare case. If many threads are hammering + // rent/return it is possible a read was issued an index and + // then yielded while other threads caused the pointers to + // wrap around. When the yielded thread wakes up its read + // index could have been stolen by another thread. To + // prevent deadlock, bail out of read after spinning. This + // will cause either a successful rent from another index, + // or a new record to be created + logRecord = null; + return false; + } - wait.SpinOnce(); + wait.SpinOnce(); - logRecord = Interlocked.Exchange(ref this.pool[rentSnapshot % this.Capacity], null); - if (logRecord != null) - { - // Rare case where the write was still working when the read came in - return true; - } + logRecord = Interlocked.Exchange(ref this.pool[rentSnapshot % this.Capacity], null); + if (logRecord != null) + { + // Rare case where the write was still working when the read came in + return true; } } } diff --git a/src/OpenTelemetry/Logs/Pool/LogRecordThreadStaticPool.cs b/src/OpenTelemetry/Logs/Pool/LogRecordThreadStaticPool.cs index d72f0754017..f44064985f4 100644 --- a/src/OpenTelemetry/Logs/Pool/LogRecordThreadStaticPool.cs +++ b/src/OpenTelemetry/Logs/Pool/LogRecordThreadStaticPool.cs @@ -16,38 +16,37 @@ #nullable enable -namespace OpenTelemetry.Logs +namespace OpenTelemetry.Logs; + +internal sealed class LogRecordThreadStaticPool : ILogRecordPool { - internal sealed class LogRecordThreadStaticPool : ILogRecordPool - { - [ThreadStatic] - public static LogRecord? Storage; + [ThreadStatic] + public static LogRecord? Storage; - private LogRecordThreadStaticPool() - { - } + private LogRecordThreadStaticPool() + { + } - public static LogRecordThreadStaticPool Instance { get; } = new(); + public static LogRecordThreadStaticPool Instance { get; } = new(); - public LogRecord Rent() + public LogRecord Rent() + { + var logRecord = Storage; + if (logRecord != null) { - var logRecord = Storage; - if (logRecord != null) - { - Storage = null; - return logRecord; - } - - return new(); + Storage = null; + return logRecord; } - public void Return(LogRecord logRecord) + return new(); + } + + public void Return(LogRecord logRecord) + { + if (Storage == null) { - if (Storage == null) - { - LogRecordPoolHelper.Clear(logRecord); - Storage = logRecord; - } + LogRecordPoolHelper.Clear(logRecord); + Storage = logRecord; } } } diff --git a/src/OpenTelemetry/Logs/SimpleLogRecordExportProcessor.cs b/src/OpenTelemetry/Logs/SimpleLogRecordExportProcessor.cs index 3ac9498601d..8af6322a3ff 100644 --- a/src/OpenTelemetry/Logs/SimpleLogRecordExportProcessor.cs +++ b/src/OpenTelemetry/Logs/SimpleLogRecordExportProcessor.cs @@ -18,20 +18,19 @@ using OpenTelemetry.Logs; -namespace OpenTelemetry +namespace OpenTelemetry; + +/// +/// Implements a simple log record export processor. +/// +public class SimpleLogRecordExportProcessor : SimpleExportProcessor { /// - /// Implements a simple log record export processor. + /// Initializes a new instance of the class. /// - public class SimpleLogRecordExportProcessor : SimpleExportProcessor + /// Log record exporter. + public SimpleLogRecordExportProcessor(BaseExporter exporter) + : base(exporter) { - /// - /// Initializes a new instance of the class. - /// - /// Log record exporter. - public SimpleLogRecordExportProcessor(BaseExporter exporter) - : base(exporter) - { - } } } diff --git a/src/OpenTelemetry/ProviderExtensions.cs b/src/OpenTelemetry/ProviderExtensions.cs index 936652cf594..966615b0ba7 100644 --- a/src/OpenTelemetry/ProviderExtensions.cs +++ b/src/OpenTelemetry/ProviderExtensions.cs @@ -21,82 +21,81 @@ using OpenTelemetry.Resources; using OpenTelemetry.Trace; -namespace OpenTelemetry +namespace OpenTelemetry; + +/// +/// Contains provider extension methods. +/// +public static class ProviderExtensions { /// - /// Contains provider extension methods. + /// Gets the associated with the . /// - public static class ProviderExtensions + /// . + /// if found otherwise . + public static Resource GetResource(this BaseProvider baseProvider) { - /// - /// Gets the associated with the . - /// - /// . - /// if found otherwise . - public static Resource GetResource(this BaseProvider baseProvider) + if (baseProvider is TracerProviderSdk tracerProviderSdk) { - if (baseProvider is TracerProviderSdk tracerProviderSdk) - { - return tracerProviderSdk.Resource; - } - else if (baseProvider is MeterProviderSdk meterProviderSdk) - { - return meterProviderSdk.Resource; - } - else if (baseProvider is LoggerProviderSdk loggerProviderSdk) - { - return loggerProviderSdk.Resource; - } - else if (baseProvider is OpenTelemetryLoggerProvider openTelemetryLoggerProvider) - { - return openTelemetryLoggerProvider.Provider.GetResource(); - } - - return Resource.Empty; + return tracerProviderSdk.Resource; } - - /// - /// Gets the associated with the . - /// - /// . - /// if found otherwise . - public static Resource GetDefaultResource(this BaseProvider baseProvider) + else if (baseProvider is MeterProviderSdk meterProviderSdk) { - var builder = ResourceBuilder.CreateDefault(); - builder.ServiceProvider = GetServiceProvider(baseProvider); - return builder.Build(); + return meterProviderSdk.Resource; } - - internal static IServiceProvider? GetServiceProvider(this BaseProvider baseProvider) + else if (baseProvider is LoggerProviderSdk loggerProviderSdk) { - if (baseProvider is TracerProviderSdk tracerProviderSdk) - { - return tracerProviderSdk.ServiceProvider; - } - else if (baseProvider is MeterProviderSdk meterProviderSdk) - { - return meterProviderSdk.ServiceProvider; - } - else if (baseProvider is LoggerProviderSdk loggerProviderSdk) - { - return loggerProviderSdk.ServiceProvider; - } - else if (baseProvider is OpenTelemetryLoggerProvider openTelemetryLoggerProvider) - { - return openTelemetryLoggerProvider.Provider.GetServiceProvider(); - } - - return null; + return loggerProviderSdk.Resource; + } + else if (baseProvider is OpenTelemetryLoggerProvider openTelemetryLoggerProvider) + { + return openTelemetryLoggerProvider.Provider.GetResource(); } - internal static Action? GetObservableInstrumentCollectCallback(this BaseProvider baseProvider) + return Resource.Empty; + } + + /// + /// Gets the associated with the . + /// + /// . + /// if found otherwise . + public static Resource GetDefaultResource(this BaseProvider baseProvider) + { + var builder = ResourceBuilder.CreateDefault(); + builder.ServiceProvider = GetServiceProvider(baseProvider); + return builder.Build(); + } + + internal static IServiceProvider? GetServiceProvider(this BaseProvider baseProvider) + { + if (baseProvider is TracerProviderSdk tracerProviderSdk) + { + return tracerProviderSdk.ServiceProvider; + } + else if (baseProvider is MeterProviderSdk meterProviderSdk) + { + return meterProviderSdk.ServiceProvider; + } + else if (baseProvider is LoggerProviderSdk loggerProviderSdk) + { + return loggerProviderSdk.ServiceProvider; + } + else if (baseProvider is OpenTelemetryLoggerProvider openTelemetryLoggerProvider) { - if (baseProvider is MeterProviderSdk meterProviderSdk) - { - return meterProviderSdk.CollectObservableInstruments; - } + return openTelemetryLoggerProvider.Provider.GetServiceProvider(); + } + + return null; + } - return null; + internal static Action? GetObservableInstrumentCollectCallback(this BaseProvider baseProvider) + { + if (baseProvider is MeterProviderSdk meterProviderSdk) + { + return meterProviderSdk.CollectObservableInstruments; } + + return null; } }