Add SetErrorStatusOnException option to TracerProviderSdk (#1858)
* add SetErrorStatusOnUnhandledException option to TracerProviderSdk
* changelog
* add doc
* markdown lint
* fix nits
* markdown lint
* update public api file
* fix nits in the changelog
* add test cases
* improve example
* improve doc
* rename
* change the wording in the doc
* use RecordException in the example
* tweak the doc
* address review comment from Tom
* adjust wording
* Revert "adjust wording"
This reverts commit 9bf74a336d.
* better exception message
* update example/doc
* more test cases
* address corner case in test
Co-authored-by: Cijo Thomas <cithomas@microsoft.com>
This commit is contained in:
parent
a071d4d1d9
commit
9ec98ab120
|
|
@ -197,6 +197,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Shared", "src
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApp.AspNetCore.5.0", "test\TestApp.AspNetCore.5.0\TestApp.AspNetCore.5.0.csproj", "{972396A8-E35B-499C-9BA1-765E9B8822E1}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "exception-handling", "docs\trace\exception-handling\exception-handling.csproj", "{08D29501-F0A3-468F-B18D-BD1821A72383}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -387,6 +389,10 @@ Global
|
|||
{972396A8-E35B-499C-9BA1-765E9B8822E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{972396A8-E35B-499C-9BA1-765E9B8822E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{972396A8-E35B-499C-9BA1-765E9B8822E1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{08D29501-F0A3-468F-B18D-BD1821A72383}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{08D29501-F0A3-468F-B18D-BD1821A72383}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{08D29501-F0A3-468F-B18D-BD1821A72383}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{08D29501-F0A3-468F-B18D-BD1821A72383}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -416,6 +422,7 @@ Global
|
|||
{B3F03725-23A0-4582-9526-F6A7E38F35CC} = {3862190B-E2C5-418E-AFDC-DB281FB5C705}
|
||||
{13C10C9A-07E8-43EB-91F5-C2B116FBE0FC} = {3862190B-E2C5-418E-AFDC-DB281FB5C705}
|
||||
{972396A8-E35B-499C-9BA1-765E9B8822E1} = {77C7929A-2EED-4AA6-8705-B5C443C8AA0F}
|
||||
{08D29501-F0A3-468F-B18D-BD1821A72383} = {5B7FB835-3FFF-4BC2-99C5-A5B5FAE3C818}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {55639B5C-0770-4A22-AB56-859604650521}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<!-- https://dotnet.microsoft.com/download/dotnet-core -->
|
||||
<TargetFrameworks>netcoreapp2.1;netcoreapp3.1</TargetFrameworks>
|
||||
<TargetFrameworks>netcoreapp2.1;netcoreapp3.1;net5.0</TargetFrameworks>
|
||||
<!-- https://dotnet.microsoft.com/download/dotnet-framework -->
|
||||
<TargetFrameworks Condition="$(OS) == 'Windows_NT'">$(TargetFrameworks);net461;net462;net47;net471;net472;net48</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
// <copyright file="Program.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;
|
||||
using System.Diagnostics;
|
||||
using OpenTelemetry;
|
||||
using OpenTelemetry.Trace;
|
||||
|
||||
public class Program
|
||||
{
|
||||
private static readonly ActivitySource MyActivitySource = new ActivitySource(
|
||||
"MyCompany.MyProduct.MyLibrary");
|
||||
|
||||
public static void Main()
|
||||
{
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder(options =>
|
||||
{
|
||||
options.SetErrorStatusOnException = true;
|
||||
})
|
||||
.AddSource("MyCompany.MyProduct.MyLibrary")
|
||||
.SetSampler(new AlwaysOnSampler())
|
||||
.AddConsoleExporter()
|
||||
.Build();
|
||||
|
||||
try
|
||||
{
|
||||
using (MyActivitySource.StartActivity("Foo"))
|
||||
{
|
||||
using (MyActivitySource.StartActivity("Bar"))
|
||||
{
|
||||
throw new Exception("Oops!");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// swallow the exception
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
# Exception Handling
|
||||
|
||||
## User-handled Exception
|
||||
|
||||
The term `User-handled Exception` is used to describe exceptions that are
|
||||
handled by the application.
|
||||
|
||||
While using `Activity` API, the common pattern would be:
|
||||
|
||||
```csharp
|
||||
using (var activity = MyActivitySource.StartActivity("Foo"))
|
||||
{
|
||||
try
|
||||
{
|
||||
Func();
|
||||
}
|
||||
catch (SomeException ex)
|
||||
{
|
||||
activity?.SetStatus(Status.Error);
|
||||
DoSomething();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
activity?.SetStatus(Status.Error);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The above approach could become hard to manage if there are deeply nested
|
||||
`Activity` objects, or there are activities created in a 3rd party library.
|
||||
|
||||
The following configuration will automatically detect exception and set the
|
||||
activity status to `Error`:
|
||||
|
||||
```csharp
|
||||
Sdk.CreateTracerProviderBuilder(options => {
|
||||
options.SetErrorStatusOnException = true;
|
||||
});
|
||||
```
|
||||
|
||||
A complete example can be found [here](./Program.cs).
|
||||
|
||||
Note: this feature is platform dependent as it relies on
|
||||
[System.Runtime.InteropServices.Marshal.GetExceptionPointers](https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.marshal.getexceptionpointers).
|
||||
|
||||
## Unhandled Exception
|
||||
|
||||
The term `Unhandled Exception` is used to describe exceptions that are not
|
||||
handled by the application. When an unhandled exception happened, the behavior
|
||||
will depend on the presence of a debugger:
|
||||
|
||||
* If there is no debugger, the exception will normally crash the process or
|
||||
terminate the thread.
|
||||
* If a debugger is attached, the debugger will be notified that an unhandled
|
||||
exception happened.
|
||||
* In case a postmortem debugger is configured, the postmortem debugger will be
|
||||
activited and normally it will collect a crash dump.
|
||||
|
||||
It might be useful to automatically capture the unhandled exceptions, travel
|
||||
through the unfinished activities and export them for troubleshooting. Here goes
|
||||
one possible way of doing this:
|
||||
|
||||
**WARNING:** Use `AppDomain.UnhandledException` with caution. A throw in the
|
||||
handler puts the process into an unrecoverable state.
|
||||
|
||||
<!-- markdownlint-disable MD013 -->
|
||||
```csharp
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using OpenTelemetry;
|
||||
using OpenTelemetry.Trace;
|
||||
|
||||
public class Program
|
||||
{
|
||||
private static readonly ActivitySource MyActivitySource = new ActivitySource("MyCompany.MyProduct.MyLibrary");
|
||||
|
||||
public static void Main()
|
||||
{
|
||||
AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler;
|
||||
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder(options =>
|
||||
{
|
||||
options.SetErrorStatusOnException = true;
|
||||
})
|
||||
.AddSource("MyCompany.MyProduct.MyLibrary")
|
||||
.SetSampler(new AlwaysOnSampler())
|
||||
.AddConsoleExporter()
|
||||
.Build();
|
||||
|
||||
using (MyActivitySource.StartActivity("Foo"))
|
||||
{
|
||||
using (MyActivitySource.StartActivity("Bar"))
|
||||
{
|
||||
throw new Exception("Oops!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void UnhandledExceptionHandler(object source, UnhandledExceptionEventArgs args)
|
||||
{
|
||||
var ex = (Exception)args.ExceptionObject;
|
||||
|
||||
var activity = Activity.Current;
|
||||
|
||||
while (activity != null)
|
||||
{
|
||||
activity.RecordException(ex);
|
||||
activity.Dispose();
|
||||
activity = activity.Parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
<!-- markdownlint-enable MD013 -->
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<ItemGroup>
|
||||
<!---
|
||||
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="$(OpenTelemetryExporterConsolePkgVer)" />
|
||||
-->
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.Console\OpenTelemetry.Exporter.Console.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -1,3 +1,8 @@
|
|||
OpenTelemetry.Trace.ParentBasedSampler.ParentBasedSampler(OpenTelemetry.Trace.Sampler rootSampler, OpenTelemetry.Trace.Sampler remoteParentSampled = null, OpenTelemetry.Trace.Sampler remoteParentNotSampled = null, OpenTelemetry.Trace.Sampler localParentSampled = null, OpenTelemetry.Trace.Sampler localParentNotSampled = null) -> void
|
||||
OpenTelemetry.Trace.TracerProviderOptions
|
||||
OpenTelemetry.Trace.TracerProviderOptions.SetErrorStatusOnException.get -> bool
|
||||
OpenTelemetry.Trace.TracerProviderOptions.SetErrorStatusOnException.set -> void
|
||||
OpenTelemetry.Trace.TracerProviderOptions.TracerProviderOptions() -> void
|
||||
static OpenTelemetry.Sdk.CreateTracerProviderBuilder(System.Action<OpenTelemetry.Trace.TracerProviderOptions> configure = null) -> OpenTelemetry.Trace.TracerProviderBuilder
|
||||
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddLegacyActivity(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, string operationName) -> OpenTelemetry.Trace.TracerProviderBuilder
|
||||
static OpenTelemetry.Trace.TracerProviderExtensions.ForceFlush(this OpenTelemetry.Trace.TracerProvider provider, int timeoutMilliseconds = -1) -> bool
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
OpenTelemetry.Trace.ParentBasedSampler.ParentBasedSampler(OpenTelemetry.Trace.Sampler rootSampler, OpenTelemetry.Trace.Sampler remoteParentSampled = null, OpenTelemetry.Trace.Sampler remoteParentNotSampled = null, OpenTelemetry.Trace.Sampler localParentSampled = null, OpenTelemetry.Trace.Sampler localParentNotSampled = null) -> void
|
||||
OpenTelemetry.Trace.TracerProviderOptions
|
||||
OpenTelemetry.Trace.TracerProviderOptions.SetErrorStatusOnException.get -> bool
|
||||
OpenTelemetry.Trace.TracerProviderOptions.SetErrorStatusOnException.set -> void
|
||||
OpenTelemetry.Trace.TracerProviderOptions.TracerProviderOptions() -> void
|
||||
static OpenTelemetry.Sdk.CreateTracerProviderBuilder(System.Action<OpenTelemetry.Trace.TracerProviderOptions> configure = null) -> OpenTelemetry.Trace.TracerProviderBuilder
|
||||
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddLegacyActivity(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, string operationName) -> OpenTelemetry.Trace.TracerProviderBuilder
|
||||
static OpenTelemetry.Trace.TracerProviderExtensions.ForceFlush(this OpenTelemetry.Trace.TracerProvider provider, int timeoutMilliseconds = -1) -> bool
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
OpenTelemetry.Trace.ParentBasedSampler.ParentBasedSampler(OpenTelemetry.Trace.Sampler rootSampler, OpenTelemetry.Trace.Sampler remoteParentSampled = null, OpenTelemetry.Trace.Sampler remoteParentNotSampled = null, OpenTelemetry.Trace.Sampler localParentSampled = null, OpenTelemetry.Trace.Sampler localParentNotSampled = null) -> void
|
||||
OpenTelemetry.Trace.TracerProviderOptions
|
||||
OpenTelemetry.Trace.TracerProviderOptions.SetErrorStatusOnException.get -> bool
|
||||
OpenTelemetry.Trace.TracerProviderOptions.SetErrorStatusOnException.set -> void
|
||||
OpenTelemetry.Trace.TracerProviderOptions.TracerProviderOptions() -> void
|
||||
static OpenTelemetry.Sdk.CreateTracerProviderBuilder(System.Action<OpenTelemetry.Trace.TracerProviderOptions> configure = null) -> OpenTelemetry.Trace.TracerProviderBuilder
|
||||
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddLegacyActivity(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, string operationName) -> OpenTelemetry.Trace.TracerProviderBuilder
|
||||
static OpenTelemetry.Trace.TracerProviderExtensions.ForceFlush(this OpenTelemetry.Trace.TracerProvider provider, int timeoutMilliseconds = -1) -> bool
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
OpenTelemetry.Trace.ParentBasedSampler.ParentBasedSampler(OpenTelemetry.Trace.Sampler rootSampler, OpenTelemetry.Trace.Sampler remoteParentSampled = null, OpenTelemetry.Trace.Sampler remoteParentNotSampled = null, OpenTelemetry.Trace.Sampler localParentSampled = null, OpenTelemetry.Trace.Sampler localParentNotSampled = null) -> void
|
||||
OpenTelemetry.Trace.TracerProviderOptions
|
||||
OpenTelemetry.Trace.TracerProviderOptions.SetErrorStatusOnException.get -> bool
|
||||
OpenTelemetry.Trace.TracerProviderOptions.SetErrorStatusOnException.set -> void
|
||||
OpenTelemetry.Trace.TracerProviderOptions.TracerProviderOptions() -> void
|
||||
static OpenTelemetry.Sdk.CreateTracerProviderBuilder(System.Action<OpenTelemetry.Trace.TracerProviderOptions> configure = null) -> OpenTelemetry.Trace.TracerProviderBuilder
|
||||
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddLegacyActivity(this OpenTelemetry.Trace.TracerProviderBuilder tracerProviderBuilder, string operationName) -> OpenTelemetry.Trace.TracerProviderBuilder
|
||||
static OpenTelemetry.Trace.TracerProviderExtensions.ForceFlush(this OpenTelemetry.Trace.TracerProvider provider, int timeoutMilliseconds = -1) -> bool
|
||||
|
|
|
|||
|
|
@ -9,14 +9,15 @@ please check the latest changes
|
|||
|
||||
## Unreleased
|
||||
|
||||
* Added `ForceFlush` to `TracerProvider`. ([#1837](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1837))
|
||||
|
||||
* Added `TracerProviderOptions` and `SetErrorStatusOnException`.
|
||||
([#1858](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1858))
|
||||
* Added `ForceFlush` to `TracerProvider`.
|
||||
([#1837](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1837))
|
||||
* Added a TracerProvierBuilder extension method called
|
||||
`AddLegacyActivityOperationName` which is used by instrumentation libraries
|
||||
that use DiagnosticSource to get activities processed without
|
||||
ActivitySourceAdapter.
|
||||
[#1836](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1836)
|
||||
|
||||
([#1836](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1836))
|
||||
* Added new constructor with optional parameters to allow customization of
|
||||
`ParentBasedSampler` behavior. ([#1727](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1727))
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using OpenTelemetry.Context.Propagation;
|
||||
using OpenTelemetry.Internal;
|
||||
|
|
@ -54,13 +55,24 @@ namespace OpenTelemetry
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates TracerProviderBuilder which should be used to build
|
||||
/// TracerProvider.
|
||||
/// Creates TracerProviderBuilder which should be used to build TracerProvider.
|
||||
/// </summary>
|
||||
/// <returns>TracerProviderBuilder instance, which should be used to build TracerProvider.</returns>
|
||||
public static TracerProviderBuilder CreateTracerProviderBuilder()
|
||||
{
|
||||
return new TracerProviderBuilderSdk();
|
||||
return CreateTracerProviderBuilder(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates TracerProviderBuilder which should be used to build TracerProvider.
|
||||
/// </summary>
|
||||
/// <param name="configure">TracerProvider configuration options.</param>
|
||||
/// <returns>TracerProviderBuilder instance, which should be used to build TracerProvider.</returns>
|
||||
public static TracerProviderBuilder CreateTracerProviderBuilder(Action<TracerProviderOptions> configure = null)
|
||||
{
|
||||
var options = new TracerProviderOptions();
|
||||
configure?.Invoke(options);
|
||||
return new TracerProviderBuilderSdk(options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
// <copyright file="ExceptionProcessor.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;
|
||||
using System.Diagnostics;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace OpenTelemetry.Trace
|
||||
{
|
||||
internal class ExceptionProcessor : BaseProcessor<Activity>
|
||||
{
|
||||
private const string ExceptionPointersKey = "otel.exception_pointers";
|
||||
|
||||
private readonly Func<IntPtr> fnGetExceptionPointers;
|
||||
|
||||
public ExceptionProcessor()
|
||||
{
|
||||
try
|
||||
{
|
||||
var flags = BindingFlags.Static | BindingFlags.Public;
|
||||
var method = typeof(Marshal).GetMethod("GetExceptionPointers", flags, null, new Type[] { }, null);
|
||||
var lambda = Expression.Lambda<Func<IntPtr>>(Expression.Call(method));
|
||||
this.fnGetExceptionPointers = lambda.Compile();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new NotSupportedException("System.Runtime.InteropServices.Marshal.GetExceptionPointers is not supported.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnStart(Activity activity)
|
||||
{
|
||||
var pointers = this.fnGetExceptionPointers();
|
||||
|
||||
if (pointers != IntPtr.Zero)
|
||||
{
|
||||
activity.SetTag(ExceptionPointersKey, pointers);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnEnd(Activity activity)
|
||||
{
|
||||
var pointers = this.fnGetExceptionPointers();
|
||||
|
||||
if (pointers == IntPtr.Zero)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var snapshot = activity.GetTagValue(ExceptionPointersKey) as IntPtr?;
|
||||
|
||||
if (snapshot != null)
|
||||
{
|
||||
activity.SetTag(ExceptionPointersKey, null);
|
||||
}
|
||||
|
||||
if (snapshot != pointers)
|
||||
{
|
||||
activity.SetStatus(Status.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -28,14 +28,28 @@ namespace OpenTelemetry.Trace
|
|||
{
|
||||
private readonly List<InstrumentationFactory> instrumentationFactories = new List<InstrumentationFactory>();
|
||||
|
||||
private readonly TracerProviderOptions options;
|
||||
private readonly List<BaseProcessor<Activity>> processors = new List<BaseProcessor<Activity>>();
|
||||
private readonly List<string> sources = new List<string>();
|
||||
private readonly Dictionary<string, bool> legacyActivityOperationNames = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
|
||||
private ResourceBuilder resourceBuilder = ResourceBuilder.CreateDefault();
|
||||
private Sampler sampler = new ParentBasedSampler(new AlwaysOnSampler());
|
||||
|
||||
internal TracerProviderBuilderSdk()
|
||||
internal TracerProviderBuilderSdk(TracerProviderOptions options)
|
||||
{
|
||||
this.options = options ?? new TracerProviderOptions();
|
||||
|
||||
if (options.SetErrorStatusOnException)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.AddProcessor(new ExceptionProcessor());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new NotSupportedException($"{nameof(options.SetErrorStatusOnException)} is not supported on this platform.", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
// <copyright file="TracerProviderOptions.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>
|
||||
|
||||
namespace OpenTelemetry.Trace
|
||||
{
|
||||
public class TracerProviderOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the status of <see cref="System.Diagnostics.Activity"/>
|
||||
/// should be set to <c>Status.Error</c> when it ended abnormally due to an unhandled exception.
|
||||
/// </summary>
|
||||
public bool SetErrorStatusOnException { get; set; } = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
// <copyright file="ExceptionProcessorTest.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;
|
||||
using System.Diagnostics;
|
||||
using OpenTelemetry.Tests;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Trace.Tests
|
||||
{
|
||||
public class ExceptionProcessorTest
|
||||
{
|
||||
private const string ActivitySourceName = "ExceptionProcessorTest";
|
||||
|
||||
[Fact]
|
||||
public void ActivityStatusSetToErrorWhenExceptionProcessorEnabled()
|
||||
{
|
||||
using var activitySource = new ActivitySource(ActivitySourceName);
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.AddSource(ActivitySourceName)
|
||||
.SetSampler(new AlwaysOnSampler())
|
||||
.AddProcessor(new ExceptionProcessor())
|
||||
.Build();
|
||||
|
||||
Activity activity1 = null;
|
||||
Activity activity2 = null;
|
||||
Activity activity3 = null;
|
||||
Activity activity4 = null;
|
||||
Activity activity5 = null;
|
||||
|
||||
try
|
||||
{
|
||||
using (activity1 = activitySource.StartActivity("Activity1"))
|
||||
{
|
||||
using (activity2 = activitySource.StartActivity("Activity2"))
|
||||
{
|
||||
throw new Exception("Oops!");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
using (activity3 = activitySource.StartActivity("Activity3"))
|
||||
{
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
using (activity4 = activitySource.StartActivity("Activity4"))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
throw new Exception("Oops!");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
activity5 = activitySource.StartActivity("Activity5");
|
||||
}
|
||||
finally
|
||||
{
|
||||
activity5.Dispose();
|
||||
}
|
||||
|
||||
Assert.Equal(StatusCode.Error, activity1.GetStatus().StatusCode);
|
||||
Assert.Null(activity1.GetTagValue("otel.exception_pointers"));
|
||||
Assert.Equal(StatusCode.Error, activity2.GetStatus().StatusCode);
|
||||
Assert.Null(activity2.GetTagValue("otel.exception_pointers"));
|
||||
Assert.Equal(StatusCode.Unset, activity3.GetStatus().StatusCode);
|
||||
Assert.Null(activity3.GetTagValue("otel.exception_pointers"));
|
||||
Assert.Equal(StatusCode.Unset, activity4.GetStatus().StatusCode);
|
||||
Assert.Null(activity4.GetTagValue("otel.exception_pointers"));
|
||||
Assert.Equal(StatusCode.Unset, activity5.GetStatus().StatusCode);
|
||||
#if !NETFRAMEWORK
|
||||
// In this rare case, the following Activity tag will not get cleaned up due to perf consideration.
|
||||
Assert.NotNull(activity5.GetTagValue("otel.exception_pointers"));
|
||||
#endif
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ActivityStatusNotSetWhenExceptionProcessorNotEnabled()
|
||||
{
|
||||
using var activitySource = new ActivitySource(ActivitySourceName);
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.AddSource(ActivitySourceName)
|
||||
.SetSampler(new AlwaysOnSampler())
|
||||
.Build();
|
||||
|
||||
Activity activity = null;
|
||||
|
||||
try
|
||||
{
|
||||
using (activity = activitySource.StartActivity("Activity"))
|
||||
{
|
||||
throw new Exception("Oops!");
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
|
||||
Assert.Equal(StatusCode.Unset, activity.GetStatus().StatusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
// <copyright file="TracerProviderOptionsTest.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;
|
||||
using System.Diagnostics;
|
||||
using OpenTelemetry.Tests;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Trace.Tests
|
||||
{
|
||||
public class TracerProviderOptionsTest
|
||||
{
|
||||
private const string ActivitySourceName = "TracerProviderOptionsTest";
|
||||
|
||||
[Fact]
|
||||
public void SetErrorStatusOnExceptionEnabled()
|
||||
{
|
||||
using var activitySource = new ActivitySource(ActivitySourceName);
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder(options =>
|
||||
{
|
||||
options.SetErrorStatusOnException = true;
|
||||
})
|
||||
.AddSource(ActivitySourceName)
|
||||
.SetSampler(new AlwaysOnSampler())
|
||||
.Build();
|
||||
|
||||
var activity = activitySource.StartActivity("Activity");
|
||||
|
||||
try
|
||||
{
|
||||
using (activity)
|
||||
{
|
||||
throw new Exception("Oops!");
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
|
||||
Assert.Equal(StatusCode.Error, activity.GetStatus().StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetErrorStatusOnExceptionDefault()
|
||||
{
|
||||
using var activitySource = new ActivitySource(ActivitySourceName);
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder(options => { })
|
||||
.AddSource(ActivitySourceName)
|
||||
.SetSampler(new AlwaysOnSampler())
|
||||
.Build();
|
||||
|
||||
var activity = activitySource.StartActivity("Activity");
|
||||
|
||||
try
|
||||
{
|
||||
using (activity)
|
||||
{
|
||||
throw new Exception("Oops!");
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
|
||||
Assert.Equal(StatusCode.Unset, activity.GetStatus().StatusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue