Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement TracerProvider Shutdown #1489

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ OpenTelemetry.Trace.Tracer.StartSpan(string name, OpenTelemetry.Trace.SpanKind k
OpenTelemetry.Trace.TracerProvider
OpenTelemetry.Trace.TracerProvider.Dispose() -> void
OpenTelemetry.Trace.TracerProvider.GetTracer(string name, string version = null) -> OpenTelemetry.Trace.Tracer
OpenTelemetry.Trace.TracerProvider.Shutdown(int timeoutMilliseconds = -1) -> bool
OpenTelemetry.Trace.TracerProvider.TracerProvider() -> void
abstract OpenTelemetry.Context.RuntimeContextSlot<T>.Get() -> T
abstract OpenTelemetry.Context.RuntimeContextSlot<T>.Set(T value) -> void
Expand Down Expand Up @@ -307,3 +308,4 @@ virtual OpenTelemetry.Metrics.MeterFactoryBase.GetMeter(string name, string vers
virtual OpenTelemetry.Metrics.MeterProvider.Dispose(bool disposing) -> void
virtual OpenTelemetry.Metrics.MeterProvider.GetMeter(string name, string version = null) -> OpenTelemetry.Metrics.Meter
virtual OpenTelemetry.Trace.TracerProvider.Dispose(bool disposing) -> void
virtual OpenTelemetry.Trace.TracerProvider.OnShutdown(int timeoutMilliseconds) -> bool
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ OpenTelemetry.Trace.Tracer.StartSpan(string name, OpenTelemetry.Trace.SpanKind k
OpenTelemetry.Trace.TracerProvider
OpenTelemetry.Trace.TracerProvider.Dispose() -> void
OpenTelemetry.Trace.TracerProvider.GetTracer(string name, string version = null) -> OpenTelemetry.Trace.Tracer
OpenTelemetry.Trace.TracerProvider.Shutdown(int timeoutMilliseconds = -1) -> bool
OpenTelemetry.Trace.TracerProvider.TracerProvider() -> void
abstract OpenTelemetry.Context.RuntimeContextSlot<T>.Get() -> T
abstract OpenTelemetry.Context.RuntimeContextSlot<T>.Set(T value) -> void
Expand Down Expand Up @@ -307,3 +308,4 @@ virtual OpenTelemetry.Metrics.MeterFactoryBase.GetMeter(string name, string vers
virtual OpenTelemetry.Metrics.MeterProvider.Dispose(bool disposing) -> void
virtual OpenTelemetry.Metrics.MeterProvider.GetMeter(string name, string version = null) -> OpenTelemetry.Metrics.Meter
virtual OpenTelemetry.Trace.TracerProvider.Dispose(bool disposing) -> void
virtual OpenTelemetry.Trace.TracerProvider.OnShutdown(int timeoutMilliseconds) -> bool
15 changes: 15 additions & 0 deletions src/OpenTelemetry.Api/Internal/OpenTelemetryApiEventSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ public void TracestateExtractException(Exception ex)
}
}

[NonEvent]
public void TracerProviderException(string evnt, Exception ex)
{
if (this.IsEnabled(EventLevel.Warning, (EventKeywords)(-1)))
{
this.TracerProviderException(evnt, ex.ToInvariantString());
}
}

[NonEvent]
public void TracestateKeyIsInvalid(ReadOnlySpan<char> key)
{
Expand Down Expand Up @@ -126,5 +135,11 @@ public void FailedToInjectBaggage(string format, string error)
{
this.WriteEvent(11, format, error);
}

[Event(12, Message = "Unknown error in TracerProvider '{0}': '{1}'.", Level = EventLevel.Warning)]
public void TracerProviderException(string evnt, string ex)
{
this.WriteEvent(12, evnt, ex);
}
}
}
66 changes: 66 additions & 0 deletions src/OpenTelemetry.Api/Trace/TracerProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

using System;
using System.Diagnostics;
using System.Threading;
using OpenTelemetry.Internal;

namespace OpenTelemetry.Trace
{
Expand All @@ -24,6 +26,8 @@ namespace OpenTelemetry.Trace
/// </summary>
public class TracerProvider : IDisposable
{
private int shutdownCount;

/// <summary>
/// Initializes a new instance of the <see cref="TracerProvider"/> class.
/// </summary>
Expand Down Expand Up @@ -52,13 +56,75 @@ public Tracer GetTracer(string name, string version = null)
return new Tracer(new ActivitySource(name, version));
}

/// <summary>
/// Attempts to shutdown the processor, blocks the current thread until
/// shutdown completed or timed out.
/// </summary>
/// <param name="timeoutMilliseconds">
/// The number 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="System.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 bool Shutdown(int timeoutMilliseconds = Timeout.Infinite)
utpilla marked this conversation as resolved.
Show resolved Hide resolved
{
if (timeoutMilliseconds < 0 && timeoutMilliseconds != Timeout.Infinite)
{
throw new ArgumentOutOfRangeException(nameof(timeoutMilliseconds), timeoutMilliseconds, "timeoutMilliseconds should be non-negative.");
}

if (Interlocked.Increment(ref this.shutdownCount) > 1)
{
return false; // shutdown already called
}

try
{
return this.OnShutdown(timeoutMilliseconds);
}
catch (Exception ex)
{
OpenTelemetryApiEventSource.Log.TracerProviderException(nameof(this.Shutdown), ex);
return false;
}
}

/// <inheritdoc/>
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}

/// <summary>
/// Called by <c>Shutdown</c>. This function should block the current
/// thread until shutdown completed or timed out.
/// </summary>
/// <param name="timeoutMilliseconds">
/// The number 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>
/// <remarks>
/// This function is called synchronously on the thread which made the
/// first call to <c>Shutdown</c>. This function should not throw
/// exceptions.
/// </remarks>
protected virtual bool OnShutdown(int timeoutMilliseconds)
{
return true;
}

/// <summary>
/// Releases the unmanaged resources used by this class and optionally releases the managed resources.
/// </summary>
Expand Down
9 changes: 5 additions & 4 deletions src/OpenTelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

## Unreleased

* Implemented Shutdown for TracerProvider
([#1489](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1489))

## 0.8.0-beta.1

Released 2020-Nov-5

* TracerProviderBuilder API changes
Renamed AddInstrumentation to AddDiagnosticSourceInstrumentation
and made internal.
Added AddInstrumentation
* TracerProviderBuilder API changes Renamed AddInstrumentation to
AddDiagnosticSourceInstrumentation and made internal. Added AddInstrumentation
([#1454](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1454))

* DiagnosticSource subscription helper classes (DiagnosticSourceSubscriber,
Expand Down
9 changes: 9 additions & 0 deletions src/OpenTelemetry/Trace/TracerProviderSdk.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using System.Threading;
using OpenTelemetry.Internal;
using OpenTelemetry.Resources;

Expand Down Expand Up @@ -220,6 +221,14 @@ protected override void Dispose(bool disposing)
base.Dispose(disposing);
}

protected override bool OnShutdown(int timeoutMilliseconds)
{
bool? result;
result = this.processor?.Shutdown(timeoutMilliseconds);
this.listener?.Dispose();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a bit strange to call dispose in shutdown.
Consider clean up the dispose and shutdown methods (e.g. Dispose will dispose the underlying listener).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spec says that after the TracerProvider Shutdown, any subsequent calls to get a Tracer should result in a no-op tracer. To achieve the equivalent of that requirement, we have to dispose the listener. If we don't dispose the listener we would continue to export the data as the exporters don't implement OnShutdown.

utpilla marked this conversation as resolved.
Show resolved Hide resolved
return result ?? true;
}

private static ActivitySamplingResult ComputeActivitySamplingResult(
in ActivityCreationOptions<ActivityContext> options,
Sampler sampler)
Expand Down