diff --git a/src/OpenTelemetry/.publicApi/net462/PublicAPI.Shipped.txt b/src/OpenTelemetry/.publicApi/net462/PublicAPI.Shipped.txt index 56774a7fc4d..4ada116f14e 100644 --- a/src/OpenTelemetry/.publicApi/net462/PublicAPI.Shipped.txt +++ b/src/OpenTelemetry/.publicApi/net462/PublicAPI.Shipped.txt @@ -5,7 +5,7 @@ ~OpenTelemetry.BaseExporter.ParentProvider.get -> OpenTelemetry.BaseProvider ~OpenTelemetry.BaseExportProcessor ~OpenTelemetry.BaseExportProcessor.BaseExportProcessor(OpenTelemetry.BaseExporter exporter) -> void -~OpenTelemetry.BaseProcessor.ParentProvider.get -> OpenTelemetry.BaseProvider +OpenTelemetry.BaseProcessor.ParentProvider.get -> OpenTelemetry.BaseProvider? ~OpenTelemetry.Batch ~OpenTelemetry.Batch.Batch(T[] items, int count) -> void ~OpenTelemetry.Batch.Enumerator.Current.get -> T diff --git a/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt index e69de29bb2d..7ead6b2aa97 100644 --- a/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/net462/PublicAPI.Unshipped.txt @@ -0,0 +1,3 @@ +OpenTelemetry.Logs.OpenTelemetryLoggerProvider.ForceFlush(int timeoutMilliseconds = -1) -> bool +OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider() -> void +OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider(System.Action! configure) -> void \ No newline at end of file diff --git a/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Shipped.txt b/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Shipped.txt index 56774a7fc4d..4ada116f14e 100644 --- a/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Shipped.txt +++ b/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Shipped.txt @@ -5,7 +5,7 @@ ~OpenTelemetry.BaseExporter.ParentProvider.get -> OpenTelemetry.BaseProvider ~OpenTelemetry.BaseExportProcessor ~OpenTelemetry.BaseExportProcessor.BaseExportProcessor(OpenTelemetry.BaseExporter exporter) -> void -~OpenTelemetry.BaseProcessor.ParentProvider.get -> OpenTelemetry.BaseProvider +OpenTelemetry.BaseProcessor.ParentProvider.get -> OpenTelemetry.BaseProvider? ~OpenTelemetry.Batch ~OpenTelemetry.Batch.Batch(T[] items, int count) -> void ~OpenTelemetry.Batch.Enumerator.Current.get -> T diff --git a/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt b/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt index e69de29bb2d..7ead6b2aa97 100644 --- a/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/OpenTelemetry/.publicApi/netstandard2.0/PublicAPI.Unshipped.txt @@ -0,0 +1,3 @@ +OpenTelemetry.Logs.OpenTelemetryLoggerProvider.ForceFlush(int timeoutMilliseconds = -1) -> bool +OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider() -> void +OpenTelemetry.Logs.OpenTelemetryLoggerProvider.OpenTelemetryLoggerProvider(System.Action! configure) -> void \ No newline at end of file diff --git a/src/OpenTelemetry/BaseProcessor.cs b/src/OpenTelemetry/BaseProcessor.cs index 4115b108ee6..02df50e39bf 100644 --- a/src/OpenTelemetry/BaseProcessor.cs +++ b/src/OpenTelemetry/BaseProcessor.cs @@ -14,6 +14,8 @@ // limitations under the License. // +#nullable enable + using System; using System.Threading; using OpenTelemetry.Internal; @@ -26,12 +28,21 @@ namespace OpenTelemetry /// The type of object to be processed. public abstract class BaseProcessor : IDisposable { + private readonly string typeName; private int shutdownCount; + /// + /// Initializes a new instance of the class. + /// + public BaseProcessor() + { + this.typeName = this.GetType().Name; + } + /// /// Gets the parent . /// - public BaseProvider ParentProvider { get; private set; } + public BaseProvider? ParentProvider { get; private set; } /// /// Called synchronously when a telemetry object is started. @@ -86,7 +97,11 @@ public bool ForceFlush(int timeoutMilliseconds = Timeout.Infinite) try { - return this.OnForceFlush(timeoutMilliseconds); + bool result = this.OnForceFlush(timeoutMilliseconds); + + OpenTelemetrySdkEventSource.Log.ProcessorForceFlushInvoked(this.typeName, result); + + return result; } catch (Exception ex) { diff --git a/src/OpenTelemetry/CHANGELOG.md b/src/OpenTelemetry/CHANGELOG.md index 13f7b12e21d..b1d54a823ca 100644 --- a/src/OpenTelemetry/CHANGELOG.md +++ b/src/OpenTelemetry/CHANGELOG.md @@ -14,6 +14,9 @@ * `CompositeProcessor` will now ensure `ParentProvider` is set on its children ([#3368](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3368)) +* Added `ForceFlush` and helper ctors on `OpenTelemetryLoggerProvider` + ([#3364](https://github.com/open-telemetry/opentelemetry-dotnet/pull/3364)) + ## 1.3.0 Released 2022-Jun-03 diff --git a/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs b/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs index 0db72d4cba7..1dcd65fd94b 100644 --- a/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs +++ b/src/OpenTelemetry/Internal/OpenTelemetrySdkEventSource.cs @@ -390,6 +390,12 @@ public void UnsupportedAttributeType(string type, string key) this.WriteEvent(42, type.ToString(), key); } + [Event(43, Message = "ForceFlush invoked for processor type '{0}' returned result '{1}'.", Level = EventLevel.Verbose)] + public void ProcessorForceFlushInvoked(string processorType, bool result) + { + this.WriteEvent(43, processorType, result); + } + #if DEBUG public class OpenTelemetryEventListener : EventListener { diff --git a/src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs b/src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs index d4a7a2d15da..c057445c84b 100644 --- a/src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs +++ b/src/OpenTelemetry/Logs/OpenTelemetryLoggerProvider.cs @@ -16,7 +16,9 @@ #nullable enable +using System; using System.Collections; +using System.Threading; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using OpenTelemetry.Internal; @@ -54,6 +56,23 @@ public OpenTelemetryLoggerProvider(IOptionsMonitor o { } + /// + /// Initializes a new instance of the class. + /// + /// configuration callback. + public OpenTelemetryLoggerProvider(Action configure) + : this(BuildOptions(configure ?? throw new ArgumentNullException(nameof(configure)))) + { + } + + /// + /// Initializes a new instance of the class. + /// + public OpenTelemetryLoggerProvider() + : this(BuildOptions(configure: null)) + { + } + internal OpenTelemetryLoggerProvider(OpenTelemetryLoggerOptions options) { Guard.ThrowIfNull(options); @@ -112,6 +131,29 @@ public ILogger CreateLogger(string categoryName) return logger; } + /// + /// Flushes all the processors registered under , blocks the current thread + /// until flush completed, shutdown signaled or timed out. + /// + /// + /// The number (non-negative) of milliseconds to wait, or + /// Timeout.Infinite to wait indefinitely. + /// + /// + /// Returns true when force flush succeeded; otherwise, false. + /// + /// + /// Thrown when the timeoutMilliseconds is smaller than -1. + /// + /// + /// This function guarantees thread-safety. + /// + public bool ForceFlush(int timeoutMilliseconds = Timeout.Infinite) + { + return this.Processor?.ForceFlush(timeoutMilliseconds) ?? true; + } + internal OpenTelemetryLoggerProvider AddProcessor(BaseProcessor processor) { Guard.ThrowIfNull(processor); @@ -158,5 +200,12 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } + + private static OpenTelemetryLoggerOptions BuildOptions(Action? configure) + { + OpenTelemetryLoggerOptions options = new(); + configure?.Invoke(options); + return options; + } } } diff --git a/test/OpenTelemetry.Tests/Logs/OpenTelemetryLoggerProviderTests.cs b/test/OpenTelemetry.Tests/Logs/OpenTelemetryLoggerProviderTests.cs new file mode 100644 index 00000000000..330fa4b50e7 --- /dev/null +++ b/test/OpenTelemetry.Tests/Logs/OpenTelemetryLoggerProviderTests.cs @@ -0,0 +1,89 @@ +// +// 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. +// + +using System.Collections.Generic; +using Microsoft.Extensions.Logging; +using OpenTelemetry.Exporter; +using OpenTelemetry.Resources; +using Xunit; + +namespace OpenTelemetry.Logs.Tests +{ + public sealed class OpenTelemetryLoggerProviderTests + { + [Fact] + public void DefaultCtorTests() + { + OpenTelemetryLoggerOptions defaults = new(); + + using OpenTelemetryLoggerProvider provider = new(); + + Assert.Equal(defaults.IncludeScopes, provider.IncludeScopes); + Assert.Equal(defaults.IncludeFormattedMessage, provider.IncludeFormattedMessage); + Assert.Equal(defaults.ParseStateValues, provider.ParseStateValues); + Assert.Null(provider.Processor); + Assert.NotNull(provider.Resource); + } + + [Fact] + public void ConfigureCtorTests() + { + OpenTelemetryLoggerOptions defaults = new(); + + using OpenTelemetryLoggerProvider provider = new(options => + { + options.IncludeScopes = !defaults.IncludeScopes; + options.IncludeFormattedMessage = !defaults.IncludeFormattedMessage; + options.ParseStateValues = !defaults.ParseStateValues; + + options.SetResourceBuilder(ResourceBuilder + .CreateEmpty() + .AddAttributes(new[] { new KeyValuePair("key1", "value1") })); + + options.AddInMemoryExporter(new List()); + }); + + Assert.Equal(!defaults.IncludeScopes, provider.IncludeScopes); + Assert.Equal(!defaults.IncludeFormattedMessage, provider.IncludeFormattedMessage); + Assert.Equal(!defaults.ParseStateValues, provider.ParseStateValues); + Assert.NotNull(provider.Processor); + Assert.NotNull(provider.Resource); + Assert.Contains(provider.Resource.Attributes, value => value.Key == "key1" && (string)value.Value == "value1"); + } + + [Fact] + public void ForceFlushTest() + { + using OpenTelemetryLoggerProvider provider = new(); + + Assert.True(provider.ForceFlush()); + + List exportedItems = new(); + + provider.AddProcessor(new BatchLogRecordExportProcessor(new InMemoryExporter(exportedItems))); + + var logger = provider.CreateLogger("TestLogger"); + + logger.LogInformation("hello world"); + + Assert.Empty(exportedItems); + + Assert.True(provider.ForceFlush()); + + Assert.Single(exportedItems); + } + } +}