Remove ActivitySourceAdapter (#1836)
Removed ActivitySourceAdapater and made TracerProviderSdk natively support legacy activites
This commit is contained in:
parent
39841e5e1f
commit
0581ce2957
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<ItemGroup>
|
||||
<!---
|
||||
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="$(OpenTelemetryExporterConsolePkgVer)" />
|
||||
|
|
|
|||
|
|
@ -31,12 +31,11 @@ namespace OpenTelemetry.Instrumentation.AspNet
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AspNetInstrumentation"/> class.
|
||||
/// </summary>
|
||||
/// <param name="activitySource">ActivitySource adapter instance.</param>
|
||||
/// <param name="options">Configuration options for ASP.NET instrumentation.</param>
|
||||
public AspNetInstrumentation(ActivitySourceAdapter activitySource, AspNetInstrumentationOptions options)
|
||||
public AspNetInstrumentation(AspNetInstrumentationOptions options)
|
||||
{
|
||||
this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(
|
||||
name => new HttpInListener(name, options, activitySource),
|
||||
name => new HttpInListener(name, options),
|
||||
listener => listener.Name == AspNetDiagnosticListenerName,
|
||||
null);
|
||||
this.diagnosticSourceSubscriber.Subscribe();
|
||||
|
|
|
|||
|
|
@ -37,13 +37,11 @@ namespace OpenTelemetry.Instrumentation.AspNet.Implementation
|
|||
private readonly PropertyFetcher<object> routeFetcher = new PropertyFetcher<object>("Route");
|
||||
private readonly PropertyFetcher<string> routeTemplateFetcher = new PropertyFetcher<string>("RouteTemplate");
|
||||
private readonly AspNetInstrumentationOptions options;
|
||||
private readonly ActivitySourceAdapter activitySource;
|
||||
|
||||
public HttpInListener(string name, AspNetInstrumentationOptions options, ActivitySourceAdapter activitySource)
|
||||
public HttpInListener(string name, AspNetInstrumentationOptions options)
|
||||
: base(name)
|
||||
{
|
||||
this.options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
this.activitySource = activitySource;
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "Activity is retrieved from Activity.Current later and disposed.")]
|
||||
|
|
@ -98,6 +96,9 @@ namespace OpenTelemetry.Instrumentation.AspNet.Implementation
|
|||
// correctly stop and restore Activity.Current.
|
||||
newOne.SetCustomProperty("OTel.ActivityByAspNet", activity);
|
||||
activity.SetCustomProperty("OTel.ActivityByHttpInListener", newOne);
|
||||
|
||||
// Set IsAllDataRequested to false for the activity created by the framework to only export the sibling activity and not the framework activity
|
||||
activity.IsAllDataRequested = false;
|
||||
activity = newOne;
|
||||
}
|
||||
|
||||
|
|
@ -111,7 +112,8 @@ namespace OpenTelemetry.Instrumentation.AspNet.Implementation
|
|||
var path = requestValues.Path;
|
||||
activity.DisplayName = path;
|
||||
|
||||
this.activitySource.Start(activity, ActivityKind.Server, ActivitySource);
|
||||
ActivityInstrumentationHelper.SetActivitySourceProperty(activity, ActivitySource);
|
||||
ActivityInstrumentationHelper.SetKindProperty(activity, ActivityKind.Server);
|
||||
|
||||
if (activity.IsAllDataRequested)
|
||||
{
|
||||
|
|
@ -244,8 +246,6 @@ namespace OpenTelemetry.Instrumentation.AspNet.Implementation
|
|||
Activity.Current = activity;
|
||||
}
|
||||
}
|
||||
|
||||
this.activitySource.Stop(activityToEnrich);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
using System;
|
||||
using OpenTelemetry.Instrumentation.AspNet;
|
||||
using OpenTelemetry.Instrumentation.AspNet.Implementation;
|
||||
|
||||
namespace OpenTelemetry.Trace
|
||||
{
|
||||
|
|
@ -42,7 +43,10 @@ namespace OpenTelemetry.Trace
|
|||
var aspnetOptions = new AspNetInstrumentationOptions();
|
||||
configureAspNetInstrumentationOptions?.Invoke(aspnetOptions);
|
||||
|
||||
builder.AddDiagnosticSourceInstrumentation((activitySource) => new AspNetInstrumentation(activitySource, aspnetOptions));
|
||||
builder.AddInstrumentation(() => new AspNetInstrumentation(aspnetOptions));
|
||||
builder.AddSource(HttpInListener.ActivitySourceName);
|
||||
builder.AddLegacyActivity("Microsoft.AspNet.HttpReqIn"); // for the activities created by AspNetCore
|
||||
builder.AddLegacyActivity("ActivityCreatedByHttpInListener"); // for the sibling activities created by the instrumentation library
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,11 +29,10 @@ namespace OpenTelemetry.Instrumentation.AspNetCore
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AspNetCoreInstrumentation"/> class.
|
||||
/// </summary>
|
||||
/// <param name="activitySource">ActivitySource adapter instance.</param>
|
||||
/// <param name="options">Configuration options for ASP.NET Core instrumentation.</param>
|
||||
public AspNetCoreInstrumentation(ActivitySourceAdapter activitySource, AspNetCoreInstrumentationOptions options)
|
||||
public AspNetCoreInstrumentation(AspNetCoreInstrumentationOptions options)
|
||||
{
|
||||
this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new HttpInListener("Microsoft.AspNetCore", options, activitySource), null);
|
||||
this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new HttpInListener("Microsoft.AspNetCore", options), null);
|
||||
this.diagnosticSourceSubscriber.Subscribe();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,12 +30,13 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
|
|||
{
|
||||
internal class HttpInListener : ListenerHandler
|
||||
{
|
||||
internal const string ActivityOperationName = "Microsoft.AspNetCore.Hosting.HttpRequestIn";
|
||||
internal const string ActivityNameByHttpInListener = "ActivityCreatedByHttpInListener";
|
||||
internal static readonly AssemblyName AssemblyName = typeof(HttpInListener).Assembly.GetName();
|
||||
internal static readonly string ActivitySourceName = AssemblyName.Name;
|
||||
internal static readonly Version Version = AssemblyName.Version;
|
||||
internal static readonly ActivitySource ActivitySource = new ActivitySource(ActivitySourceName, Version.ToString());
|
||||
private const string UnknownHostName = "UNKNOWN-HOST";
|
||||
private const string ActivityNameByHttpInListener = "ActivityCreatedByHttpInListener";
|
||||
private static readonly Func<HttpRequest, string, IEnumerable<string>> HttpRequestHeaderValuesGetter = (request, name) => request.Headers[name];
|
||||
private readonly PropertyFetcher<HttpContext> startContextFetcher = new PropertyFetcher<HttpContext>("HttpContext");
|
||||
private readonly PropertyFetcher<HttpContext> stopContextFetcher = new PropertyFetcher<HttpContext>("HttpContext");
|
||||
|
|
@ -45,14 +46,12 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
|
|||
private readonly PropertyFetcher<string> beforeActionTemplateFetcher = new PropertyFetcher<string>("Template");
|
||||
private readonly bool hostingSupportsW3C;
|
||||
private readonly AspNetCoreInstrumentationOptions options;
|
||||
private readonly ActivitySourceAdapter activitySource;
|
||||
|
||||
public HttpInListener(string name, AspNetCoreInstrumentationOptions options, ActivitySourceAdapter activitySource)
|
||||
public HttpInListener(string name, AspNetCoreInstrumentationOptions options)
|
||||
: base(name)
|
||||
{
|
||||
this.hostingSupportsW3C = typeof(HttpRequest).Assembly.GetName().Version.Major >= 3;
|
||||
this.options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
this.activitySource = activitySource;
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "The objects should not be disposed.")]
|
||||
|
|
@ -99,6 +98,9 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
|
|||
|
||||
// Starting the new activity make it the Activity.Current one.
|
||||
newOne.Start();
|
||||
|
||||
// Set IsAllDataRequested to false for the activity created by the framework to only export the sibling activity and not the framework activity
|
||||
activity.IsAllDataRequested = false;
|
||||
activity = newOne;
|
||||
}
|
||||
|
||||
|
|
@ -108,7 +110,8 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
|
|||
}
|
||||
}
|
||||
|
||||
this.activitySource.Start(activity, ActivityKind.Server, ActivitySource);
|
||||
ActivityInstrumentationHelper.SetActivitySourceProperty(activity, ActivitySource);
|
||||
ActivityInstrumentationHelper.SetKindProperty(activity, ActivityKind.Server);
|
||||
|
||||
if (activity.IsAllDataRequested)
|
||||
{
|
||||
|
|
@ -208,8 +211,6 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
|
|||
// the one created by the instrumentation.
|
||||
// And retrieve it here, and set it to Current.
|
||||
}
|
||||
|
||||
this.activitySource.Stop(activity);
|
||||
}
|
||||
|
||||
public override void OnCustom(string name, Activity activity, object payload)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
using System;
|
||||
using OpenTelemetry.Instrumentation.AspNetCore;
|
||||
using OpenTelemetry.Instrumentation.AspNetCore.Implementation;
|
||||
|
||||
namespace OpenTelemetry.Trace
|
||||
{
|
||||
|
|
@ -41,7 +42,10 @@ namespace OpenTelemetry.Trace
|
|||
|
||||
var aspnetCoreOptions = new AspNetCoreInstrumentationOptions();
|
||||
configureAspNetCoreInstrumentationOptions?.Invoke(aspnetCoreOptions);
|
||||
builder.AddDiagnosticSourceInstrumentation((activitySource) => new AspNetCoreInstrumentation(activitySource, aspnetCoreOptions));
|
||||
builder.AddInstrumentation(() => new AspNetCoreInstrumentation(aspnetCoreOptions));
|
||||
builder.AddSource(HttpInListener.ActivitySourceName);
|
||||
builder.AddLegacyActivity(HttpInListener.ActivityOperationName); // for the activities created by AspNetCore
|
||||
builder.AddLegacyActivity(HttpInListener.ActivityNameByHttpInListener); // for the sibling activities created by the instrumentation library
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,16 +29,10 @@ namespace OpenTelemetry.Instrumentation.GrpcNetClient
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GrpcClientInstrumentation"/> class.
|
||||
/// </summary>
|
||||
/// <param name="activitySource">ActivitySource adapter instance.</param>
|
||||
/// <param name="options">Configuration options for Grpc client instrumentation.</param>
|
||||
public GrpcClientInstrumentation(ActivitySourceAdapter activitySource, GrpcClientInstrumentationOptions options = null)
|
||||
public GrpcClientInstrumentation(GrpcClientInstrumentationOptions options = null)
|
||||
{
|
||||
if (activitySource == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(activitySource));
|
||||
}
|
||||
|
||||
this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new GrpcClientDiagnosticListener(activitySource, options), null);
|
||||
this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new GrpcClientDiagnosticListener(options), null);
|
||||
this.diagnosticSourceSubscriber.Subscribe();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,20 +32,13 @@ namespace OpenTelemetry.Instrumentation.GrpcNetClient.Implementation
|
|||
internal static readonly ActivitySource ActivitySource = new ActivitySource(ActivitySourceName, Version.ToString());
|
||||
|
||||
private readonly GrpcClientInstrumentationOptions options;
|
||||
private readonly ActivitySourceAdapter activitySource;
|
||||
private readonly PropertyFetcher<HttpRequestMessage> startRequestFetcher = new PropertyFetcher<HttpRequestMessage>("Request");
|
||||
private readonly PropertyFetcher<HttpResponseMessage> stopRequestFetcher = new PropertyFetcher<HttpResponseMessage>("Response");
|
||||
|
||||
public GrpcClientDiagnosticListener(ActivitySourceAdapter activitySource, GrpcClientInstrumentationOptions options)
|
||||
public GrpcClientDiagnosticListener(GrpcClientInstrumentationOptions options)
|
||||
: base("Grpc.Net.Client")
|
||||
{
|
||||
if (activitySource == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(activitySource));
|
||||
}
|
||||
|
||||
this.options = options;
|
||||
this.activitySource = activitySource;
|
||||
}
|
||||
|
||||
public override void OnStartActivity(Activity activity, object payload)
|
||||
|
|
@ -89,7 +82,8 @@ namespace OpenTelemetry.Instrumentation.GrpcNetClient.Implementation
|
|||
|
||||
activity.DisplayName = grpcMethod?.Trim('/');
|
||||
|
||||
this.activitySource.Start(activity, ActivityKind.Client, ActivitySource);
|
||||
ActivityInstrumentationHelper.SetActivitySourceProperty(activity, ActivitySource);
|
||||
ActivityInstrumentationHelper.SetKindProperty(activity, ActivityKind.Client);
|
||||
|
||||
if (activity.IsAllDataRequested)
|
||||
{
|
||||
|
|
@ -158,8 +152,6 @@ namespace OpenTelemetry.Instrumentation.GrpcNetClient.Implementation
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.activitySource.Stop(activity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
using System;
|
||||
using OpenTelemetry.Instrumentation.GrpcNetClient;
|
||||
using OpenTelemetry.Instrumentation.GrpcNetClient.Implementation;
|
||||
|
||||
namespace OpenTelemetry.Trace
|
||||
{
|
||||
|
|
@ -43,7 +44,10 @@ namespace OpenTelemetry.Trace
|
|||
var grpcOptions = new GrpcClientInstrumentationOptions();
|
||||
configure?.Invoke(grpcOptions);
|
||||
|
||||
builder.AddDiagnosticSourceInstrumentation((activitySource) => new GrpcClientInstrumentation(activitySource, grpcOptions));
|
||||
builder.AddInstrumentation(() => new GrpcClientInstrumentation(grpcOptions));
|
||||
builder.AddSource(GrpcClientDiagnosticListener.ActivitySourceName);
|
||||
builder.AddLegacyActivity("Grpc.Net.Client.GrpcOut");
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,11 +29,10 @@ namespace OpenTelemetry.Instrumentation.Http
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HttpClientInstrumentation"/> class.
|
||||
/// </summary>
|
||||
/// <param name="activitySourceAdapter">ActivitySource adapter instance.</param>
|
||||
/// <param name="options">Configuration options for HTTP client instrumentation.</param>
|
||||
public HttpClientInstrumentation(ActivitySourceAdapter activitySourceAdapter, HttpClientInstrumentationOptions options)
|
||||
public HttpClientInstrumentation(HttpClientInstrumentationOptions options)
|
||||
{
|
||||
this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new HttpHandlerDiagnosticListener(options, activitySourceAdapter), null);
|
||||
this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new HttpHandlerDiagnosticListener(options), null);
|
||||
this.diagnosticSourceSubscriber.Subscribe();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ namespace OpenTelemetry.Instrumentation.Http.Implementation
|
|||
|
||||
private static readonly Regex CoreAppMajorVersionCheckRegex = new Regex("^\\.NETCoreApp,Version=v(\\d+)\\.", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private readonly ActivitySourceAdapter activitySource;
|
||||
private readonly PropertyFetcher<HttpRequestMessage> startRequestFetcher = new PropertyFetcher<HttpRequestMessage>("Request");
|
||||
private readonly PropertyFetcher<HttpResponseMessage> stopResponseFetcher = new PropertyFetcher<HttpResponseMessage>("Response");
|
||||
private readonly PropertyFetcher<Exception> stopExceptionFetcher = new PropertyFetcher<Exception>("Exception");
|
||||
|
|
@ -45,7 +44,7 @@ namespace OpenTelemetry.Instrumentation.Http.Implementation
|
|||
private readonly bool httpClientSupportsW3C;
|
||||
private readonly HttpClientInstrumentationOptions options;
|
||||
|
||||
public HttpHandlerDiagnosticListener(HttpClientInstrumentationOptions options, ActivitySourceAdapter activitySource)
|
||||
public HttpHandlerDiagnosticListener(HttpClientInstrumentationOptions options)
|
||||
: base("HttpHandlerDiagnosticListener")
|
||||
{
|
||||
var framework = Assembly
|
||||
|
|
@ -64,7 +63,6 @@ namespace OpenTelemetry.Instrumentation.Http.Implementation
|
|||
}
|
||||
|
||||
this.options = options;
|
||||
this.activitySource = activitySource;
|
||||
}
|
||||
|
||||
public override void OnStartActivity(Activity activity, object payload)
|
||||
|
|
@ -107,7 +105,8 @@ namespace OpenTelemetry.Instrumentation.Http.Implementation
|
|||
|
||||
activity.DisplayName = HttpTagHelper.GetOperationNameForHttpMethod(request.Method);
|
||||
|
||||
this.activitySource.Start(activity, ActivityKind.Client, ActivitySource);
|
||||
ActivityInstrumentationHelper.SetActivitySourceProperty(activity, ActivitySource);
|
||||
ActivityInstrumentationHelper.SetKindProperty(activity, ActivityKind.Client);
|
||||
|
||||
if (activity.IsAllDataRequested)
|
||||
{
|
||||
|
|
@ -178,8 +177,6 @@ namespace OpenTelemetry.Instrumentation.Http.Implementation
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.activitySource.Stop(activity);
|
||||
}
|
||||
|
||||
public override void OnException(Activity activity, object payload)
|
||||
|
|
|
|||
|
|
@ -16,9 +16,7 @@
|
|||
|
||||
using System;
|
||||
using OpenTelemetry.Instrumentation.Http;
|
||||
#if NETFRAMEWORK
|
||||
using OpenTelemetry.Instrumentation.Http.Implementation;
|
||||
#endif
|
||||
|
||||
namespace OpenTelemetry.Trace
|
||||
{
|
||||
|
|
@ -60,7 +58,9 @@ namespace OpenTelemetry.Trace
|
|||
|
||||
configureHttpClientInstrumentationOptions?.Invoke(httpClientOptions);
|
||||
|
||||
builder.AddDiagnosticSourceInstrumentation((activitySource) => new HttpClientInstrumentation(activitySource, httpClientOptions));
|
||||
builder.AddInstrumentation(() => new HttpClientInstrumentation(httpClientOptions));
|
||||
builder.AddSource(HttpHandlerDiagnosticListener.ActivitySourceName);
|
||||
builder.AddLegacyActivity("System.Net.Http.HttpRequestOut");
|
||||
|
||||
#if NETFRAMEWORK
|
||||
builder.AddHttpWebRequestInstrumentation(configureHttpWebRequestInstrumentationOptions);
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
static OpenTelemetry.Trace.TracerProviderExtensions.ForceFlush(this OpenTelemetry.Trace.TracerProvider provider, int timeoutMilliseconds = -1) -> bool
|
||||
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 +1,2 @@
|
|||
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 +1,2 @@
|
|||
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 +1,2 @@
|
|||
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,7 +9,14 @@ please check the latest changes
|
|||
|
||||
## Unreleased
|
||||
|
||||
* Added `ForceFlush` to `TracerProvider`. ([#1837](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1837))
|
||||
* 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)
|
||||
|
||||
## 1.0.1
|
||||
|
||||
|
|
@ -57,8 +64,8 @@ Released 2021-Jan-29
|
|||
invalid attributes we now throw an exception instead of logging an error.
|
||||
([#1720](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1720))
|
||||
* Merging "this" resource with an "other" resource now prioritizes the "other"
|
||||
resource's attributes in a conflict. We've rectified to follow a recent
|
||||
change to the spec. We previously prioritized "this" resource's tags.
|
||||
resource's attributes in a conflict. We've rectified to follow a recent change
|
||||
to the spec. We previously prioritized "this" resource's tags.
|
||||
([#1728](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1728))
|
||||
* `BatchExportProcessor` will now flush any remaining spans left in a `Batch`
|
||||
after the export operation has completed.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
// <copyright file="ActivityInstrumentationHelper.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;
|
||||
|
||||
namespace OpenTelemetry.Instrumentation
|
||||
{
|
||||
internal static class ActivityInstrumentationHelper
|
||||
{
|
||||
internal static readonly Action<Activity, ActivityKind> SetKindProperty = CreateActivityKindSetter();
|
||||
internal static readonly Action<Activity, ActivitySource> SetActivitySourceProperty = CreateActivitySourceSetter();
|
||||
|
||||
private static Action<Activity, ActivitySource> CreateActivitySourceSetter()
|
||||
{
|
||||
ParameterExpression instance = Expression.Parameter(typeof(Activity), "instance");
|
||||
ParameterExpression propertyValue = Expression.Parameter(typeof(ActivitySource), "propertyValue");
|
||||
var body = Expression.Assign(Expression.Property(instance, "Source"), propertyValue);
|
||||
return Expression.Lambda<Action<Activity, ActivitySource>>(body, instance, propertyValue).Compile();
|
||||
}
|
||||
|
||||
private static Action<Activity, ActivityKind> CreateActivityKindSetter()
|
||||
{
|
||||
ParameterExpression instance = Expression.Parameter(typeof(Activity), "instance");
|
||||
ParameterExpression propertyValue = Expression.Parameter(typeof(ActivityKind), "propertyValue");
|
||||
var body = Expression.Assign(Expression.Property(instance, "Kind"), propertyValue);
|
||||
return Expression.Lambda<Action<Activity, ActivityKind>>(body, instance, propertyValue).Compile();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,193 +0,0 @@
|
|||
// <copyright file="ActivitySourceAdapter.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 OpenTelemetry.Internal;
|
||||
|
||||
namespace OpenTelemetry.Trace
|
||||
{
|
||||
/// <summary>
|
||||
/// This class encapsulates the logic for performing ActivitySource actions
|
||||
/// on Activities that are created using default ActivitySource.
|
||||
/// All activities created without using ActivitySource will have a
|
||||
/// default ActivitySource assigned to them with their name as empty string.
|
||||
/// This class is to be used by instrumentation adapters which converts/augments
|
||||
/// activies created without ActivitySource, into something which closely
|
||||
/// matches the one created using ActivitySource.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class is meant to be only used when writing new Instrumentation for
|
||||
/// libraries which are already instrumented with DiagnosticSource/Activity
|
||||
/// following this doc:
|
||||
/// https://github.com/dotnet/runtime/blob/master/src/libraries/System.Diagnostics.DiagnosticSource/src/ActivityUserGuide.md.
|
||||
/// </remarks>
|
||||
internal class ActivitySourceAdapter
|
||||
{
|
||||
private static readonly Action<Activity, ActivityKind> SetKindProperty = CreateActivityKindSetter();
|
||||
private static readonly Action<Activity, ActivitySource> SetActivitySourceProperty = CreateActivitySourceSetter();
|
||||
private readonly Sampler sampler;
|
||||
private readonly Action<Activity> getRequestedDataAction;
|
||||
private BaseProcessor<Activity> activityProcessor;
|
||||
|
||||
internal ActivitySourceAdapter(Sampler sampler, BaseProcessor<Activity> activityProcessor)
|
||||
{
|
||||
this.sampler = sampler ?? throw new ArgumentNullException(nameof(sampler));
|
||||
if (this.sampler is AlwaysOnSampler)
|
||||
{
|
||||
this.getRequestedDataAction = this.RunGetRequestedDataAlwaysOnSampler;
|
||||
}
|
||||
else if (this.sampler is AlwaysOffSampler)
|
||||
{
|
||||
this.getRequestedDataAction = this.RunGetRequestedDataAlwaysOffSampler;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.getRequestedDataAction = this.RunGetRequestedDataOtherSampler;
|
||||
}
|
||||
|
||||
this.activityProcessor = activityProcessor;
|
||||
}
|
||||
|
||||
private ActivitySourceAdapter()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method that starts an <see cref="Activity"/>.
|
||||
/// </summary>
|
||||
/// <param name="activity"><see cref="Activity"/> to be started.</param>
|
||||
/// <param name="kind">ActivityKind to be set of the activity.</param>
|
||||
/// <param name="source">ActivitySource to be set of the activity.</param>
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")]
|
||||
public void Start(Activity activity, ActivityKind kind, ActivitySource source)
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.ActivityStarted(activity);
|
||||
|
||||
SetActivitySourceProperty(activity, source);
|
||||
SetKindProperty(activity, kind);
|
||||
this.getRequestedDataAction(activity);
|
||||
if (activity.IsAllDataRequested)
|
||||
{
|
||||
this.activityProcessor?.OnStart(activity);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method that stops an <see cref="Activity"/>.
|
||||
/// </summary>
|
||||
/// <param name="activity"><see cref="Activity"/> to be stopped.</param>
|
||||
public void Stop(Activity activity)
|
||||
{
|
||||
OpenTelemetrySdkEventSource.Log.ActivityStopped(activity);
|
||||
|
||||
if (activity?.IsAllDataRequested ?? false)
|
||||
{
|
||||
this.activityProcessor?.OnEnd(activity);
|
||||
}
|
||||
}
|
||||
|
||||
internal void UpdateProcessor(BaseProcessor<Activity> processor)
|
||||
{
|
||||
this.activityProcessor = processor;
|
||||
}
|
||||
|
||||
private static Action<Activity, ActivitySource> CreateActivitySourceSetter()
|
||||
{
|
||||
ParameterExpression instance = Expression.Parameter(typeof(Activity), "instance");
|
||||
ParameterExpression propertyValue = Expression.Parameter(typeof(ActivitySource), "propertyValue");
|
||||
var body = Expression.Assign(Expression.Property(instance, "Source"), propertyValue);
|
||||
return Expression.Lambda<Action<Activity, ActivitySource>>(body, instance, propertyValue).Compile();
|
||||
}
|
||||
|
||||
private static Action<Activity, ActivityKind> CreateActivityKindSetter()
|
||||
{
|
||||
ParameterExpression instance = Expression.Parameter(typeof(Activity), "instance");
|
||||
ParameterExpression propertyValue = Expression.Parameter(typeof(ActivityKind), "propertyValue");
|
||||
var body = Expression.Assign(Expression.Property(instance, "Kind"), propertyValue);
|
||||
return Expression.Lambda<Action<Activity, ActivityKind>>(body, instance, propertyValue).Compile();
|
||||
}
|
||||
|
||||
private void RunGetRequestedDataAlwaysOnSampler(Activity activity)
|
||||
{
|
||||
activity.IsAllDataRequested = true;
|
||||
activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
|
||||
}
|
||||
|
||||
private void RunGetRequestedDataAlwaysOffSampler(Activity activity)
|
||||
{
|
||||
activity.IsAllDataRequested = false;
|
||||
}
|
||||
|
||||
private void RunGetRequestedDataOtherSampler(Activity activity)
|
||||
{
|
||||
ActivityContext parentContext;
|
||||
|
||||
// Check activity.ParentId alone is sufficient to normally determine if a activity is root or not. But if one uses activity.SetParentId to override the TraceId (without intending to set an actual parent), then additional check of parentspanid being empty is required to confirm if an activity is root or not.
|
||||
// This checker can be removed, once Activity exposes an API to customize ID Generation (https://github.com/dotnet/runtime/issues/46704) or issue https://github.com/dotnet/runtime/issues/46706 is addressed.
|
||||
if (string.IsNullOrEmpty(activity.ParentId) || activity.ParentSpanId.ToHexString() == "0000000000000000")
|
||||
{
|
||||
parentContext = default;
|
||||
}
|
||||
else if (activity.Parent != null)
|
||||
{
|
||||
parentContext = activity.Parent.Context;
|
||||
}
|
||||
else
|
||||
{
|
||||
parentContext = new ActivityContext(
|
||||
activity.TraceId,
|
||||
activity.ParentSpanId,
|
||||
activity.ActivityTraceFlags,
|
||||
activity.TraceStateString,
|
||||
isRemote: true);
|
||||
}
|
||||
|
||||
var samplingParameters = new SamplingParameters(
|
||||
parentContext,
|
||||
activity.TraceId,
|
||||
activity.DisplayName,
|
||||
activity.Kind,
|
||||
activity.TagObjects,
|
||||
activity.Links);
|
||||
|
||||
var samplingResult = this.sampler.ShouldSample(samplingParameters);
|
||||
|
||||
switch (samplingResult.Decision)
|
||||
{
|
||||
case SamplingDecision.Drop:
|
||||
activity.IsAllDataRequested = false;
|
||||
break;
|
||||
case SamplingDecision.RecordOnly:
|
||||
activity.IsAllDataRequested = true;
|
||||
break;
|
||||
case SamplingDecision.RecordAndSample:
|
||||
activity.IsAllDataRequested = true;
|
||||
activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
|
||||
break;
|
||||
}
|
||||
|
||||
if (samplingResult.Decision != SamplingDecision.Drop)
|
||||
{
|
||||
foreach (var att in samplingResult.Attributes)
|
||||
{
|
||||
activity.SetTag(att.Key, att.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -71,6 +71,22 @@ namespace OpenTelemetry.Trace
|
|||
return tracerProviderBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds activity with a given operation name to the list of subscribed activities. This is only for legacy activities (i.e. activities created without using ActivitySource API).
|
||||
/// </summary>
|
||||
/// <param name="tracerProviderBuilder">TracerProviderBuilder instance.</param>
|
||||
/// <param name="operationName">OperationName to add.</param>
|
||||
/// <returns>Returns <see cref="TracerProviderBuilder"/> for chaining.</returns>
|
||||
public static TracerProviderBuilder AddLegacyActivity(this TracerProviderBuilder tracerProviderBuilder, string operationName)
|
||||
{
|
||||
if (tracerProviderBuilder is TracerProviderBuilderSdk tracerProviderBuilderSdk)
|
||||
{
|
||||
tracerProviderBuilderSdk.AddLegacyActivity(operationName);
|
||||
}
|
||||
|
||||
return tracerProviderBuilder;
|
||||
}
|
||||
|
||||
public static TracerProvider Build(this TracerProviderBuilder tracerProviderBuilder)
|
||||
{
|
||||
if (tracerProviderBuilder is TracerProviderBuilderSdk tracerProviderBuilderSdk)
|
||||
|
|
@ -80,32 +96,5 @@ namespace OpenTelemetry.Trace
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a DiagnosticSource based instrumentation.
|
||||
/// This is required for libraries which is already instrumented with
|
||||
/// DiagnosticSource and Activity, without using ActivitySource.
|
||||
/// </summary>
|
||||
/// <typeparam name="TInstrumentation">Type of instrumentation class.</typeparam>
|
||||
/// <param name="tracerProviderBuilder">TracerProviderBuilder instance.</param>
|
||||
/// <param name="instrumentationFactory">Function that builds instrumentation.</param>
|
||||
/// <returns>Returns <see cref="TracerProviderBuilder"/> for chaining.</returns>
|
||||
internal static TracerProviderBuilder AddDiagnosticSourceInstrumentation<TInstrumentation>(
|
||||
this TracerProviderBuilder tracerProviderBuilder,
|
||||
Func<ActivitySourceAdapter, TInstrumentation> instrumentationFactory)
|
||||
where TInstrumentation : class
|
||||
{
|
||||
if (instrumentationFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(instrumentationFactory));
|
||||
}
|
||||
|
||||
if (tracerProviderBuilder is TracerProviderBuilderSdk tracerProviderBuilderSdk)
|
||||
{
|
||||
tracerProviderBuilderSdk.AddDiagnosticSourceInstrumentation(instrumentationFactory);
|
||||
}
|
||||
|
||||
return tracerProviderBuilder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,11 +26,11 @@ namespace OpenTelemetry.Trace
|
|||
/// </summary>
|
||||
internal class TracerProviderBuilderSdk : TracerProviderBuilder
|
||||
{
|
||||
private readonly List<DiagnosticSourceInstrumentationFactory> diagnosticSourceInstrumentationFactories = new List<DiagnosticSourceInstrumentationFactory>();
|
||||
private readonly List<InstrumentationFactory> instrumentationFactories = new List<InstrumentationFactory>();
|
||||
|
||||
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());
|
||||
|
||||
|
|
@ -129,55 +129,32 @@ namespace OpenTelemetry.Trace
|
|||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds activity with a given operation name to the list of subscribed activities. This is only for legacy activities (i.e. activities created without using ActivitySource API).
|
||||
/// </summary>
|
||||
/// <param name="operationName">OperationName to add.</param>
|
||||
/// <returns>Returns <see cref="TracerProviderBuilder"/> for chaining.</returns>
|
||||
internal TracerProviderBuilder AddLegacyActivity(string operationName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(operationName))
|
||||
{
|
||||
throw new ArgumentException($"{nameof(operationName)} contains null or whitespace string.");
|
||||
}
|
||||
|
||||
this.legacyActivityOperationNames[operationName] = true;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
internal TracerProvider Build()
|
||||
{
|
||||
return new TracerProviderSdk(
|
||||
this.resourceBuilder.Build(),
|
||||
this.sources,
|
||||
this.diagnosticSourceInstrumentationFactories,
|
||||
this.instrumentationFactories,
|
||||
this.sampler,
|
||||
this.processors);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a DiagnosticSource based instrumentation.
|
||||
/// This is required for libraries which is already instrumented with
|
||||
/// DiagnosticSource and Activity, without using ActivitySource.
|
||||
/// </summary>
|
||||
/// <typeparam name="TInstrumentation">Type of instrumentation class.</typeparam>
|
||||
/// <param name="instrumentationFactory">Function that builds instrumentation.</param>
|
||||
/// <returns>Returns <see cref="TracerProviderBuilder"/> for chaining.</returns>
|
||||
internal TracerProviderBuilder AddDiagnosticSourceInstrumentation<TInstrumentation>(
|
||||
Func<ActivitySourceAdapter, TInstrumentation> instrumentationFactory)
|
||||
where TInstrumentation : class
|
||||
{
|
||||
if (instrumentationFactory == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(instrumentationFactory));
|
||||
}
|
||||
|
||||
this.diagnosticSourceInstrumentationFactories.Add(
|
||||
new DiagnosticSourceInstrumentationFactory(
|
||||
typeof(TInstrumentation).Name,
|
||||
"semver:" + typeof(TInstrumentation).Assembly.GetName().Version,
|
||||
instrumentationFactory));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
internal readonly struct DiagnosticSourceInstrumentationFactory
|
||||
{
|
||||
public readonly string Name;
|
||||
public readonly string Version;
|
||||
public readonly Func<ActivitySourceAdapter, object> Factory;
|
||||
|
||||
internal DiagnosticSourceInstrumentationFactory(string name, string version, Func<ActivitySourceAdapter, object> factory)
|
||||
{
|
||||
this.Name = name;
|
||||
this.Version = version;
|
||||
this.Factory = factory;
|
||||
}
|
||||
this.processors,
|
||||
this.legacyActivityOperationNames);
|
||||
}
|
||||
|
||||
internal readonly struct InstrumentationFactory
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ using System.Diagnostics;
|
|||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using OpenTelemetry.Internal;
|
||||
using OpenTelemetry.Resources;
|
||||
|
||||
|
|
@ -33,34 +32,29 @@ namespace OpenTelemetry.Trace
|
|||
private readonly List<object> instrumentations = new List<object>();
|
||||
private readonly ActivityListener listener;
|
||||
private readonly Sampler sampler;
|
||||
private readonly ActivitySourceAdapter adapter;
|
||||
private readonly Dictionary<string, bool> legacyActivityOperationNames;
|
||||
private BaseProcessor<Activity> processor;
|
||||
private Action<Activity> getRequestedDataAction;
|
||||
private bool supportLegacyActivity;
|
||||
|
||||
internal TracerProviderSdk(
|
||||
Resource resource,
|
||||
IEnumerable<string> sources,
|
||||
IEnumerable<TracerProviderBuilderSdk.DiagnosticSourceInstrumentationFactory> diagnosticSourceInstrumentationFactories,
|
||||
IEnumerable<TracerProviderBuilderSdk.InstrumentationFactory> instrumentationFactories,
|
||||
Sampler sampler,
|
||||
List<BaseProcessor<Activity>> processors)
|
||||
List<BaseProcessor<Activity>> processors,
|
||||
Dictionary<string, bool> legacyActivityOperationNames)
|
||||
{
|
||||
this.Resource = resource;
|
||||
this.sampler = sampler;
|
||||
this.legacyActivityOperationNames = legacyActivityOperationNames;
|
||||
this.supportLegacyActivity = legacyActivityOperationNames.Count > 0;
|
||||
|
||||
foreach (var processor in processors)
|
||||
{
|
||||
this.AddProcessor(processor);
|
||||
}
|
||||
|
||||
if (diagnosticSourceInstrumentationFactories.Any())
|
||||
{
|
||||
this.adapter = new ActivitySourceAdapter(sampler, this.processor);
|
||||
foreach (var instrumentationFactory in diagnosticSourceInstrumentationFactories)
|
||||
{
|
||||
this.instrumentations.Add(instrumentationFactory.Factory(this.adapter));
|
||||
}
|
||||
}
|
||||
|
||||
if (instrumentationFactories.Any())
|
||||
{
|
||||
foreach (var instrumentationFactory in instrumentationFactories)
|
||||
|
|
@ -76,6 +70,21 @@ namespace OpenTelemetry.Trace
|
|||
{
|
||||
OpenTelemetrySdkEventSource.Log.ActivityStarted(activity);
|
||||
|
||||
if (this.supportLegacyActivity && string.IsNullOrEmpty(activity.Source.Name))
|
||||
{
|
||||
// We have a legacy activity in hand now
|
||||
if (legacyActivityOperationNames.ContainsKey(activity.OperationName))
|
||||
{
|
||||
// Legacy activity matches the user configured list. Call sampler for the legacy activity
|
||||
this.getRequestedDataAction(activity);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Legacy activity doesn't match the user configured list. No need to proceed further.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!activity.IsAllDataRequested)
|
||||
{
|
||||
return;
|
||||
|
|
@ -92,6 +101,16 @@ namespace OpenTelemetry.Trace
|
|||
{
|
||||
OpenTelemetrySdkEventSource.Log.ActivityStopped(activity);
|
||||
|
||||
if (this.supportLegacyActivity && string.IsNullOrEmpty(activity.Source.Name))
|
||||
{
|
||||
// We have a legacy activity in hand now
|
||||
if (!legacyActivityOperationNames.ContainsKey(activity.OperationName))
|
||||
{
|
||||
// Legacy activity doesn't match the user configured list. No need to proceed further.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!activity.IsAllDataRequested)
|
||||
{
|
||||
return;
|
||||
|
|
@ -116,17 +135,20 @@ namespace OpenTelemetry.Trace
|
|||
{
|
||||
listener.Sample = (ref ActivityCreationOptions<ActivityContext> options) =>
|
||||
!Sdk.SuppressInstrumentation ? ActivitySamplingResult.AllDataAndRecorded : ActivitySamplingResult.None;
|
||||
this.getRequestedDataAction = this.RunGetRequestedDataAlwaysOnSampler;
|
||||
}
|
||||
else if (sampler is AlwaysOffSampler)
|
||||
{
|
||||
listener.Sample = (ref ActivityCreationOptions<ActivityContext> options) =>
|
||||
!Sdk.SuppressInstrumentation ? PropagateOrIgnoreData(options.Parent.TraceId) : ActivitySamplingResult.None;
|
||||
this.getRequestedDataAction = this.RunGetRequestedDataAlwaysOffSampler;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This delegate informs ActivitySource about sampling decision when the parent context is an ActivityContext.
|
||||
listener.Sample = (ref ActivityCreationOptions<ActivityContext> options) =>
|
||||
!Sdk.SuppressInstrumentation ? ComputeActivitySamplingResult(options, sampler) : ActivitySamplingResult.None;
|
||||
this.getRequestedDataAction = this.RunGetRequestedDataOtherSampler;
|
||||
}
|
||||
|
||||
if (sources.Any())
|
||||
|
|
@ -153,7 +175,10 @@ namespace OpenTelemetry.Trace
|
|||
|
||||
// Function which takes ActivitySource and returns true/false to indicate if it should be subscribed to
|
||||
// or not.
|
||||
listener.ShouldListenTo = (activitySource) => regex.IsMatch(activitySource.Name);
|
||||
listener.ShouldListenTo = (activitySource) =>
|
||||
this.supportLegacyActivity ?
|
||||
string.IsNullOrEmpty(activitySource.Name) || regex.IsMatch(activitySource.Name) :
|
||||
regex.IsMatch(activitySource.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -164,11 +189,23 @@ namespace OpenTelemetry.Trace
|
|||
activitySources[name] = true;
|
||||
}
|
||||
|
||||
if (this.supportLegacyActivity)
|
||||
{
|
||||
activitySources[string.Empty] = true;
|
||||
}
|
||||
|
||||
// Function which takes ActivitySource and returns true/false to indicate if it should be subscribed to
|
||||
// or not.
|
||||
listener.ShouldListenTo = (activitySource) => activitySources.ContainsKey(activitySource.Name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.supportLegacyActivity)
|
||||
{
|
||||
listener.ShouldListenTo = (activitySource) => string.IsNullOrEmpty(activitySource.Name);
|
||||
}
|
||||
}
|
||||
|
||||
ActivitySource.AddActivityListener(listener);
|
||||
this.listener = listener;
|
||||
|
|
@ -202,8 +239,6 @@ namespace OpenTelemetry.Trace
|
|||
});
|
||||
}
|
||||
|
||||
this.adapter?.UpdateProcessor(this.processor);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -318,5 +353,73 @@ namespace OpenTelemetry.Trace
|
|||
? ActivitySamplingResult.PropagationData
|
||||
: ActivitySamplingResult.None;
|
||||
}
|
||||
|
||||
private void RunGetRequestedDataAlwaysOnSampler(Activity activity)
|
||||
{
|
||||
activity.IsAllDataRequested = true;
|
||||
activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
|
||||
}
|
||||
|
||||
private void RunGetRequestedDataAlwaysOffSampler(Activity activity)
|
||||
{
|
||||
activity.IsAllDataRequested = false;
|
||||
}
|
||||
|
||||
private void RunGetRequestedDataOtherSampler(Activity activity)
|
||||
{
|
||||
ActivityContext parentContext;
|
||||
|
||||
// Check activity.ParentId alone is sufficient to normally determine if a activity is root or not. But if one uses activity.SetParentId to override the TraceId (without intending to set an actual parent), then additional check of parentspanid being empty is required to confirm if an activity is root or not.
|
||||
// This checker can be removed, once Activity exposes an API to customize ID Generation (https://github.com/dotnet/runtime/issues/46704) or issue https://github.com/dotnet/runtime/issues/46706 is addressed.
|
||||
if (string.IsNullOrEmpty(activity.ParentId) || activity.ParentSpanId.ToHexString() == "0000000000000000")
|
||||
{
|
||||
parentContext = default;
|
||||
}
|
||||
else if (activity.Parent != null)
|
||||
{
|
||||
parentContext = activity.Parent.Context;
|
||||
}
|
||||
else
|
||||
{
|
||||
parentContext = new ActivityContext(
|
||||
activity.TraceId,
|
||||
activity.ParentSpanId,
|
||||
activity.ActivityTraceFlags,
|
||||
activity.TraceStateString,
|
||||
isRemote: true);
|
||||
}
|
||||
|
||||
var samplingParameters = new SamplingParameters(
|
||||
parentContext,
|
||||
activity.TraceId,
|
||||
activity.DisplayName,
|
||||
activity.Kind,
|
||||
activity.TagObjects,
|
||||
activity.Links);
|
||||
|
||||
var samplingResult = this.sampler.ShouldSample(samplingParameters);
|
||||
|
||||
switch (samplingResult.Decision)
|
||||
{
|
||||
case SamplingDecision.Drop:
|
||||
activity.IsAllDataRequested = false;
|
||||
break;
|
||||
case SamplingDecision.RecordOnly:
|
||||
activity.IsAllDataRequested = true;
|
||||
break;
|
||||
case SamplingDecision.RecordAndSample:
|
||||
activity.IsAllDataRequested = true;
|
||||
activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
|
||||
break;
|
||||
}
|
||||
|
||||
if (samplingResult.Decision != SamplingDecision.Drop)
|
||||
{
|
||||
foreach (var att in samplingResult.Attributes)
|
||||
{
|
||||
activity.SetTag(att.Key, att.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,78 +0,0 @@
|
|||
// <copyright file="ActivitySourceAdapterBenchmark.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.Diagnostics;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using OpenTelemetry;
|
||||
using OpenTelemetry.Trace;
|
||||
|
||||
namespace Benchmarks.Trace
|
||||
{
|
||||
[MemoryDiagnoser]
|
||||
public class ActivitySourceAdapterBenchmark
|
||||
{
|
||||
private TestInstrumentation testInstrumentation = null;
|
||||
private TracerProvider tracerProvider;
|
||||
|
||||
[GlobalSetup]
|
||||
public void GlobalSetup()
|
||||
{
|
||||
this.tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.AddDiagnosticSourceInstrumentation((adapter) =>
|
||||
{
|
||||
this.testInstrumentation = new TestInstrumentation(adapter);
|
||||
return this.testInstrumentation;
|
||||
})
|
||||
.Build();
|
||||
}
|
||||
|
||||
[GlobalCleanup]
|
||||
public void GlobalCleanup()
|
||||
{
|
||||
this.tracerProvider.Dispose();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void ActivitySourceAdapterStartStop()
|
||||
{
|
||||
var activity = new Activity("test").Start();
|
||||
this.testInstrumentation.Start(activity);
|
||||
this.testInstrumentation.Stop(activity);
|
||||
activity.Stop();
|
||||
}
|
||||
|
||||
private class TestInstrumentation
|
||||
{
|
||||
internal static ActivitySource ActivitySource = new ActivitySource("test", "1.0.0");
|
||||
private ActivitySourceAdapter adapter;
|
||||
|
||||
public TestInstrumentation(ActivitySourceAdapter adapter)
|
||||
{
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
public void Start(Activity activity)
|
||||
{
|
||||
this.adapter.Start(activity, ActivityKind.Internal, ActivitySource);
|
||||
}
|
||||
|
||||
public void Stop(Activity activity)
|
||||
{
|
||||
this.adapter.Stop(activity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -31,6 +31,8 @@ namespace Benchmarks.Trace
|
|||
private readonly ActivitySource sourceWithOneProcessor = new ActivitySource("Benchmark.OneProcessor");
|
||||
private readonly ActivitySource sourceWithTwoProcessors = new ActivitySource("Benchmark.TwoProcessors");
|
||||
private readonly ActivitySource sourceWithThreeProcessors = new ActivitySource("Benchmark.ThreeProcessors");
|
||||
private readonly ActivitySource sourceWithOneLegacyActivityOperationNameSubscription = new ActivitySource("Benchmark.OneInstrumentation");
|
||||
private readonly ActivitySource sourceWithTwoLegacyActivityOperationNameSubscriptions = new ActivitySource("Benchmark.TwoInstrumentations");
|
||||
|
||||
public TraceBenchmarks()
|
||||
{
|
||||
|
|
@ -80,6 +82,21 @@ namespace Benchmarks.Trace
|
|||
.AddProcessor(new DummyActivityProcessor())
|
||||
.AddProcessor(new DummyActivityProcessor())
|
||||
.Build();
|
||||
|
||||
Sdk.CreateTracerProviderBuilder()
|
||||
.SetSampler(new AlwaysOnSampler())
|
||||
.AddSource(this.sourceWithOneLegacyActivityOperationNameSubscription.Name)
|
||||
.AddLegacyActivity("TestOperationName")
|
||||
.AddProcessor(new DummyActivityProcessor())
|
||||
.Build();
|
||||
|
||||
Sdk.CreateTracerProviderBuilder()
|
||||
.SetSampler(new AlwaysOnSampler())
|
||||
.AddSource(this.sourceWithTwoLegacyActivityOperationNameSubscriptions.Name)
|
||||
.AddLegacyActivity("TestOperationName1")
|
||||
.AddLegacyActivity("TestOperationName2")
|
||||
.AddProcessor(new DummyActivityProcessor())
|
||||
.Build();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
|
|
@ -142,6 +159,22 @@ namespace Benchmarks.Trace
|
|||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void OneInstrumentation()
|
||||
{
|
||||
using (var activity = this.sourceWithOneLegacyActivityOperationNameSubscription.StartActivity("Benchmark"))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void TwoInstrumentations()
|
||||
{
|
||||
using (var activity = this.sourceWithTwoLegacyActivityOperationNameSubscriptions.StartActivity("Benchmark"))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal class DummyActivityProcessor : BaseProcessor<Activity>
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -202,8 +202,8 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
|
|||
|
||||
if (HttpContext.Current.Request.Path == filter || filter == "{ThrowException}")
|
||||
{
|
||||
// only SetParentProvider/Shutdown/Dispose are called because request was filtered.
|
||||
Assert.Equal(3, activityProcessor.Invocations.Count);
|
||||
// only SetParentProvider/Shutdown/Dispose/OnStart are called because request was filtered.
|
||||
Assert.Equal(4, activityProcessor.Invocations.Count);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -211,7 +211,60 @@ namespace OpenTelemetry.Instrumentation.AspNet.Tests
|
|||
var currentActivity = Activity.Current;
|
||||
|
||||
Activity span;
|
||||
Assert.Equal(5, activityProcessor.Invocations.Count); // SetParentProvider/OnStart/OnEnd/OnShutdown/Dispose called.
|
||||
if (carrierFormat == "CustomContextNonmatchParent")
|
||||
{
|
||||
Assert.Equal(6, activityProcessor.Invocations.Count); // SetParentProvider/OnStart(framework activity)/OnStart(sibling activity)/OnEnd(sibling activity)/OnShutdown/Dispose called.
|
||||
|
||||
var startedActivities = activityProcessor.Invocations.Where(invo => invo.Method.Name == "OnStart");
|
||||
var stoppedActivities = activityProcessor.Invocations.Where(invo => invo.Method.Name == "OnEnd");
|
||||
Assert.Equal(2, startedActivities.Count());
|
||||
Assert.Single(stoppedActivities);
|
||||
|
||||
// The activity created by the framework and the sibling activity are both sent to Processor.OnStart
|
||||
Assert.Contains(startedActivities, item =>
|
||||
{
|
||||
var startedActivity = item.Arguments[0] as Activity;
|
||||
return startedActivity.OperationName == HttpInListener.ActivityOperationName;
|
||||
});
|
||||
|
||||
Assert.Contains(startedActivities, item =>
|
||||
{
|
||||
var startedActivity = item.Arguments[0] as Activity;
|
||||
return startedActivity.OperationName == HttpInListener.ActivityNameByHttpInListener;
|
||||
});
|
||||
|
||||
// Only the sibling activity is sent to Processor.OnEnd
|
||||
Assert.Contains(stoppedActivities, item =>
|
||||
{
|
||||
var stoppedActivity = item.Arguments[0] as Activity;
|
||||
return stoppedActivity.OperationName == HttpInListener.ActivityNameByHttpInListener;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Equal(5, activityProcessor.Invocations.Count); // SetParentProvider/OnStart/OnEnd/OnShutdown/Dispose called.
|
||||
|
||||
var startedActivities = activityProcessor.Invocations.Where(invo => invo.Method.Name == "OnStart");
|
||||
var stoppedActivities = activityProcessor.Invocations.Where(invo => invo.Method.Name == "OnEnd");
|
||||
|
||||
// There is no sibling activity created
|
||||
Assert.Single(startedActivities);
|
||||
Assert.Single(stoppedActivities);
|
||||
|
||||
Assert.Contains(startedActivities, item =>
|
||||
{
|
||||
var startedActivity = item.Arguments[0] as Activity;
|
||||
return startedActivity.OperationName == HttpInListener.ActivityOperationName;
|
||||
});
|
||||
|
||||
// Only the sibling activity is sent to Processor.OnEnd
|
||||
Assert.Contains(stoppedActivities, item =>
|
||||
{
|
||||
var stoppedActivity = item.Arguments[0] as Activity;
|
||||
return stoppedActivity.OperationName == HttpInListener.ActivityOperationName;
|
||||
});
|
||||
}
|
||||
|
||||
span = (Activity)activityProcessor.Invocations[2].Arguments[0];
|
||||
|
||||
Assert.Equal(
|
||||
|
|
|
|||
|
|
@ -88,7 +88,10 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Tests
|
|||
}
|
||||
|
||||
Assert.Equal(3, activityProcessor.Invocations.Count); // begin and end was called
|
||||
var activity = (Activity)activityProcessor.Invocations[2].Arguments[0];
|
||||
|
||||
// we should only call Processor.OnEnd for the "/api/values" request
|
||||
Assert.Single(activityProcessor.Invocations, invo => invo.Method.Name == "OnEnd");
|
||||
var activity = activityProcessor.Invocations.FirstOrDefault(invo => invo.Method.Name == "OnEnd").Arguments[0] as Activity;
|
||||
|
||||
Assert.Equal(200, activity.GetTagValue(SemanticConventions.AttributeHttpStatusCode));
|
||||
|
||||
|
|
@ -168,8 +171,15 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Tests
|
|||
WaitForProcessorInvocations(activityProcessor, 3);
|
||||
}
|
||||
|
||||
Assert.Equal(3, activityProcessor.Invocations.Count); // begin and end was called
|
||||
var activity = (Activity)activityProcessor.Invocations[2].Arguments[0];
|
||||
// List of invocations
|
||||
// 1. SetParentProvider for TracerProviderSdk
|
||||
// 2. OnStart for the activity created by AspNetCore with the OperationName: Microsoft.AspNetCore.Hosting.HttpRequestIn
|
||||
// 3. OnStart for the sibling activity created by the instrumentation library with the OperationName: ActivityCreatedByHttpInListener
|
||||
// 4. OnEnd for the sibling activity created by the instrumentation library with the OperationName: ActivityCreatedByHttpInListener
|
||||
|
||||
// we should only call Processor.OnEnd once for the sibling activity with the OperationName ActivityCreatedByHttpInListener
|
||||
Assert.Single(activityProcessor.Invocations, invo => invo.Method.Name == "OnEnd");
|
||||
var activity = activityProcessor.Invocations.FirstOrDefault(invo => invo.Method.Name == "OnEnd").Arguments[0] as Activity;
|
||||
|
||||
#if !NETCOREAPP2_1
|
||||
// ASP.NET Core after 2.x is W3C aware and hence Activity created by it
|
||||
|
|
@ -221,14 +231,42 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Tests
|
|||
var response = await client.GetAsync("/api/values/2");
|
||||
response.EnsureSuccessStatusCode(); // Status Code 200-299
|
||||
|
||||
WaitForProcessorInvocations(activityProcessor, 3);
|
||||
WaitForProcessorInvocations(activityProcessor, 4);
|
||||
}
|
||||
|
||||
// begin and end was called once each.
|
||||
Assert.Equal(3, activityProcessor.Invocations.Count);
|
||||
var activity = (Activity)activityProcessor.Invocations[2].Arguments[0];
|
||||
Assert.Equal("ActivityCreatedByHttpInListener", activity.OperationName);
|
||||
// List of invocations on the processor
|
||||
// 1. SetParentProvider for TracerProviderSdk
|
||||
// 2. OnStart for the activity created by AspNetCore with the OperationName: Microsoft.AspNetCore.Hosting.HttpRequestIn
|
||||
// 3. OnStart for the sibling activity created by the instrumentation library with the OperationName: ActivityCreatedByHttpInListener
|
||||
// 4. OnEnd for the sibling activity created by the instrumentation library with the OperationName: ActivityCreatedByHttpInListener
|
||||
Assert.Equal(4, activityProcessor.Invocations.Count);
|
||||
|
||||
var startedActivities = activityProcessor.Invocations.Where(invo => invo.Method.Name == "OnStart");
|
||||
var stoppedActivities = activityProcessor.Invocations.Where(invo => invo.Method.Name == "OnEnd");
|
||||
Assert.Equal(2, startedActivities.Count());
|
||||
Assert.Single(stoppedActivities);
|
||||
|
||||
// The activity created by the framework and the sibling activity are both sent to Processor.OnStart
|
||||
Assert.Contains(startedActivities, item =>
|
||||
{
|
||||
var startedActivity = item.Arguments[0] as Activity;
|
||||
return startedActivity.OperationName == HttpInListener.ActivityOperationName;
|
||||
});
|
||||
|
||||
Assert.Contains(startedActivities, item =>
|
||||
{
|
||||
var startedActivity = item.Arguments[0] as Activity;
|
||||
return startedActivity.OperationName == HttpInListener.ActivityNameByHttpInListener;
|
||||
});
|
||||
|
||||
// Only the sibling activity is sent to Processor.OnEnd
|
||||
Assert.Contains(stoppedActivities, item =>
|
||||
{
|
||||
var stoppedActivity = item.Arguments[0] as Activity;
|
||||
return stoppedActivity.OperationName == HttpInListener.ActivityNameByHttpInListener;
|
||||
});
|
||||
|
||||
var activity = activityProcessor.Invocations.FirstOrDefault(invo => invo.Method.Name == "OnEnd").Arguments[0] as Activity;
|
||||
Assert.Equal(ActivityKind.Server, activity.Kind);
|
||||
Assert.True(activity.Duration != TimeSpan.Zero);
|
||||
Assert.Equal("api/Values/{id}", activity.DisplayName);
|
||||
|
|
@ -271,12 +309,18 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Tests
|
|||
response1.EnsureSuccessStatusCode(); // Status Code 200-299
|
||||
response2.EnsureSuccessStatusCode(); // Status Code 200-299
|
||||
|
||||
WaitForProcessorInvocations(activityProcessor, 3);
|
||||
WaitForProcessorInvocations(activityProcessor, 4);
|
||||
}
|
||||
|
||||
// we should only create one span and never call processor with another
|
||||
Assert.Equal(3, activityProcessor.Invocations.Count); // begin and end was called
|
||||
var activity = (Activity)activityProcessor.Invocations[2].Arguments[0];
|
||||
// 1. SetParentProvider for TracerProviderSdk
|
||||
// 2. OnStart for the activity created by AspNetCore for "/api/values" with the OperationName: Microsoft.AspNetCore.Hosting.HttpRequestIn
|
||||
// 3. OnEnd for the activity created by AspNetCore for "/api/values" with the OperationName: Microsoft.AspNetCore.Hosting.HttpRequestIn
|
||||
// 4. OnStart for the activity created by AspNetCore for "/api/values/2" with the OperationName: Microsoft.AspNetCore.Hosting.HttpRequestIn
|
||||
Assert.Equal(4, activityProcessor.Invocations.Count);
|
||||
|
||||
// we should only call Processor.OnEnd for the "/api/values" request
|
||||
Assert.Single(activityProcessor.Invocations, invo => invo.Method.Name == "OnEnd");
|
||||
var activity = activityProcessor.Invocations.FirstOrDefault(invo => invo.Method.Name == "OnEnd").Arguments[0] as Activity;
|
||||
|
||||
Assert.Equal(ActivityKind.Server, activity.Kind);
|
||||
Assert.Equal("/api/values", activity.GetTagValue(SpanAttributeConstants.HttpPathKey) as string);
|
||||
|
|
@ -328,8 +372,16 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Tests
|
|||
|
||||
// As InstrumentationFilter threw, we continue as if the
|
||||
// InstrumentationFilter did not exist.
|
||||
Assert.Equal(3, activityProcessor.Invocations.Count); // begin and end was called
|
||||
var activity = (Activity)activityProcessor.Invocations[2].Arguments[0];
|
||||
|
||||
// List of invocations on the processor
|
||||
// 1. SetParentProvider for TracerProviderSdk
|
||||
// 2. OnStart for the activity created by AspNetCore for "/api/values" with the OperationName: Microsoft.AspNetCore.Hosting.HttpRequestIn
|
||||
// 3. OnEnd for the activity created by AspNetCore for "/api/values" with the OperationName: Microsoft.AspNetCore.Hosting.HttpRequestIn
|
||||
// 4. OnStart for the activity created by AspNetCore for "/api/values/2" with the OperationName: Microsoft.AspNetCore.Hosting.HttpRequestIn
|
||||
|
||||
// we should only call Processor.OnEnd for the "/api/values" request
|
||||
Assert.Single(activityProcessor.Invocations, invo => invo.Method.Name == "OnEnd");
|
||||
var activity = activityProcessor.Invocations.FirstOrDefault(invo => invo.Method.Name == "OnEnd").Arguments[0] as Activity;
|
||||
|
||||
Assert.Equal(ActivityKind.Server, activity.Kind);
|
||||
Assert.Equal("/api/values", activity.GetTagValue(SpanAttributeConstants.HttpPathKey) as string);
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ namespace OpenTelemetry.Instrumentation.Http.Tests
|
|||
await c.SendAsync(request);
|
||||
}
|
||||
|
||||
Assert.Equal(3, processor.Invocations.Count); // SetParentProvider/OnShutdown/Dispose called.
|
||||
Assert.Equal(4, processor.Invocations.Count); // SetParentProvider/OnShutdown/Dispose/OnStart called.
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -249,7 +249,7 @@ namespace OpenTelemetry.Instrumentation.Http.Tests
|
|||
await c.GetAsync(this.url);
|
||||
}
|
||||
|
||||
Assert.Equal(3, processor.Invocations.Count); // SetParentProvider/OnShutdown/Dispose called.
|
||||
Assert.Equal(4, processor.Invocations.Count); // SetParentProvider/OnShutdown/Dispose/OnStart called.
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -270,7 +270,7 @@ namespace OpenTelemetry.Instrumentation.Http.Tests
|
|||
}
|
||||
}
|
||||
|
||||
Assert.Equal(3, processor.Invocations.Count); // SetParentProvider/OnShutdown/Dispose called.
|
||||
Assert.Equal(4, processor.Invocations.Count); // SetParentProvider/OnShutdown/Dispose/OnStart called.
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
// <copyright file="ActivityInstrumentationHelperTest.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.Diagnostics;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Instrumentation.Tests
|
||||
{
|
||||
public class ActivityInstrumentationHelperTest
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("TestActivitySource", null)]
|
||||
[InlineData("TestActivitySource", "1.0.0")]
|
||||
public void SetActivitySource(string name, string version)
|
||||
{
|
||||
var activity = new Activity("Test");
|
||||
var activitySource = new ActivitySource(name, version);
|
||||
|
||||
activity.Start();
|
||||
ActivityInstrumentationHelper.SetActivitySourceProperty(activity, activitySource);
|
||||
Assert.Equal(activitySource.Name, activity.Source.Name);
|
||||
Assert.Equal(activitySource.Version, activity.Source.Version);
|
||||
activity.Stop();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(ActivityKind.Client)]
|
||||
[InlineData(ActivityKind.Consumer)]
|
||||
[InlineData(ActivityKind.Internal)]
|
||||
[InlineData(ActivityKind.Producer)]
|
||||
[InlineData(ActivityKind.Server)]
|
||||
public void SetActivityKind(ActivityKind activityKind)
|
||||
{
|
||||
var activity = new Activity("Test");
|
||||
activity.Start();
|
||||
ActivityInstrumentationHelper.SetKindProperty(activity, activityKind);
|
||||
Assert.Equal(activityKind, activity.Kind);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,267 +0,0 @@
|
|||
// <copyright file="ActivitySourceAdapterTest.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.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using OpenTelemetry.Tests;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Trace.Tests
|
||||
{
|
||||
public class ActivitySourceAdapterTest : IDisposable
|
||||
{
|
||||
private TestSampler testSampler;
|
||||
private TestActivityProcessor testProcessor;
|
||||
private ActivitySourceAdapter activitySourceAdapter;
|
||||
|
||||
static ActivitySourceAdapterTest()
|
||||
{
|
||||
Activity.DefaultIdFormat = ActivityIdFormat.W3C;
|
||||
Activity.ForceDefaultIdFormat = true;
|
||||
}
|
||||
|
||||
public ActivitySourceAdapterTest()
|
||||
{
|
||||
this.testSampler = new TestSampler();
|
||||
this.testProcessor = new TestActivityProcessor();
|
||||
this.activitySourceAdapter = new ActivitySourceAdapter(this.testSampler, this.testProcessor);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ActivitySourceAdapterValidatesConstructor()
|
||||
{
|
||||
// Sampler null
|
||||
Assert.Throws<ArgumentNullException>(() => new ActivitySourceAdapter(null, this.testProcessor));
|
||||
|
||||
// Processor null. This is not expected to throw as processor can
|
||||
// be null and can be later added.
|
||||
var adapter = new ActivitySourceAdapter(this.testSampler, null);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(ActivityKind.Client)]
|
||||
[InlineData(ActivityKind.Consumer)]
|
||||
[InlineData(ActivityKind.Internal)]
|
||||
[InlineData(ActivityKind.Producer)]
|
||||
[InlineData(ActivityKind.Server)]
|
||||
public void ActivitySourceAdapterSetsKind(ActivityKind kind)
|
||||
{
|
||||
var activity = new Activity("test");
|
||||
activity.Start();
|
||||
this.activitySourceAdapter.Start(activity, kind, new ActivitySource("test", "1.0.0"));
|
||||
|
||||
Assert.Equal(kind, activity.Kind);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(SamplingDecision.Drop)]
|
||||
[InlineData(SamplingDecision.RecordOnly)]
|
||||
[InlineData(SamplingDecision.RecordAndSample)]
|
||||
public void ActivitySourceAdapterCallsStartStopActivityProcessor1(SamplingDecision decision)
|
||||
{
|
||||
this.testSampler.SamplingAction = (samplingParameters) =>
|
||||
{
|
||||
return new SamplingResult(decision);
|
||||
};
|
||||
|
||||
bool startCalled = false;
|
||||
bool endCalled = false;
|
||||
this.testProcessor.StartAction =
|
||||
(a) =>
|
||||
{
|
||||
startCalled = true;
|
||||
|
||||
// If start is called, that means activity is sampled,
|
||||
// and TraceFlag is set to Recorded.
|
||||
Assert.Equal(decision == SamplingDecision.RecordOnly || decision == SamplingDecision.RecordAndSample, a.IsAllDataRequested);
|
||||
Assert.Equal(decision == SamplingDecision.RecordAndSample ? ActivityTraceFlags.Recorded : ActivityTraceFlags.None, a.ActivityTraceFlags);
|
||||
Assert.Equal(decision == SamplingDecision.RecordAndSample, a.Recorded);
|
||||
};
|
||||
|
||||
this.testProcessor.EndAction =
|
||||
(a) =>
|
||||
{
|
||||
endCalled = true;
|
||||
};
|
||||
|
||||
var activity = new Activity("test");
|
||||
activity.Start();
|
||||
this.activitySourceAdapter.Start(activity, ActivityKind.Producer, new ActivitySource("test", "1.0.0"));
|
||||
activity.Stop();
|
||||
this.activitySourceAdapter.Stop(activity);
|
||||
|
||||
Assert.Equal(ActivityKind.Producer, activity.Kind);
|
||||
Assert.Equal(activity.IsAllDataRequested, startCalled);
|
||||
Assert.Equal(activity.IsAllDataRequested, endCalled);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public void ActivitySourceAdapterCallsStartStopActivityProcessor2(bool isSampled)
|
||||
{
|
||||
this.testSampler.SamplingAction = (samplingParameters) =>
|
||||
{
|
||||
return new SamplingResult(isSampled);
|
||||
};
|
||||
|
||||
bool startCalled = false;
|
||||
bool endCalled = false;
|
||||
this.testProcessor.StartAction =
|
||||
(a) =>
|
||||
{
|
||||
startCalled = true;
|
||||
|
||||
// If start is called, that means activity is sampled,
|
||||
// and TraceFlag is set to Recorded.
|
||||
Assert.Equal(isSampled, a.IsAllDataRequested);
|
||||
Assert.Equal(isSampled ? ActivityTraceFlags.Recorded : ActivityTraceFlags.None, a.ActivityTraceFlags);
|
||||
Assert.Equal(isSampled, a.Recorded);
|
||||
};
|
||||
|
||||
this.testProcessor.EndAction =
|
||||
(a) =>
|
||||
{
|
||||
endCalled = true;
|
||||
};
|
||||
|
||||
var activity = new Activity("test");
|
||||
activity.Start();
|
||||
this.activitySourceAdapter.Start(activity, ActivityKind.Internal, new ActivitySource("test", "1.0.0"));
|
||||
activity.Stop();
|
||||
this.activitySourceAdapter.Stop(activity);
|
||||
|
||||
Assert.Equal(isSampled, startCalled);
|
||||
Assert.Equal(isSampled, endCalled);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(SamplingDecision.Drop)]
|
||||
[InlineData(SamplingDecision.RecordOnly)]
|
||||
[InlineData(SamplingDecision.RecordAndSample)]
|
||||
public void ActivitySourceAdapterPopulatesSamplingAttributesToActivity(SamplingDecision sampling)
|
||||
{
|
||||
this.testSampler.SamplingAction = (samplingParams) =>
|
||||
{
|
||||
var attributes = new Dictionary<string, object>();
|
||||
attributes.Add("tagkeybysampler", "tagvalueaddedbysampler");
|
||||
return new SamplingResult(sampling, attributes);
|
||||
};
|
||||
|
||||
var activity = new Activity("test");
|
||||
activity.Start();
|
||||
this.activitySourceAdapter.Start(activity, ActivityKind.Internal, new ActivitySource("test", "1.0.0"));
|
||||
if (sampling != SamplingDecision.Drop)
|
||||
{
|
||||
Assert.Contains(new KeyValuePair<string, object>("tagkeybysampler", "tagvalueaddedbysampler"), activity.TagObjects);
|
||||
}
|
||||
|
||||
activity.Stop();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ActivitySourceAdapterPopulatesSamplingParamsCorrectlyForRootActivity()
|
||||
{
|
||||
this.testSampler.SamplingAction = (samplingParameters) =>
|
||||
{
|
||||
Assert.Equal(default, samplingParameters.ParentContext);
|
||||
return new SamplingResult(SamplingDecision.RecordAndSample);
|
||||
};
|
||||
|
||||
// Start activity without setting parent. i.e it'll have null parent
|
||||
// and becomes root activity
|
||||
var activity = new Activity("test");
|
||||
activity.Start();
|
||||
this.activitySourceAdapter.Start(activity, ActivityKind.Internal, new ActivitySource("test", "1.0.0"));
|
||||
activity.Stop();
|
||||
this.activitySourceAdapter.Stop(activity);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(ActivityTraceFlags.None)]
|
||||
[InlineData(ActivityTraceFlags.Recorded)]
|
||||
public void ActivitySourceAdapterPopulatesSamplingParamsCorrectlyForActivityWithRemoteParent(ActivityTraceFlags traceFlags)
|
||||
{
|
||||
var parentTraceId = ActivityTraceId.CreateRandom();
|
||||
var parentSpanId = ActivitySpanId.CreateRandom();
|
||||
var parentTraceFlag = (traceFlags == ActivityTraceFlags.Recorded) ? "01" : "00";
|
||||
string remoteParentId = $"00-{parentTraceId}-{parentSpanId}-{parentTraceFlag}";
|
||||
string tracestate = "a=b;c=d";
|
||||
|
||||
this.testSampler.SamplingAction = (samplingParameters) =>
|
||||
{
|
||||
Assert.Equal(parentTraceId, samplingParameters.ParentContext.TraceId);
|
||||
Assert.Equal(parentSpanId, samplingParameters.ParentContext.SpanId);
|
||||
Assert.Equal(traceFlags, samplingParameters.ParentContext.TraceFlags);
|
||||
Assert.Equal(tracestate, samplingParameters.ParentContext.TraceState);
|
||||
return new SamplingResult(SamplingDecision.RecordAndSample);
|
||||
};
|
||||
|
||||
// Create an activity with remote parent id.
|
||||
// The sampling parameters are expected to be that of the
|
||||
// parent context i.e the remote parent.
|
||||
var activity = new Activity("test").SetParentId(remoteParentId);
|
||||
activity.TraceStateString = tracestate;
|
||||
activity.Start();
|
||||
this.activitySourceAdapter.Start(activity, ActivityKind.Internal, new ActivitySource("test", "1.0.0"));
|
||||
activity.Stop();
|
||||
this.activitySourceAdapter.Stop(activity);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(ActivityTraceFlags.None)]
|
||||
[InlineData(ActivityTraceFlags.Recorded)]
|
||||
public void ActivitySourceAdapterPopulatesSamplingParamsCorrectlyForActivityWithInProcParent(ActivityTraceFlags traceFlags)
|
||||
{
|
||||
// Create some parent activity.
|
||||
string tracestate = "a=b;c=d";
|
||||
var activityLocalParent = new Activity("testParent");
|
||||
activityLocalParent.ActivityTraceFlags = traceFlags;
|
||||
activityLocalParent.TraceStateString = tracestate;
|
||||
activityLocalParent.Start();
|
||||
|
||||
this.testSampler.SamplingAction = (samplingParameters) =>
|
||||
{
|
||||
Assert.Equal(activityLocalParent.TraceId, samplingParameters.ParentContext.TraceId);
|
||||
Assert.Equal(activityLocalParent.SpanId, samplingParameters.ParentContext.SpanId);
|
||||
Assert.Equal(activityLocalParent.ActivityTraceFlags, samplingParameters.ParentContext.TraceFlags);
|
||||
Assert.Equal(tracestate, samplingParameters.ParentContext.TraceState);
|
||||
Assert.Equal(ActivityKind.Client, samplingParameters.Kind);
|
||||
return new SamplingResult(SamplingDecision.RecordAndSample);
|
||||
};
|
||||
|
||||
// This activity will have a inproc parent.
|
||||
// activity.Parent will be equal to the activity created at the beginning of this test.
|
||||
// Sampling parameters are expected to be that of the parentContext.
|
||||
// i.e of the parent Activity
|
||||
var activity = new Activity("test");
|
||||
activity.Start();
|
||||
this.activitySourceAdapter.Start(activity, ActivityKind.Client, new ActivitySource("test", "1.0.0"));
|
||||
activity.Stop();
|
||||
this.activitySourceAdapter.Stop(activity);
|
||||
|
||||
activityLocalParent.Stop();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Activity.Current = null;
|
||||
this.testProcessor.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
// <copyright file="TracerProviderBuilderExtensionsTest.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.Trace;
|
||||
using Xunit;
|
||||
|
||||
namespace OpenTelemetry.Trace.Tests
|
||||
{
|
||||
public class TracerProviderBuilderExtensionsTest
|
||||
{
|
||||
[Fact]
|
||||
public void AddLegacyOperationName_NullBuilder_Noop()
|
||||
{
|
||||
TracerProviderBuilder builder = null;
|
||||
|
||||
// No exception is thrown on executing this line
|
||||
builder.AddLegacyActivity("TestOperationName");
|
||||
using var provider = builder.Build();
|
||||
|
||||
var emptyActivitySource = new ActivitySource(string.Empty);
|
||||
Assert.False(emptyActivitySource.HasListeners()); // Check if AddLegacyOperationName was noop after TracerProviderBuilder.Build
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
[InlineData(" ")]
|
||||
public void AddLegacyOperationName_BadArgs(string operationName)
|
||||
{
|
||||
var builder = Sdk.CreateTracerProviderBuilder();
|
||||
Assert.Throws<ArgumentException>(() => builder.AddLegacyActivity(operationName));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddLegacyOperationNameAddsActivityListenerForEmptyActivitySource()
|
||||
{
|
||||
var emptyActivitySource = new ActivitySource(string.Empty);
|
||||
var builder = Sdk.CreateTracerProviderBuilder();
|
||||
builder.AddLegacyActivity("TestOperationName");
|
||||
|
||||
Assert.False(emptyActivitySource.HasListeners());
|
||||
using var provider = builder.Build();
|
||||
Assert.True(emptyActivitySource.HasListeners());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using OpenTelemetry.Instrumentation;
|
||||
using OpenTelemetry.Resources;
|
||||
using OpenTelemetry.Tests;
|
||||
using Xunit;
|
||||
|
|
@ -28,6 +29,11 @@ namespace OpenTelemetry.Trace.Tests
|
|||
{
|
||||
private const string ActivitySourceName = "TraceSdkTest";
|
||||
|
||||
public TracerProviderSdkTest()
|
||||
{
|
||||
Activity.DefaultIdFormat = ActivityIdFormat.W3C;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TracerProviderSdkInvokesSamplingWithCorrectParameters()
|
||||
{
|
||||
|
|
@ -252,8 +258,10 @@ namespace OpenTelemetry.Trace.Tests
|
|||
Assert.False(endCalled);
|
||||
}
|
||||
|
||||
// Test to check that TracerProvider does not call Processor.OnStart or Processor.OnEnd for a legacy activity when no legacy OperationName is
|
||||
// provided to TracerProviderBuilder.
|
||||
[Fact]
|
||||
public void TracerProvideSdkCreatesActivitySource()
|
||||
public void SdkDoesNotProcessLegacyActivityWithNoAdditionalConfig()
|
||||
{
|
||||
using TestActivityProcessor testActivityProcessor = new TestActivityProcessor();
|
||||
|
||||
|
|
@ -263,38 +271,301 @@ namespace OpenTelemetry.Trace.Tests
|
|||
testActivityProcessor.StartAction =
|
||||
(a) =>
|
||||
{
|
||||
Assert.False(Sdk.SuppressInstrumentation);
|
||||
Assert.True(a.IsAllDataRequested); // If Proccessor.OnStart is called, activity's IsAllDataRequested is set to true
|
||||
startCalled = true;
|
||||
};
|
||||
|
||||
testActivityProcessor.EndAction =
|
||||
(a) =>
|
||||
{
|
||||
Assert.False(Sdk.SuppressInstrumentation);
|
||||
Assert.True(a.IsAllDataRequested); // If Processor.OnEnd is called, activity's IsAllDataRequested is set to true
|
||||
endCalled = true;
|
||||
};
|
||||
|
||||
TestInstrumentation testInstrumentation = null;
|
||||
var emptyActivitySource = new ActivitySource(string.Empty);
|
||||
Assert.False(emptyActivitySource.HasListeners()); // No ActivityListener for empty ActivitySource added yet
|
||||
|
||||
// No AddLegacyOperationName chained to TracerProviderBuilder
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.AddProcessor(testActivityProcessor)
|
||||
.AddDiagnosticSourceInstrumentation((adapter) =>
|
||||
{
|
||||
testInstrumentation = new TestInstrumentation(adapter);
|
||||
return testInstrumentation;
|
||||
})
|
||||
.Build();
|
||||
|
||||
var adapter = testInstrumentation.Adapter;
|
||||
Activity activity = new Activity("test");
|
||||
Assert.False(emptyActivitySource.HasListeners()); // No listener for empty ActivitySource even after build
|
||||
|
||||
Activity activity = new Activity("Test");
|
||||
activity.Start();
|
||||
activity.Stop();
|
||||
|
||||
Assert.False(startCalled); // Processor.OnStart is not called since we did not add any legacy OperationName
|
||||
Assert.False(endCalled); // Processor.OnEnd is not called since we did not add any legacy OperationName
|
||||
}
|
||||
|
||||
// Test to check that TracerProvider samples a legacy activity using a custom Sampler and calls Processor.OnStart and Processor.OnEnd for the
|
||||
// legacy activity when the correct legacy OperationName is provided to TracerProviderBuilder.
|
||||
[Fact]
|
||||
public void SdkSamplesAndProcessesLegacyActivityWithRightConfig()
|
||||
{
|
||||
bool samplerCalled = false;
|
||||
|
||||
var sampler = new TestSampler
|
||||
{
|
||||
SamplingAction =
|
||||
(samplingParameters) =>
|
||||
{
|
||||
samplerCalled = true;
|
||||
return new SamplingResult(SamplingDecision.RecordAndSample);
|
||||
},
|
||||
};
|
||||
|
||||
using TestActivityProcessor testActivityProcessor = new TestActivityProcessor();
|
||||
|
||||
bool startCalled = false;
|
||||
bool endCalled = false;
|
||||
|
||||
testActivityProcessor.StartAction =
|
||||
(a) =>
|
||||
{
|
||||
Assert.True(samplerCalled);
|
||||
Assert.False(Sdk.SuppressInstrumentation);
|
||||
Assert.True(a.IsAllDataRequested); // If Proccessor.OnStart is called, activity's IsAllDataRequested is set to true
|
||||
startCalled = true;
|
||||
};
|
||||
|
||||
testActivityProcessor.EndAction =
|
||||
(a) =>
|
||||
{
|
||||
Assert.False(Sdk.SuppressInstrumentation);
|
||||
Assert.True(a.IsAllDataRequested); // If Processor.OnEnd is called, activity's IsAllDataRequested is set to true
|
||||
endCalled = true;
|
||||
};
|
||||
|
||||
var emptyActivitySource = new ActivitySource(string.Empty);
|
||||
Assert.False(emptyActivitySource.HasListeners()); // No ActivityListener for empty ActivitySource added yet
|
||||
|
||||
var operationNameForLegacyActivity = "TestOperationName";
|
||||
|
||||
// AddLegacyOperationName chained to TracerProviderBuilder
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.SetSampler(sampler)
|
||||
.AddProcessor(testActivityProcessor)
|
||||
.AddLegacyActivity(operationNameForLegacyActivity)
|
||||
.Build();
|
||||
|
||||
Assert.True(emptyActivitySource.HasListeners()); // Listener for empty ActivitySource added after TracerProvider build
|
||||
|
||||
Activity activity = new Activity(operationNameForLegacyActivity);
|
||||
activity.Start();
|
||||
activity.Stop();
|
||||
|
||||
Assert.True(startCalled); // Processor.OnStart is called since we added a legacy OperationName
|
||||
Assert.True(endCalled); // Processor.OnEnd is called since we added a legacy OperationName
|
||||
}
|
||||
|
||||
// Test to check that TracerProvider samples a legacy activity using a custom Sampler and calls Processor.OnStart and Processor.OnEnd for the
|
||||
// legacy activity when the correct legacy OperationName is provided to TracerProviderBuilder and a wildcard Source is added
|
||||
[Fact]
|
||||
public void SdkSamplesAndProcessesLegacyActivityWithRightConfigOnWildCardMode()
|
||||
{
|
||||
bool samplerCalled = false;
|
||||
|
||||
var sampler = new TestSampler
|
||||
{
|
||||
SamplingAction =
|
||||
(samplingParameters) =>
|
||||
{
|
||||
samplerCalled = true;
|
||||
return new SamplingResult(SamplingDecision.RecordAndSample);
|
||||
},
|
||||
};
|
||||
|
||||
using TestActivityProcessor testActivityProcessor = new TestActivityProcessor();
|
||||
|
||||
bool startCalled = false;
|
||||
bool endCalled = false;
|
||||
|
||||
testActivityProcessor.StartAction =
|
||||
(a) =>
|
||||
{
|
||||
Assert.True(samplerCalled);
|
||||
Assert.False(Sdk.SuppressInstrumentation);
|
||||
Assert.True(a.IsAllDataRequested); // If Proccessor.OnStart is called, activity's IsAllDataRequested is set to true
|
||||
startCalled = true;
|
||||
};
|
||||
|
||||
testActivityProcessor.EndAction =
|
||||
(a) =>
|
||||
{
|
||||
Assert.False(Sdk.SuppressInstrumentation);
|
||||
Assert.True(a.IsAllDataRequested); // If Processor.OnEnd is called, activity's IsAllDataRequested is set to true
|
||||
endCalled = true;
|
||||
};
|
||||
|
||||
var emptyActivitySource = new ActivitySource(string.Empty);
|
||||
Assert.False(emptyActivitySource.HasListeners()); // No ActivityListener for empty ActivitySource added yet
|
||||
|
||||
var operationNameForLegacyActivity = "TestOperationName";
|
||||
|
||||
// AddLegacyOperationName chained to TracerProviderBuilder
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.SetSampler(sampler)
|
||||
.AddSource("ABCCompany.XYZProduct.*") // Adding a wild card source
|
||||
.AddProcessor(testActivityProcessor)
|
||||
.AddLegacyActivity(operationNameForLegacyActivity)
|
||||
.Build();
|
||||
|
||||
Assert.True(emptyActivitySource.HasListeners()); // Listener for empty ActivitySource added after TracerProvider build
|
||||
|
||||
Activity activity = new Activity(operationNameForLegacyActivity);
|
||||
activity.Start();
|
||||
activity.Stop();
|
||||
|
||||
Assert.True(startCalled); // Processor.OnStart is called since we added a legacy OperationName
|
||||
Assert.True(endCalled); // Processor.OnEnd is called since we added a legacy OperationName
|
||||
}
|
||||
|
||||
// Test to check that TracerProvider does not call Processor.OnEnd for a legacy activity whose ActivitySource got updated before Activity.Stop and
|
||||
// the updated source was not added to the Provider
|
||||
[Fact]
|
||||
public void SdkCallsOnlyProcessorOnStartForLegacyActivityWhenActivitySourceIsUpdatedWithoutAddSource()
|
||||
{
|
||||
using TestActivityProcessor testActivityProcessor = new TestActivityProcessor();
|
||||
|
||||
bool startCalled = false;
|
||||
bool endCalled = false;
|
||||
|
||||
testActivityProcessor.StartAction =
|
||||
(a) =>
|
||||
{
|
||||
Assert.False(Sdk.SuppressInstrumentation);
|
||||
Assert.True(a.IsAllDataRequested); // If Proccessor.OnStart is called, activity's IsAllDataRequested is set to true
|
||||
startCalled = true;
|
||||
};
|
||||
|
||||
testActivityProcessor.EndAction =
|
||||
(a) =>
|
||||
{
|
||||
Assert.False(Sdk.SuppressInstrumentation);
|
||||
Assert.True(a.IsAllDataRequested); // If Processor.OnEnd is called, activity's IsAllDataRequested is set to true
|
||||
endCalled = true;
|
||||
};
|
||||
|
||||
var emptyActivitySource = new ActivitySource(string.Empty);
|
||||
Assert.False(emptyActivitySource.HasListeners()); // No ActivityListener for empty ActivitySource added yet
|
||||
|
||||
var operationNameForLegacyActivity = "TestOperationName";
|
||||
var activitySourceForLegacyActvity = new ActivitySource("TestActivitySource", "1.0.0");
|
||||
|
||||
// AddLegacyOperationName chained to TracerProviderBuilder
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.AddLegacyActivity(operationNameForLegacyActivity)
|
||||
.AddProcessor(testActivityProcessor)
|
||||
.Build();
|
||||
|
||||
Assert.True(emptyActivitySource.HasListeners()); // Listener for empty ActivitySource added after TracerProvider build
|
||||
|
||||
Activity activity = new Activity(operationNameForLegacyActivity);
|
||||
activity.Start();
|
||||
ActivityInstrumentationHelper.SetActivitySourceProperty(activity, activitySourceForLegacyActvity);
|
||||
activity.Stop();
|
||||
|
||||
Assert.True(startCalled); // Processor.OnStart is called since we provided the legacy OperationName
|
||||
Assert.False(endCalled); // Processor.OnEnd is not called since the ActivitySource is updated and the updated source name is not added as a Source to the provider
|
||||
}
|
||||
|
||||
// Test to check that TracerProvider calls Processor.OnStart and Processor.OnEnd for a legacy activity whose ActivitySource got updated before Activity.Stop and
|
||||
// the updated source was added to the Provider
|
||||
[Fact]
|
||||
public void SdkProcessesLegacyActivityWhenActivitySourceIsUpdatedWithAddSource()
|
||||
{
|
||||
using TestActivityProcessor testActivityProcessor = new TestActivityProcessor();
|
||||
|
||||
bool startCalled = false;
|
||||
bool endCalled = false;
|
||||
|
||||
testActivityProcessor.StartAction =
|
||||
(a) =>
|
||||
{
|
||||
Assert.False(Sdk.SuppressInstrumentation);
|
||||
Assert.True(a.IsAllDataRequested); // If Proccessor.OnStart is called, activity's IsAllDataRequested is set to true
|
||||
startCalled = true;
|
||||
};
|
||||
|
||||
testActivityProcessor.EndAction =
|
||||
(a) =>
|
||||
{
|
||||
Assert.False(Sdk.SuppressInstrumentation);
|
||||
Assert.True(a.IsAllDataRequested); // If Processor.OnEnd is called, activity's IsAllDataRequested is set to true
|
||||
endCalled = true;
|
||||
};
|
||||
|
||||
var emptyActivitySource = new ActivitySource(string.Empty);
|
||||
Assert.False(emptyActivitySource.HasListeners()); // No ActivityListener for empty ActivitySource added yet
|
||||
|
||||
var operationNameForLegacyActivity = "TestOperationName";
|
||||
var activitySourceForLegacyActvity = new ActivitySource("TestActivitySource", "1.0.0");
|
||||
|
||||
// AddLegacyOperationName chained to TracerProviderBuilder
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.AddSource(activitySourceForLegacyActvity.Name) // Add the updated ActivitySource as a Source
|
||||
.AddLegacyActivity(operationNameForLegacyActivity)
|
||||
.AddProcessor(testActivityProcessor)
|
||||
.Build();
|
||||
|
||||
Assert.True(emptyActivitySource.HasListeners()); // Listener for empty ActivitySource added after TracerProvider build
|
||||
|
||||
Activity activity = new Activity(operationNameForLegacyActivity);
|
||||
activity.Start();
|
||||
ActivityInstrumentationHelper.SetActivitySourceProperty(activity, activitySourceForLegacyActvity);
|
||||
activity.Stop();
|
||||
|
||||
Assert.True(startCalled); // Processor.OnStart is called since we provided the legacy OperationName
|
||||
Assert.True(endCalled); // Processor.OnEnd is not called since the ActivitySource is updated and the updated source name is added as a Source to the provider
|
||||
}
|
||||
|
||||
// Test to check that TracerProvider continues to process legacy activities even after a new Processor is added after the building the provider.
|
||||
[Fact]
|
||||
public void SdkProcessesLegacyActivityEvenAfterAddingNewProcessor()
|
||||
{
|
||||
using TestActivityProcessor testActivityProcessor = new TestActivityProcessor();
|
||||
|
||||
bool startCalled = false;
|
||||
bool endCalled = false;
|
||||
|
||||
testActivityProcessor.StartAction =
|
||||
(a) =>
|
||||
{
|
||||
Assert.False(Sdk.SuppressInstrumentation);
|
||||
Assert.True(a.IsAllDataRequested); // If Proccessor.OnStart is called, activity's IsAllDataRequested is set to true
|
||||
startCalled = true;
|
||||
};
|
||||
|
||||
testActivityProcessor.EndAction =
|
||||
(a) =>
|
||||
{
|
||||
Assert.False(Sdk.SuppressInstrumentation);
|
||||
Assert.True(a.IsAllDataRequested); // If Processor.OnEnd is called, activity's IsAllDataRequested is set to true
|
||||
endCalled = true;
|
||||
};
|
||||
|
||||
var operationNameForLegacyActivity = "TestOperationName";
|
||||
|
||||
// AddLegacyOperationName chained to TracerProviderBuilder
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.AddProcessor(testActivityProcessor)
|
||||
.AddLegacyActivity(operationNameForLegacyActivity)
|
||||
.Build();
|
||||
|
||||
Activity activity = new Activity(operationNameForLegacyActivity);
|
||||
activity.Start();
|
||||
adapter.Start(activity, ActivityKind.Internal, new ActivitySource("test", "1.0.0"));
|
||||
adapter.Stop(activity);
|
||||
activity.Stop();
|
||||
|
||||
Assert.True(startCalled);
|
||||
Assert.True(endCalled);
|
||||
|
||||
// As Processors can be added anytime after Provider construction,
|
||||
// the following validates that updated processors are reflected
|
||||
// in ActivitySourceAdapter.
|
||||
// As Processors can be added anytime after Provider construction, the following validates
|
||||
// the following validates that updated processors are processing the legacy activities created from here on.
|
||||
TestActivityProcessor testActivityProcessorNew = new TestActivityProcessor();
|
||||
|
||||
bool startCalledNew = false;
|
||||
|
|
@ -303,20 +574,23 @@ namespace OpenTelemetry.Trace.Tests
|
|||
testActivityProcessorNew.StartAction =
|
||||
(a) =>
|
||||
{
|
||||
Assert.False(Sdk.SuppressInstrumentation);
|
||||
Assert.True(a.IsAllDataRequested); // If Proccessor.OnStart is called, activity's IsAllDataRequested is set to true
|
||||
startCalledNew = true;
|
||||
};
|
||||
|
||||
testActivityProcessorNew.EndAction =
|
||||
(a) =>
|
||||
{
|
||||
Assert.False(Sdk.SuppressInstrumentation);
|
||||
Assert.True(a.IsAllDataRequested); // If Processor.OnEnd is called, activity's IsAllDataRequested is set to true
|
||||
endCalledNew = true;
|
||||
};
|
||||
|
||||
tracerProvider.AddProcessor(testActivityProcessorNew);
|
||||
Activity activityNew = new Activity("test");
|
||||
|
||||
Activity activityNew = new Activity(operationNameForLegacyActivity); // Create a new Activity with the same operation name
|
||||
activityNew.Start();
|
||||
adapter.Start(activityNew, ActivityKind.Internal, new ActivitySource("test", "1.0.0"));
|
||||
adapter.Stop(activityNew);
|
||||
activityNew.Stop();
|
||||
|
||||
Assert.True(startCalledNew);
|
||||
|
|
@ -324,28 +598,164 @@ namespace OpenTelemetry.Trace.Tests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void TracerProvideSdkCreatesActivitySourceWhenNoProcessor()
|
||||
public void SdkSamplesLegacyActivityWithAlwaysOnSampler()
|
||||
{
|
||||
TestInstrumentation testInstrumentation = null;
|
||||
var operationNameForLegacyActivity = "TestOperationName";
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.AddDiagnosticSourceInstrumentation((adapter) =>
|
||||
{
|
||||
testInstrumentation = new TestInstrumentation(adapter);
|
||||
return testInstrumentation;
|
||||
})
|
||||
.SetSampler(new AlwaysOnSampler())
|
||||
.AddLegacyActivity(operationNameForLegacyActivity)
|
||||
.Build();
|
||||
|
||||
var adapter = testInstrumentation.Adapter;
|
||||
Activity activity = new Activity("test");
|
||||
Activity activity = new Activity(operationNameForLegacyActivity);
|
||||
activity.Start();
|
||||
adapter.Start(activity, ActivityKind.Internal, new ActivitySource("test", "1.0.0"));
|
||||
adapter.Stop(activity);
|
||||
activity.Stop();
|
||||
|
||||
// No asserts here. Validates that no exception
|
||||
// gets thrown when processors are not added,
|
||||
// TODO: Refactor to have more proper unit test
|
||||
// to target each individual classes.
|
||||
Assert.True(activity.IsAllDataRequested);
|
||||
Assert.True(activity.ActivityTraceFlags.HasFlag(ActivityTraceFlags.Recorded));
|
||||
|
||||
activity.Stop();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SdkSamplesLegacyActivityWithAlwaysOffSampler()
|
||||
{
|
||||
var operationNameForLegacyActivity = "TestOperationName";
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.SetSampler(new AlwaysOffSampler())
|
||||
.AddLegacyActivity(operationNameForLegacyActivity)
|
||||
.Build();
|
||||
|
||||
Activity activity = new Activity(operationNameForLegacyActivity);
|
||||
activity.Start();
|
||||
|
||||
Assert.False(activity.IsAllDataRequested);
|
||||
Assert.False(activity.ActivityTraceFlags.HasFlag(ActivityTraceFlags.Recorded));
|
||||
|
||||
activity.Stop();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(SamplingDecision.Drop, false, false)]
|
||||
[InlineData(SamplingDecision.RecordOnly, true, false)]
|
||||
[InlineData(SamplingDecision.RecordAndSample, true, true)]
|
||||
public void SdkSamplesLegacyActivityWithCustomSampler(SamplingDecision samplingDecision, bool isAllDataRequested, bool hasRecordedFlag)
|
||||
{
|
||||
var operationNameForLegacyActivity = "TestOperationName";
|
||||
var sampler = new TestSampler() { SamplingAction = (samplingParameters) => new SamplingResult(samplingDecision) };
|
||||
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.SetSampler(sampler)
|
||||
.AddLegacyActivity(operationNameForLegacyActivity)
|
||||
.Build();
|
||||
|
||||
Activity activity = new Activity(operationNameForLegacyActivity);
|
||||
activity.Start();
|
||||
|
||||
Assert.Equal(isAllDataRequested, activity.IsAllDataRequested);
|
||||
Assert.Equal(hasRecordedFlag, activity.ActivityTraceFlags.HasFlag(ActivityTraceFlags.Recorded));
|
||||
|
||||
activity.Stop();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SdkPopulatesSamplingParamsCorrectlyForRootLegacyActivity()
|
||||
{
|
||||
var operationNameForLegacyActivity = "TestOperationName";
|
||||
var sampler = new TestSampler()
|
||||
{
|
||||
SamplingAction = (samplingParameters) =>
|
||||
{
|
||||
Assert.Equal(default, samplingParameters.ParentContext);
|
||||
return new SamplingResult(SamplingDecision.RecordAndSample);
|
||||
},
|
||||
};
|
||||
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.SetSampler(sampler)
|
||||
.AddLegacyActivity(operationNameForLegacyActivity)
|
||||
.Build();
|
||||
|
||||
// Start activity without setting parent. i.e it'll have null parent
|
||||
// and becomes root activity
|
||||
Activity activity = new Activity(operationNameForLegacyActivity);
|
||||
activity.Start();
|
||||
activity.Stop();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(ActivityTraceFlags.None)]
|
||||
[InlineData(ActivityTraceFlags.Recorded)]
|
||||
public void SdkPopulatesSamplingParamsCorrectlyForLegacyActivityWithRemoteParent(ActivityTraceFlags traceFlags)
|
||||
{
|
||||
var parentTraceId = ActivityTraceId.CreateRandom();
|
||||
var parentSpanId = ActivitySpanId.CreateRandom();
|
||||
var parentTraceFlag = (traceFlags == ActivityTraceFlags.Recorded) ? "01" : "00";
|
||||
string remoteParentId = $"00-{parentTraceId}-{parentSpanId}-{parentTraceFlag}";
|
||||
string tracestate = "a=b;c=d";
|
||||
|
||||
var operationNameForLegacyActivity = "TestOperationName";
|
||||
var sampler = new TestSampler()
|
||||
{
|
||||
SamplingAction = (samplingParameters) =>
|
||||
{
|
||||
Assert.Equal(parentTraceId, samplingParameters.ParentContext.TraceId);
|
||||
Assert.Equal(parentSpanId, samplingParameters.ParentContext.SpanId);
|
||||
Assert.Equal(traceFlags, samplingParameters.ParentContext.TraceFlags);
|
||||
Assert.Equal(tracestate, samplingParameters.ParentContext.TraceState);
|
||||
return new SamplingResult(SamplingDecision.RecordAndSample);
|
||||
},
|
||||
};
|
||||
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.SetSampler(sampler)
|
||||
.AddLegacyActivity(operationNameForLegacyActivity)
|
||||
.Build();
|
||||
|
||||
// Create an activity with remote parent id.
|
||||
// The sampling parameters are expected to be that of the
|
||||
// parent context i.e the remote parent.
|
||||
Activity activity = new Activity(operationNameForLegacyActivity).SetParentId(remoteParentId);
|
||||
activity.TraceStateString = tracestate;
|
||||
activity.Start();
|
||||
activity.Stop();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(ActivityTraceFlags.None)]
|
||||
[InlineData(ActivityTraceFlags.Recorded)]
|
||||
public void SdkPopulatesSamplingParamsCorrectlyForLegacyActivityWithInProcParent(ActivityTraceFlags traceFlags)
|
||||
{
|
||||
// Create some parent activity.
|
||||
string tracestate = "a=b;c=d";
|
||||
var activityLocalParent = new Activity("TestParent");
|
||||
activityLocalParent.ActivityTraceFlags = traceFlags;
|
||||
activityLocalParent.TraceStateString = tracestate;
|
||||
activityLocalParent.Start();
|
||||
|
||||
var operationNameForLegacyActivity = "TestOperationName";
|
||||
var sampler = new TestSampler()
|
||||
{
|
||||
SamplingAction = (samplingParameters) =>
|
||||
{
|
||||
Assert.Equal(activityLocalParent.TraceId, samplingParameters.ParentContext.TraceId);
|
||||
Assert.Equal(activityLocalParent.SpanId, samplingParameters.ParentContext.SpanId);
|
||||
Assert.Equal(activityLocalParent.ActivityTraceFlags, samplingParameters.ParentContext.TraceFlags);
|
||||
Assert.Equal(tracestate, samplingParameters.ParentContext.TraceState);
|
||||
return new SamplingResult(SamplingDecision.RecordAndSample);
|
||||
},
|
||||
};
|
||||
|
||||
using var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.SetSampler(sampler)
|
||||
.AddLegacyActivity(operationNameForLegacyActivity)
|
||||
.Build();
|
||||
|
||||
// This activity will have a inproc parent.
|
||||
// activity.Parent will be equal to the activity created at the beginning of this test.
|
||||
// Sampling parameters are expected to be that of the parentContext.
|
||||
// i.e of the parent Activity
|
||||
Activity activity = new Activity(operationNameForLegacyActivity);
|
||||
activity.Start();
|
||||
activity.Stop();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -353,16 +763,14 @@ namespace OpenTelemetry.Trace.Tests
|
|||
{
|
||||
TestInstrumentation testInstrumentation = null;
|
||||
var tracerProvider = Sdk.CreateTracerProviderBuilder()
|
||||
.AddDiagnosticSourceInstrumentation((adapter) =>
|
||||
.AddInstrumentation(() =>
|
||||
{
|
||||
testInstrumentation = new TestInstrumentation(adapter);
|
||||
testInstrumentation = new TestInstrumentation();
|
||||
return testInstrumentation;
|
||||
})
|
||||
.Build();
|
||||
|
||||
Assert.NotNull(testInstrumentation);
|
||||
var adapter = testInstrumentation.Adapter;
|
||||
Assert.NotNull(adapter);
|
||||
Assert.False(testInstrumentation.IsDisposed);
|
||||
tracerProvider.Dispose();
|
||||
Assert.True(testInstrumentation.IsDisposed);
|
||||
|
|
@ -421,11 +829,9 @@ namespace OpenTelemetry.Trace.Tests
|
|||
private class TestInstrumentation : IDisposable
|
||||
{
|
||||
public bool IsDisposed;
|
||||
public ActivitySourceAdapter Adapter;
|
||||
|
||||
public TestInstrumentation(ActivitySourceAdapter adapter)
|
||||
public TestInstrumentation()
|
||||
{
|
||||
this.Adapter = adapter;
|
||||
this.IsDisposed = false;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue