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:
Reiley Yang 2021-03-06 07:19:49 -08:00 committed by GitHub
parent a071d4d1d9
commit 9ec98ab120
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 547 additions and 9 deletions

View File

@ -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}

View File

@ -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>

View File

@ -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
}
}
}

View File

@ -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 -->

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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>

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}