Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions src/OpenTelemetry/Logs/LoggerProviderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// <copyright file="LoggerProviderExtensions.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>
/// Contains extension methods for the <see cref="LoggerProvider"/> class.
/// </summary>
internal static class LoggerProviderExtensions
{
/// <summary>
/// Add a processor to the <see cref="LoggerProvider"/>.
/// </summary>
/// <remarks>
/// Note: The supplied <paramref name="processor"/> will be
/// automatically disposed when then the <see
/// cref="LoggerProvider"/> is disposed.
/// </remarks>
/// <param name="provider"><see cref="LoggerProvider"/> instance on which ForceFlush will be called.</param>
/// <param name="processor">Log processor to add.</param>
/// <returns>The supplied <see cref="OpenTelemetryLoggerOptions"/> for chaining.</returns>
public static LoggerProvider AddProcessor(this LoggerProvider provider, BaseProcessor<LogRecord> processor)
{
Guard.ThrowIfNull(provider);
Guard.ThrowIfNull(processor);

if (provider is LoggerProviderSdk loggerProviderSdk)
{
loggerProviderSdk.AddProcessor(processor);
}

return provider;
}

/// <summary>
/// Flushes all the processors registered under <see cref="LoggerProvider"/>, blocks the current thread
/// until flush completed, shutdown signaled or timed out.
/// </summary>
/// <param name="provider"><see cref="LoggerProvider"/> instance on which ForceFlush will be called.</param>
/// <param name="timeoutMilliseconds">
/// The number (non-negative) of milliseconds to wait, or
/// <c>Timeout.Infinite</c> to wait indefinitely.
/// </param>
/// <returns>
/// Returns <c>true</c> when force flush succeeded; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when the <c>timeoutMilliseconds</c> is smaller than -1.
/// </exception>
/// <remarks>
/// This function guarantees thread-safety.
/// </remarks>
public static bool ForceFlush(this LoggerProvider provider, int timeoutMilliseconds = Timeout.Infinite)
{
Guard.ThrowIfNull(provider);
Guard.ThrowIfInvalidTimeout(timeoutMilliseconds);

if (provider is LoggerProviderSdk loggerProviderSdk)
{
return loggerProviderSdk.ForceFlush(timeoutMilliseconds);
}

return true;
}

/// <summary>
/// Attempts to shutdown the <see cref="LoggerProvider"/>, blocks the current thread until
/// shutdown completed or timed out.
/// </summary>
/// <param name="provider"><see cref="LoggerProvider"/> instance on which Shutdown will be called.</param>
/// <param name="timeoutMilliseconds">
/// The number (non-negative) of milliseconds to wait, or
/// <c>Timeout.Infinite</c> to wait indefinitely.
/// </param>
/// <returns>
/// Returns <c>true</c> when shutdown succeeded; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when the <c>timeoutMilliseconds</c> is smaller than -1.
/// </exception>
/// <remarks>
/// This function guarantees thread-safety. Only the first call will
/// win, subsequent calls will be no-op.
/// </remarks>
public static bool Shutdown(this LoggerProvider provider, int timeoutMilliseconds = Timeout.Infinite)
{
Guard.ThrowIfNull(provider);
Guard.ThrowIfInvalidTimeout(timeoutMilliseconds);

if (provider is LoggerProviderSdk loggerProviderSdk)
{
return loggerProviderSdk.Shutdown(timeoutMilliseconds);
}

return true;
}
}
19 changes: 10 additions & 9 deletions src/OpenTelemetry/Logs/LoggerProviderSdk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@ namespace OpenTelemetry.Logs;
internal sealed class LoggerProviderSdk : LoggerProvider
{
internal readonly IServiceProvider ServiceProvider;
private readonly IDisposable? ownedServiceProvider;
internal readonly IDisposable? OwnedServiceProvider;
internal bool Disposed;
internal int ShutdownCount;

private readonly List<object> instrumentations = new();
private ILogRecordPool? threadStaticPool = LogRecordThreadStaticPool.Instance;
private int shutdownCount;
private bool disposed;

public LoggerProviderSdk(
IServiceProvider serviceProvider,
Expand All @@ -49,8 +50,8 @@ public LoggerProviderSdk(

if (ownsServiceProvider)
{
this.ownedServiceProvider = serviceProvider as IDisposable;
Debug.Assert(this.ownedServiceProvider != null, "ownedServiceProvider was null");
this.OwnedServiceProvider = serviceProvider as IDisposable;
Debug.Assert(this.OwnedServiceProvider != null, "ownedServiceProvider was null");
}

OpenTelemetrySdkEventSource.Log.LoggerProviderSdkEvent("Building TracerProvider.");
Expand Down Expand Up @@ -160,7 +161,7 @@ public bool ForceFlush(int timeoutMilliseconds = Timeout.Infinite)

public bool Shutdown(int timeoutMilliseconds)
{
if (Interlocked.Increment(ref this.shutdownCount) > 1)
if (Interlocked.Increment(ref this.ShutdownCount) > 1)
{
return false; // shutdown already called
}
Expand Down Expand Up @@ -209,7 +210,7 @@ protected override bool TryCreateLogger(string? name, out Logger? logger)
/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
if (!this.disposed)
if (!this.Disposed)
{
if (disposing)
{
Expand All @@ -227,10 +228,10 @@ protected override void Dispose(bool disposing)
this.Processor?.Shutdown(5000);
this.Processor?.Dispose();

this.ownedServiceProvider?.Dispose();
this.OwnedServiceProvider?.Dispose();
}

this.disposed = true;
this.Disposed = true;
OpenTelemetrySdkEventSource.Log.ProviderDisposed(nameof(LoggerProviderSdk));
}

Expand Down
112 changes: 112 additions & 0 deletions test/OpenTelemetry.Tests/Logs/LoggerProviderExtensionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// <copyright file="LoggerProviderExtensionsTests.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 Xunit;

namespace OpenTelemetry.Logs.Tests;

public class LoggerProviderExtensionsTests
{
[Fact]
public void AddProcessorTest()
{
using var provider = Sdk.CreateLoggerProviderBuilder()
.Build();

Assert.NotNull(provider);

var providerSdk = provider as LoggerProviderSdk;

Assert.NotNull(providerSdk);

Assert.Null(providerSdk.Processor);

provider.AddProcessor(new TestProcessor());

Assert.NotNull(providerSdk.Processor);
}

[Fact]
public void ForceFlushTest()
{
var exporter = new TestExporter();

using var provider = Sdk.CreateLoggerProviderBuilder()
.AddProcessor(
new BatchLogRecordExportProcessor(
exporter,
scheduledDelayMilliseconds: int.MaxValue))
.Build();

Assert.NotNull(provider);

var providerSdk = provider as LoggerProviderSdk;

Assert.NotNull(providerSdk);

var logger = providerSdk.GetLogger();

Assert.NotNull(logger);

logger.EmitLog(new LogRecordData { Body = "Hello world" });

Assert.Empty(exporter.LogRecords);

Assert.True(provider.ForceFlush());

Assert.Single(exporter.LogRecords);
}

[Fact]
public void ShutdownTest()
{
using var provider = Sdk.CreateLoggerProviderBuilder()
.Build();

Assert.NotNull(provider);

var providerSdk = provider as LoggerProviderSdk;

Assert.NotNull(providerSdk);

Assert.Equal(0, providerSdk.ShutdownCount);

Assert.True(provider.Shutdown());

Assert.Equal(1, providerSdk.ShutdownCount);
}

private sealed class TestProcessor : BaseProcessor<LogRecord>
{
}

private sealed class TestExporter : BaseExporter<LogRecord>
{
public List<LogRecord> LogRecords { get; } = new();

public override ExportResult Export(in Batch<LogRecord> batch)
{
foreach (var logRecord in batch)
{
this.LogRecords.Add(logRecord);
}

return ExportResult.Success;
}
}
}