diff --git a/samples/Exporters/AspNet/Global.asax.cs b/samples/Exporters/AspNet/Global.asax.cs
index 153203a0c..f5b9f6a26 100644
--- a/samples/Exporters/AspNet/Global.asax.cs
+++ b/samples/Exporters/AspNet/Global.asax.cs
@@ -15,22 +15,9 @@ namespace OpenTelemetry.Exporter.AspNet
protected void Application_Start()
{
- this.tracerFactory = TracerFactory.Create(builder =>
- {
- builder
- .UseJaeger(c =>
- {
- c.AgentHost = "localhost";
- c.AgentPort = 6831;
- })
- .AddRequestInstrumentation()
- .AddDependencyInstrumentation();
- });
-
- TracerFactoryBase.SetDefault(this.tracerFactory);
-
- this.openTelemetry = OpenTelemetrySdk.EnableOpenTelemetry(
+ this.openTelemetry = OpenTelemetrySdk.Default.EnableOpenTelemetry(
(builder) => builder.AddDependencyInstrumentation()
+ .AddRequestInstrumentation()
.UseJaegerActivityExporter(c =>
{
c.AgentHost = "localhost";
diff --git a/samples/Exporters/AspNet/OpenTelemetry.Exporter.AspNet.Win.csproj b/samples/Exporters/AspNet/OpenTelemetry.Exporter.AspNet.Win.csproj
index 7d9ffa6b6..d7c9680ad 100644
--- a/samples/Exporters/AspNet/OpenTelemetry.Exporter.AspNet.Win.csproj
+++ b/samples/Exporters/AspNet/OpenTelemetry.Exporter.AspNet.Win.csproj
@@ -78,6 +78,9 @@
+
+ 5.0.0-preview.4.20251.6
+
diff --git a/samples/Exporters/AspNet/Web.config b/samples/Exporters/AspNet/Web.config
index ba5ac1214..23e5c73f5 100644
--- a/samples/Exporters/AspNet/Web.config
+++ b/samples/Exporters/AspNet/Web.config
@@ -27,6 +27,10 @@
+
+
+
+
diff --git a/samples/Exporters/Console/TestConsoleActivity.cs b/samples/Exporters/Console/TestConsoleActivity.cs
index 3702abbd4..2c60a7f41 100644
--- a/samples/Exporters/Console/TestConsoleActivity.cs
+++ b/samples/Exporters/Console/TestConsoleActivity.cs
@@ -27,7 +27,7 @@ namespace Samples
{
// Enable OpenTelemetry for the source "MyCompany.MyProduct.MyWebServer"
// and use Console exporter
- OpenTelemetrySdk.EnableOpenTelemetry(
+ OpenTelemetrySdk.Default.EnableOpenTelemetry(
(builder) => builder.AddActivitySource("MyCompany.MyProduct.MyWebServer")
.UseConsoleActivityExporter(opt => opt.DisplayAsJson = options.DisplayAsJson));
diff --git a/samples/Exporters/Console/TestHttpClient.cs b/samples/Exporters/Console/TestHttpClient.cs
index 4b725788a..842b6b345 100644
--- a/samples/Exporters/Console/TestHttpClient.cs
+++ b/samples/Exporters/Console/TestHttpClient.cs
@@ -14,8 +14,9 @@
// limitations under the License.
//
using System;
+using System.Diagnostics;
using System.Net.Http;
-using OpenTelemetry.Trace;
+using OpenTelemetry.Exporter.Console;
using OpenTelemetry.Trace.Configuration;
namespace Samples
@@ -26,12 +27,13 @@ namespace Samples
{
Console.WriteLine("Hello World!");
- using var tracerFactory = TracerFactory.Create(builder => builder
- .UseZipkin(o => o.ServiceName = "http-client-test")
- .AddDependencyInstrumentation());
- var tracer = tracerFactory.GetTracer("http-client-test");
+ OpenTelemetrySdk.Default.EnableOpenTelemetry(
+ (builder) => builder.AddHttpClientDependencyInstrumentation()
+ .AddActivitySource("http-client-test")
+ .UseConsoleActivityExporter(opt => opt.DisplayAsJson = false));
- using (tracer.StartActiveSpan("incoming request", out _))
+ var source = new ActivitySource("http-client-test");
+ using (var parent = source.StartActivity("incoming request", ActivityKind.Server))
{
using var client = new HttpClient();
client.GetStringAsync("http://bing.com").GetAwaiter().GetResult();
diff --git a/samples/Exporters/Console/TestJaeger.cs b/samples/Exporters/Console/TestJaeger.cs
index 402e71867..8863e42fb 100644
--- a/samples/Exporters/Console/TestJaeger.cs
+++ b/samples/Exporters/Console/TestJaeger.cs
@@ -37,7 +37,7 @@ namespace Samples
{
// Enable OpenTelemetry for the sources "Samples.SampleServer" and "Samples.SampleClient"
// and use the Jaeger exporter.
- OpenTelemetrySdk.EnableOpenTelemetry(
+ OpenTelemetrySdk.Default.EnableOpenTelemetry(
builder => builder
.AddActivitySource("Samples.SampleServer")
.AddActivitySource("Samples.SampleClient")
diff --git a/samples/Exporters/Console/TestOtlp.cs b/samples/Exporters/Console/TestOtlp.cs
index e33d7ff74..e00d99573 100644
--- a/samples/Exporters/Console/TestOtlp.cs
+++ b/samples/Exporters/Console/TestOtlp.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -62,7 +62,7 @@ namespace Samples
{
// Enable OpenTelemetry for the sources "Samples.SampleServer" and "Samples.SampleClient"
// and use OTLP exporter.
- OpenTelemetrySdk.EnableOpenTelemetry(
+ OpenTelemetrySdk.Default.EnableOpenTelemetry(
builder => builder
.AddActivitySource("Samples.SampleServer")
.AddActivitySource("Samples.SampleClient")
diff --git a/samples/Exporters/Web/Controllers/WeatherForecastController.cs b/samples/Exporters/Web/Controllers/WeatherForecastController.cs
index 1b805c465..ade31cef2 100644
--- a/samples/Exporters/Web/Controllers/WeatherForecastController.cs
+++ b/samples/Exporters/Web/Controllers/WeatherForecastController.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Net;
+using System.Net.Http;
using Microsoft.AspNetCore.Mvc;
namespace API.Controllers
@@ -14,9 +16,12 @@ namespace API.Controllers
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
+ private static HttpClient httpClient = new HttpClient();
+
[HttpGet]
public IEnumerable Get()
{
+ var res = httpClient.GetStringAsync("http://google.com").Result;
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
diff --git a/samples/Exporters/Web/OpenTelemetry.Exporter.Web.csproj b/samples/Exporters/Web/OpenTelemetry.Exporter.Web.csproj
index ee116bb9a..334c74a82 100644
--- a/samples/Exporters/Web/OpenTelemetry.Exporter.Web.csproj
+++ b/samples/Exporters/Web/OpenTelemetry.Exporter.Web.csproj
@@ -12,9 +12,10 @@
+
+
-
diff --git a/samples/Exporters/Web/Startup.cs b/samples/Exporters/Web/Startup.cs
index a30a3b0ec..521756626 100644
--- a/samples/Exporters/Web/Startup.cs
+++ b/samples/Exporters/Web/Startup.cs
@@ -7,6 +7,7 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
+using OpenTelemetry.Exporter.Console;
using OpenTelemetry.Trace.Configuration;
namespace API
@@ -36,18 +37,15 @@ namespace API
}
});
- services.AddOpenTelemetry((sp, builder) =>
- {
- builder
- //.SetSampler(Samplers.AlwaysSample)
- .UseZipkin(options =>
- {
- options.ServiceName = "test-zipkin";
- options.Endpoint = new Uri(this.Configuration.GetValue("Zipkin:Endpoint"));
- })
- .AddRequestInstrumentation()
- .AddDependencyInstrumentation();
- });
+ OpenTelemetrySdk.Default.EnableOpenTelemetry(
+ (builder) => builder.AddRequestInstrumentation().AddDependencyInstrumentation()
+ .UseJaegerActivityExporter(o =>
+ {
+ o.ServiceName = this.Configuration.GetValue("Jaeger:ServiceName");
+ o.AgentHost = this.Configuration.GetValue("Jaeger:Host");
+ o.AgentPort = this.Configuration.GetValue("Jaeger:Port");
+ })
+ );
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
diff --git a/samples/Exporters/Web/appsettings.json b/samples/Exporters/Web/appsettings.json
index b13d8ed87..b68356765 100644
--- a/samples/Exporters/Web/appsettings.json
+++ b/samples/Exporters/Web/appsettings.json
@@ -7,7 +7,9 @@
}
},
"AllowedHosts": "*",
- "Zipkin": {
- "Endpoint": "http://localhost:9411/api/v2/spans"
+ "Jaeger": {
+ "ServiceName": "jaeger-test",
+ "Host": "localhost",
+ "Port": 6831
}
}
diff --git a/samples/LoggingTracer/LoggingTracer.Demo.AspNetCore/Startup.cs b/samples/LoggingTracer/LoggingTracer.Demo.AspNetCore/Startup.cs
index 15b88b0e5..c7ca8bf61 100644
--- a/samples/LoggingTracer/LoggingTracer.Demo.AspNetCore/Startup.cs
+++ b/samples/LoggingTracer/LoggingTracer.Demo.AspNetCore/Startup.cs
@@ -21,8 +21,8 @@ namespace LoggingTracer.Demo.AspNetCore
var tracerFactory = new LoggingTracerFactory();
var tracer = tracerFactory.GetTracer("ServerApp", "semver:1.0.0");
- var dependenciesInstrumentation = new DependenciesInstrumentation(tracerFactory);
- var aspNetCoreInstrumentation = new AspNetCoreInstrumentation(tracer);
+ var dependenciesInstrumentation = new HttpClientInstrumentation();
+ var aspNetCoreInstrumentation = new AspNetCoreInstrumentation();
return tracerFactory;
});
diff --git a/src/OpenTelemetry.Instrumentation.AspNet.Win/AspNetInstrumentation.cs b/src/OpenTelemetry.Instrumentation.AspNet.Win/AspNetInstrumentation.cs
index 906b9157d..e1544862d 100644
--- a/src/OpenTelemetry.Instrumentation.AspNet.Win/AspNetInstrumentation.cs
+++ b/src/OpenTelemetry.Instrumentation.AspNet.Win/AspNetInstrumentation.cs
@@ -20,7 +20,7 @@ using OpenTelemetry.Trace;
namespace OpenTelemetry.Instrumentation.AspNet
{
///
- /// Requests instrumentation.
+ /// Asp.Net Requests instrumentation.
///
public class AspNetInstrumentation : IDisposable
{
@@ -31,21 +31,19 @@ namespace OpenTelemetry.Instrumentation.AspNet
///
/// Initializes a new instance of the class.
///
- /// Tracer to record traced with.
- public AspNetInstrumentation(Tracer tracer)
- : this(tracer, new AspNetInstrumentationOptions())
+ public AspNetInstrumentation()
+ : this(new AspNetInstrumentationOptions())
{
}
///
/// Initializes a new instance of the class.
///
- /// Tracer to record traced with.
/// Configuration options for ASP.NET instrumentation.
- public AspNetInstrumentation(Tracer tracer, AspNetInstrumentationOptions options)
+ public AspNetInstrumentation(AspNetInstrumentationOptions options)
{
this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(
- name => new HttpInListener(name, tracer, options),
+ name => new HttpInListener(name, options),
listener => listener.Name == AspNetDiagnosticListenerName,
null);
this.diagnosticSourceSubscriber.Subscribe();
diff --git a/src/OpenTelemetry.Instrumentation.AspNet.Win/Implementation/HttpInListener.cs b/src/OpenTelemetry.Instrumentation.AspNet.Win/Implementation/HttpInListener.cs
index 2c86ac5da..3d0d1e0f0 100644
--- a/src/OpenTelemetry.Instrumentation.AspNet.Win/Implementation/HttpInListener.cs
+++ b/src/OpenTelemetry.Instrumentation.AspNet.Win/Implementation/HttpInListener.cs
@@ -20,17 +20,22 @@ using System.Web;
using System.Web.Routing;
using OpenTelemetry.Context.Propagation;
using OpenTelemetry.Trace;
+using OpenTelemetry.Trace.Samplers;
namespace OpenTelemetry.Instrumentation.AspNet.Implementation
{
internal class HttpInListener : ListenerHandler
{
+ // hard-coded Sampler here, just to prototype.
+ // Either .NET will provide an new API to avoid Instrumentation being aware of sampling.
+ // or we'll move the Sampler to come from OpenTelemetryBuilder, and not hardcoded.
+ private readonly ActivitySampler sampler = new AlwaysOnActivitySampler();
private readonly PropertyFetcher routeFetcher = new PropertyFetcher("Route");
private readonly PropertyFetcher routeTemplateFetcher = new PropertyFetcher("RouteTemplate");
private readonly AspNetInstrumentationOptions options;
- public HttpInListener(string name, Tracer tracer, AspNetInstrumentationOptions options)
- : base(name, tracer)
+ public HttpInListener(string name, AspNetInstrumentationOptions options)
+ : base(name, null)
{
this.options = options ?? throw new ArgumentNullException(nameof(options));
}
@@ -48,53 +53,75 @@ namespace OpenTelemetry.Instrumentation.AspNet.Implementation
if (this.options.RequestFilter != null && !this.options.RequestFilter(context))
{
+ // TODO: These filters won't prevent the activity from being tracked
+ // as they are fired anyway.
InstrumentationEventSource.Log.RequestIsFilteredOut(activity.OperationName);
return;
}
+ // TODO: Avoid the reflection hack once .NET ships new Activity with Kind settable.
+ activity.GetType().GetProperty("Kind").SetValue(activity, ActivityKind.Server);
+
var request = context.Request;
var requestValues = request.Unvalidated;
// see the spec https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-semantic-conventions.md
var path = requestValues.Path;
+ activity.DisplayName = path;
- TelemetrySpan span;
- if (this.options.TextFormat is TraceContextFormat)
+ var samplingParameters = new ActivitySamplingParameters(
+ activity.Context,
+ activity.TraceId,
+ activity.DisplayName,
+ activity.Kind,
+ activity.Tags,
+ activity.Links);
+
+ // TODO: Find a way to avoid Instrumentation being tied to Sampler
+ var samplingDecision = this.sampler.ShouldSample(samplingParameters);
+ activity.IsAllDataRequested = samplingDecision.IsSampled;
+ if (samplingDecision.IsSampled)
{
- this.Tracer.StartActiveSpanFromActivity(path, Activity.Current, SpanKind.Server, out span);
+ activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
}
- else
+
+ if (!(this.options.TextFormat is TraceContextFormat))
{
+ // This requires to ignore the current activity and create a new one
+ // using the context extracted using the format TextFormat supports.
+ // TODO: actually implement code doing the above.
+ /*
var ctx = this.options.TextFormat.Extract(
request,
(r, name) => requestValues.Headers.GetValues(name));
this.Tracer.StartActiveSpan(path, ctx, SpanKind.Server, out span);
+ */
}
- if (span.IsRecording)
+ if (activity.IsAllDataRequested)
{
- span.PutHttpHostAttribute(request.Url.Host, request.Url.Port);
- span.PutHttpMethodAttribute(request.HttpMethod);
- span.PutHttpPathAttribute(path);
+ if (request.Url.Port == 80 || request.Url.Port == 443)
+ {
+ activity.AddTag(SpanAttributeConstants.HttpHostKey, request.Url.Host);
+ }
+ else
+ {
+ activity.AddTag(SpanAttributeConstants.HttpHostKey, request.Url.Host + ":" + request.Url.Port);
+ }
- span.PutHttpUserAgentAttribute(request.UserAgent);
- span.PutHttpRawUrlAttribute(request.Url.ToString());
+ activity.AddTag(SpanAttributeConstants.HttpMethodKey, request.HttpMethod);
+ activity.AddTag(SpanAttributeConstants.HttpPathKey, path);
+ activity.AddTag(SpanAttributeConstants.HttpUserAgentKey, request.UserAgent);
+ activity.AddTag(SpanAttributeConstants.HttpUrlKey, request.Url.ToString());
}
}
public override void OnStopActivity(Activity activity, object payload)
{
const string EventNameSuffix = ".OnStopActivity";
- var span = this.Tracer.CurrentSpan;
- if (span == null || !span.Context.IsValid)
- {
- InstrumentationEventSource.Log.NullOrBlankSpan(nameof(HttpInListener) + EventNameSuffix);
- return;
- }
-
- if (span.IsRecording)
+ if (activity.IsAllDataRequested)
{
var context = HttpContext.Current;
if (context == null)
@@ -104,8 +131,10 @@ namespace OpenTelemetry.Instrumentation.AspNet.Implementation
}
var response = context.Response;
-
- span.PutHttpStatusCode(response.StatusCode, response.StatusDescription);
+ activity.AddTag(SpanAttributeConstants.HttpStatusCodeKey, response.StatusCode.ToString());
+ Status status = SpanHelper.ResolveSpanStatusForHttpStatusCode((int)response.StatusCode);
+ activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode));
+ activity.AddTag(SpanAttributeConstants.StatusDescriptionKey, response.StatusDescription);
var routeData = context.Request.RequestContext.RouteData;
@@ -131,14 +160,11 @@ namespace OpenTelemetry.Instrumentation.AspNet.Implementation
if (!string.IsNullOrEmpty(template))
{
- // Override the span name that was previously set to the path part of URL.
- span.UpdateName(template);
-
- span.PutHttpRouteAttribute(template);
+ // Override the name that was previously set to the path part of URL.
+ activity.DisplayName = template;
+ activity.AddTag(SpanAttributeConstants.HttpRouteKey, template);
}
}
-
- span.End();
}
}
}
diff --git a/src/OpenTelemetry.Instrumentation.AspNet.Win/OpenTelemetryBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.AspNet.Win/OpenTelemetryBuilderExtensions.cs
new file mode 100644
index 000000000..0e93d507a
--- /dev/null
+++ b/src/OpenTelemetry.Instrumentation.AspNet.Win/OpenTelemetryBuilderExtensions.cs
@@ -0,0 +1,53 @@
+//
+// 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.
+//
+
+using System;
+using OpenTelemetry.Instrumentation.AspNet;
+
+namespace OpenTelemetry.Trace.Configuration
+{
+ ///
+ /// Extension methods to simplify registering of Asp.Net request instrumentation.
+ ///
+ public static class OpenTelemetryBuilderExtensions
+ {
+ ///
+ /// Enables the incoming requests automatic data collection for Asp.Net.
+ ///
+ /// being configured.
+ /// ASP.NET Request configuration options.
+ /// The instance of to chain the calls.
+ public static OpenTelemetryBuilder AddRequestInstrumentation(
+ this OpenTelemetryBuilder builder,
+ Action configureAspNetInstrumentationOptions = null)
+ {
+ if (builder == null)
+ {
+ throw new ArgumentNullException(nameof(builder));
+ }
+
+ // Asp.Net is not instrumented with ActivitySource, hence
+ // it'll have a default ActivitySource with name string.Empty.
+ builder.AddActivitySource(string.Empty);
+ var aspnetOptions = new AspNetInstrumentationOptions();
+ configureAspNetInstrumentationOptions?.Invoke(aspnetOptions);
+
+ builder.AddInstrumentation(() => new AspNetInstrumentation(aspnetOptions));
+
+ return builder;
+ }
+ }
+}
diff --git a/src/OpenTelemetry.Instrumentation.AspNet.Win/TracerBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.AspNet.Win/TracerBuilderExtensions.cs
deleted file mode 100644
index 1666fd51d..000000000
--- a/src/OpenTelemetry.Instrumentation.AspNet.Win/TracerBuilderExtensions.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-//
-// 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.
-//
-
-using System;
-using OpenTelemetry.Instrumentation.AspNet;
-
-namespace OpenTelemetry.Trace.Configuration
-{
- ///
- /// Extension methods to simplify registering of data collection.
- ///
- public static class TracerBuilderExtensions
- {
- ///
- /// Enables the incoming requests automatic data collection.
- ///
- /// Trace builder to use.
- /// The instance of to chain the calls.
- public static TracerBuilder AddRequestInstrumentation(this TracerBuilder builder)
- {
- if (builder == null)
- {
- throw new ArgumentNullException(nameof(builder));
- }
-
- return builder.AddInstrumentation(t => new AspNetInstrumentation(t));
- }
-
- ///
- /// Enables the incoming requests automatic data collection.
- ///
- /// Trace builder to use.
- /// Configuration options.
- /// The instance of to chain the calls.
- public static TracerBuilder AddRequestInstrumentation(this TracerBuilder builder, Action configure)
- {
- if (builder == null)
- {
- throw new ArgumentNullException(nameof(builder));
- }
-
- if (configure == null)
- {
- throw new ArgumentNullException(nameof(configure));
- }
-
- var options = new AspNetInstrumentationOptions();
- configure(options);
-
- return builder.AddInstrumentation(t => new AspNetInstrumentation(t, options));
- }
- }
-}
diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentation.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentation.cs
index d7f67589f..8d33ea49d 100644
--- a/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentation.cs
+++ b/src/OpenTelemetry.Instrumentation.AspNetCore/AspNetCoreInstrumentation.cs
@@ -15,12 +15,11 @@
//
using System;
using OpenTelemetry.Instrumentation.AspNetCore.Implementation;
-using OpenTelemetry.Trace;
namespace OpenTelemetry.Instrumentation.AspNetCore
{
///
- /// Requests instrumentation.
+ /// Asp.Net Core Requests instrumentation.
///
public class AspNetCoreInstrumentation : IDisposable
{
@@ -29,20 +28,18 @@ namespace OpenTelemetry.Instrumentation.AspNetCore
///
/// Initializes a new instance of the class.
///
- /// Tracer to record traced with.
- public AspNetCoreInstrumentation(Tracer tracer)
- : this(tracer, new AspNetCoreInstrumentationOptions())
+ public AspNetCoreInstrumentation()
+ : this(new AspNetCoreInstrumentationOptions())
{
}
///
/// Initializes a new instance of the class.
///
- /// Tracer to record traced with.
/// Configuration options for ASP.NET Core instrumentation.
- public AspNetCoreInstrumentation(Tracer tracer, AspNetCoreInstrumentationOptions options)
+ public AspNetCoreInstrumentation(AspNetCoreInstrumentationOptions options)
{
- this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new HttpInListener("Microsoft.AspNetCore", tracer, options), null);
+ this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new HttpInListener("Microsoft.AspNetCore", options), null);
this.diagnosticSourceSubscriber.Subscribe();
}
diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs
index 689d9a471..454b902a0 100644
--- a/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs
+++ b/src/OpenTelemetry.Instrumentation.AspNetCore/Implementation/HttpInListener.cs
@@ -22,12 +22,18 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using OpenTelemetry.Context.Propagation;
using OpenTelemetry.Trace;
+using OpenTelemetry.Trace.Samplers;
namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
{
internal class HttpInListener : ListenerHandler
{
private static readonly string UnknownHostName = "UNKNOWN-HOST";
+
+ // hard-coded Sampler here, just to prototype.
+ // Either .NET will provide an new API to avoid Instrumentation being aware of sampling.
+ // or we'll expose an API from OT SDK.
+ private readonly ActivitySampler sampler = new AlwaysOnActivitySampler();
private readonly PropertyFetcher startContextFetcher = new PropertyFetcher("HttpContext");
private readonly PropertyFetcher stopContextFetcher = new PropertyFetcher("HttpContext");
private readonly PropertyFetcher beforeActionActionDescriptorFetcher = new PropertyFetcher("actionDescriptor");
@@ -36,8 +42,8 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
private readonly bool hostingSupportsW3C = false;
private readonly AspNetCoreInstrumentationOptions options;
- public HttpInListener(string name, Tracer tracer, AspNetCoreInstrumentationOptions options)
- : base(name, tracer)
+ public HttpInListener(string name, AspNetCoreInstrumentationOptions options)
+ : base(name, null)
{
this.hostingSupportsW3C = typeof(HttpRequest).Assembly.GetName().Version.Major >= 3;
this.options = options ?? throw new ArgumentNullException(nameof(options));
@@ -61,49 +67,73 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
}
var request = context.Request;
-
- // see the spec https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-semantic-conventions.md
var path = (request.PathBase.HasValue || request.Path.HasValue) ? (request.PathBase + request.Path).ToString() : "/";
+ activity.DisplayName = path;
- TelemetrySpan span;
- if (this.hostingSupportsW3C && this.options.TextFormat is TraceContextFormat)
- {
- this.Tracer.StartActiveSpanFromActivity(path, Activity.Current, SpanKind.Server, out span);
- }
- else
+ if (!this.hostingSupportsW3C || !(this.options.TextFormat is TraceContextFormat))
{
+ // This requires to ignore the current activity and create a new one
+ // using the context extracted from w3ctraceparent header or
+ // using the format TextFormat supports.
+ // TODO: implement this
+ /*
var ctx = this.options.TextFormat.Extract(
request,
(r, name) => r.Headers[name]);
- this.Tracer.StartActiveSpan(path, ctx, SpanKind.Server, out span);
+ Activity newOne = new Activity(path);
+ newOne.SetParentId(ctx.Id);
+ newOne.TraceState = ctx.TraceStateString;
+ activity = newOne;
+ */
}
- if (span.IsRecording)
+ // TODO: Avoid the reflection hack once .NET ships new Activity with Kind settable.
+ activity.GetType().GetProperty("Kind").SetValue(activity, ActivityKind.Server);
+
+ var samplingParameters = new ActivitySamplingParameters(
+ activity.Context,
+ activity.TraceId,
+ activity.DisplayName,
+ activity.Kind,
+ activity.Tags,
+ activity.Links);
+
+ // TODO: Find a way to avoid Instrumentation being tied to Sampler
+ var samplingDecision = this.sampler.ShouldSample(samplingParameters);
+ activity.IsAllDataRequested = samplingDecision.IsSampled;
+ if (samplingDecision.IsSampled)
{
- // Note, route is missing at this stage. It will be available later
- span.PutHttpHostAttribute(request.Host.Host, request.Host.Port ?? 80);
- span.PutHttpMethodAttribute(request.Method);
- span.PutHttpPathAttribute(path);
+ activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
+ }
+
+ if (activity.IsAllDataRequested)
+ {
+ // see the spec https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/data-semantic-conventions.md
+
+ if (request.Host.Port == 80 || request.Host.Port == 443)
+ {
+ activity.AddTag(SpanAttributeConstants.HttpHostKey, request.Host.Host);
+ }
+ else
+ {
+ activity.AddTag(SpanAttributeConstants.HttpHostKey, request.Host.Host + ":" + request.Host.Port);
+ }
+
+ activity.AddTag(SpanAttributeConstants.HttpMethodKey, request.Method);
+ activity.AddTag(SpanAttributeConstants.HttpPathKey, path);
var userAgent = request.Headers["User-Agent"].FirstOrDefault();
- span.PutHttpUserAgentAttribute(userAgent);
- span.PutHttpRawUrlAttribute(GetUri(request));
+ activity.AddTag(SpanAttributeConstants.HttpUserAgentKey, userAgent);
+ activity.AddTag(SpanAttributeConstants.HttpUrlKey, GetUri(request));
}
}
public override void OnStopActivity(Activity activity, object payload)
{
const string EventNameSuffix = ".OnStopActivity";
- var span = this.Tracer.CurrentSpan;
- if (span == null || !span.Context.IsValid)
- {
- InstrumentationEventSource.Log.NullOrBlankSpan(nameof(HttpInListener) + EventNameSuffix);
- return;
- }
-
- if (span.IsRecording)
+ if (activity.IsAllDataRequested)
{
if (!(this.stopContextFetcher.Fetch(payload) is HttpContext context))
{
@@ -112,26 +142,19 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
}
var response = context.Response;
+ activity.AddTag(SpanAttributeConstants.HttpStatusCodeKey, response.StatusCode.ToString());
- span.PutHttpStatusCode(response.StatusCode, response.HttpContext.Features.Get().ReasonPhrase);
+ Status status = SpanHelper.ResolveSpanStatusForHttpStatusCode((int)response.StatusCode);
+ activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode));
+ activity.AddTag(SpanAttributeConstants.StatusDescriptionKey, response.HttpContext.Features.Get().ReasonPhrase);
}
-
- span.End();
}
public override void OnCustom(string name, Activity activity, object payload)
{
if (name == "Microsoft.AspNetCore.Mvc.BeforeAction")
{
- var span = this.Tracer.CurrentSpan;
-
- if (span == null)
- {
- InstrumentationEventSource.Log.NullOrBlankSpan(name);
- return;
- }
-
- if (span.IsRecording)
+ if (activity.IsAllDataRequested)
{
// See https://github.com/aspnet/Mvc/blob/2414db256f32a047770326d14d8b0e2afd49ba49/src/Microsoft.AspNetCore.Mvc.Core/MvcCoreDiagnosticSourceExtensions.cs#L36-L44
// Reflection accessing: ActionDescriptor.AttributeRouteInfo.Template
@@ -145,9 +168,8 @@ namespace OpenTelemetry.Instrumentation.AspNetCore.Implementation
if (!string.IsNullOrEmpty(template))
{
// override the span name that was previously set to the path part of URL.
- span.UpdateName(template);
-
- span.PutHttpRouteAttribute(template);
+ activity.DisplayName = template;
+ activity.AddTag(SpanAttributeConstants.HttpRouteKey, template);
}
// TODO: Should we get values from RouteData?
diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/OpenTelemetryBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/OpenTelemetryBuilderExtensions.cs
new file mode 100644
index 000000000..10d49e930
--- /dev/null
+++ b/src/OpenTelemetry.Instrumentation.AspNetCore/OpenTelemetryBuilderExtensions.cs
@@ -0,0 +1,53 @@
+//
+// 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.
+//
+
+using System;
+using OpenTelemetry.Instrumentation.AspNetCore;
+
+namespace OpenTelemetry.Trace.Configuration
+{
+ ///
+ /// Extension methods to simplify registering of asp.net core request instrumentation.
+ ///
+ public static class OpenTelemetryBuilderExtensions
+ {
+ ///
+ /// Enables the incoming requests automatic data collection for Asp.Net Core.
+ ///
+ /// being configured.
+ /// ASP.NET Core Request configuration options.
+ /// The instance of to chain the calls.
+ public static OpenTelemetryBuilder AddRequestInstrumentation(
+ this OpenTelemetryBuilder builder,
+ Action configureAspNetCoreInstrumentationOptions = null)
+ {
+ if (builder == null)
+ {
+ throw new ArgumentNullException(nameof(builder));
+ }
+
+ // Asp.Net Core is not instrumented with ActivitySource, hence
+ // it'll have a default ActivitySource with name string.Empty.
+ builder.AddActivitySource(string.Empty);
+ var aspnetCoreOptions = new AspNetCoreInstrumentationOptions();
+ configureAspNetCoreInstrumentationOptions?.Invoke(aspnetCoreOptions);
+
+ builder.AddInstrumentation(() => new AspNetCoreInstrumentation(aspnetCoreOptions));
+
+ return builder;
+ }
+ }
+}
diff --git a/src/OpenTelemetry.Instrumentation.AspNetCore/TracerBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.AspNetCore/TracerBuilderExtensions.cs
deleted file mode 100644
index 916f34168..000000000
--- a/src/OpenTelemetry.Instrumentation.AspNetCore/TracerBuilderExtensions.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-//
-// 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.
-//
-
-using System;
-using OpenTelemetry.Instrumentation.AspNetCore;
-
-namespace OpenTelemetry.Trace.Configuration
-{
- ///
- /// Extension methods to simplify registering of data collection.
- ///
- public static class TracerBuilderExtensions
- {
- ///
- /// Enables the incoming requests automatic data collection.
- ///
- /// Trace builder to use.
- /// The instance of to chain the calls.
- public static TracerBuilder AddRequestInstrumentation(this TracerBuilder builder)
- {
- if (builder == null)
- {
- throw new ArgumentNullException(nameof(builder));
- }
-
- return builder.AddInstrumentation(t => new AspNetCoreInstrumentation(t));
- }
-
- ///
- /// Enables the incoming requests automatic data collection.
- ///
- /// Trace builder to use.
- /// Configuration options.
- /// The instance of to chain the calls.
- public static TracerBuilder AddRequestInstrumentation(this TracerBuilder builder, Action configure)
- {
- if (builder == null)
- {
- throw new ArgumentNullException(nameof(builder));
- }
-
- if (configure == null)
- {
- throw new ArgumentNullException(nameof(configure));
- }
-
- var options = new AspNetCoreInstrumentationOptions();
- configure(options);
-
- return builder.AddInstrumentation(t => new AspNetCoreInstrumentation(t, options));
- }
- }
-}
diff --git a/src/OpenTelemetry.Instrumentation.Dependencies/AzureClientsInstrumentation.cs b/src/OpenTelemetry.Instrumentation.Dependencies/AzureClientsInstrumentation.cs
index 864abb000..ef1a717af 100644
--- a/src/OpenTelemetry.Instrumentation.Dependencies/AzureClientsInstrumentation.cs
+++ b/src/OpenTelemetry.Instrumentation.Dependencies/AzureClientsInstrumentation.cs
@@ -14,12 +14,15 @@
// limitations under the License.
//
using System;
-using OpenTelemetry.Trace;
namespace OpenTelemetry.Instrumentation.Dependencies
{
///
- /// Dependencies instrumentation.
+ /// AzureClients instrumentation.
+ /// TODO: Azure specific listeners would be moved out of this repo.
+ /// I believe this was initially put here for quick validation.
+ /// There were no unit tests covering this feature, so
+ /// cannot validate after Span is replaced with Activity.
///
public class AzureClientsInstrumentation : IDisposable
{
@@ -28,11 +31,10 @@ namespace OpenTelemetry.Instrumentation.Dependencies
///
/// Initializes a new instance of the class.
///
- /// Tracer to record traced with.
- public AzureClientsInstrumentation(Tracer tracer)
+ public AzureClientsInstrumentation()
{
this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(
- name => new AzureSdkDiagnosticListener(name, tracer),
+ name => new AzureSdkDiagnosticListener(name),
listener => listener.Name.StartsWith("Azure."),
null);
this.diagnosticSourceSubscriber.Subscribe();
diff --git a/src/OpenTelemetry.Instrumentation.Dependencies/AzurePipelineInstrumentation.cs b/src/OpenTelemetry.Instrumentation.Dependencies/AzurePipelineInstrumentation.cs
index 5983fed19..3deb2e204 100644
--- a/src/OpenTelemetry.Instrumentation.Dependencies/AzurePipelineInstrumentation.cs
+++ b/src/OpenTelemetry.Instrumentation.Dependencies/AzurePipelineInstrumentation.cs
@@ -14,12 +14,15 @@
// limitations under the License.
//
using System;
-using OpenTelemetry.Trace;
namespace OpenTelemetry.Instrumentation.Dependencies
{
///
- /// Dependencies instrumentation.
+ /// AzurePipeline instrumentation.
+ /// TODO: Azure specific listeners would be moved out of this repo.
+ /// I believe this was initially put here for quick validation.
+ /// There were no unit tests covering this feature, so
+ /// cannot validate after Span is replaced with Activity.
///
public class AzurePipelineInstrumentation : IDisposable
{
@@ -28,10 +31,9 @@ namespace OpenTelemetry.Instrumentation.Dependencies
///
/// Initializes a new instance of the class.
///
- /// Tracer to record traced with.
- public AzurePipelineInstrumentation(Tracer tracer)
+ public AzurePipelineInstrumentation()
{
- this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new AzureSdkDiagnosticListener("Azure.Pipeline", tracer), null);
+ this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new AzureSdkDiagnosticListener("Azure.Pipeline"), null);
this.diagnosticSourceSubscriber.Subscribe();
}
diff --git a/src/OpenTelemetry.Instrumentation.Dependencies/DependenciesInstrumentation.cs b/src/OpenTelemetry.Instrumentation.Dependencies/DependenciesInstrumentation.cs
deleted file mode 100644
index 28421c5ea..000000000
--- a/src/OpenTelemetry.Instrumentation.Dependencies/DependenciesInstrumentation.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-//
-// 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.
-//
-using System;
-using System.Collections.Generic;
-using OpenTelemetry.Trace;
-
-namespace OpenTelemetry.Instrumentation.Dependencies
-{
- ///
- /// Instrumentation adaptor that automatically collect calls to Http, SQL, and Azure SDK.
- ///
- public class DependenciesInstrumentation : IDisposable
- {
- private readonly List instrumentations = new List();
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Tracer factory to get a tracer from.
- /// Http configuration options.
- /// Sql configuration options.
- public DependenciesInstrumentation(TracerFactoryBase tracerFactory, HttpClientInstrumentationOptions httpOptions = null, SqlClientInstrumentationOptions sqlOptions = null)
- {
- if (tracerFactory == null)
- {
- throw new ArgumentNullException(nameof(tracerFactory));
- }
-
- var assemblyVersion = typeof(DependenciesInstrumentation).Assembly.GetName().Version;
-
- var httpClientListener = new HttpClientInstrumentation(tracerFactory.GetTracer(nameof(HttpClientInstrumentation), "semver:" + assemblyVersion), httpOptions ?? new HttpClientInstrumentationOptions());
- var azureClientsListener = new AzureClientsInstrumentation(tracerFactory.GetTracer(nameof(AzureClientsInstrumentation), "semver:" + assemblyVersion));
- var azurePipelineListener = new AzurePipelineInstrumentation(tracerFactory.GetTracer(nameof(AzurePipelineInstrumentation), "semver:" + assemblyVersion));
- var sqlClientListener = new SqlClientInstrumentation(tracerFactory.GetTracer(nameof(SqlClientInstrumentation), "semver:" + assemblyVersion), sqlOptions ?? new SqlClientInstrumentationOptions());
-
- this.instrumentations.Add(httpClientListener);
- this.instrumentations.Add(azureClientsListener);
- this.instrumentations.Add(azurePipelineListener);
- this.instrumentations.Add(sqlClientListener);
- }
-
- ///
- public void Dispose()
- {
- foreach (var instrumentation in this.instrumentations)
- {
- instrumentation.Dispose();
- }
- }
- }
-}
diff --git a/src/OpenTelemetry.Instrumentation.Dependencies/HttpClientInstrumentation.cs b/src/OpenTelemetry.Instrumentation.Dependencies/HttpClientInstrumentation.cs
index 3f7ace260..9dfdb0a2e 100644
--- a/src/OpenTelemetry.Instrumentation.Dependencies/HttpClientInstrumentation.cs
+++ b/src/OpenTelemetry.Instrumentation.Dependencies/HttpClientInstrumentation.cs
@@ -15,7 +15,6 @@
//
using System;
using OpenTelemetry.Instrumentation.Dependencies.Implementation;
-using OpenTelemetry.Trace;
namespace OpenTelemetry.Instrumentation.Dependencies
{
@@ -29,20 +28,18 @@ namespace OpenTelemetry.Instrumentation.Dependencies
///
/// Initializes a new instance of the class.
///
- /// Tracer to record traced with.
- public HttpClientInstrumentation(Tracer tracer)
- : this(tracer, new HttpClientInstrumentationOptions())
+ public HttpClientInstrumentation()
+ : this(new HttpClientInstrumentationOptions())
{
}
///
/// Initializes a new instance of the class.
///
- /// Tracer to record traced with.
/// Configuration options for dependencies instrumentation.
- public HttpClientInstrumentation(Tracer tracer, HttpClientInstrumentationOptions options)
+ public HttpClientInstrumentation(HttpClientInstrumentationOptions options)
{
- this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new HttpHandlerDiagnosticListener(tracer, options), options.EventFilter);
+ this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(new HttpHandlerDiagnosticListener(options), options.EventFilter);
this.diagnosticSourceSubscriber.Subscribe();
}
diff --git a/src/OpenTelemetry.Instrumentation.Dependencies/Implementation/AzureSdkDiagnosticListener.cs b/src/OpenTelemetry.Instrumentation.Dependencies/Implementation/AzureSdkDiagnosticListener.cs
index 4fa6ba353..bc9be556a 100644
--- a/src/OpenTelemetry.Instrumentation.Dependencies/Implementation/AzureSdkDiagnosticListener.cs
+++ b/src/OpenTelemetry.Instrumentation.Dependencies/Implementation/AzureSdkDiagnosticListener.cs
@@ -24,11 +24,16 @@ namespace OpenTelemetry.Instrumentation.Dependencies
{
internal class AzureSdkDiagnosticListener : ListenerHandler
{
+ internal const string ActivitySourceName = "AzureSDK";
+ internal const string ActivityName = ActivitySourceName + ".HttpRequestOut";
+ private static readonly Version Version = typeof(AzureSdkDiagnosticListener).Assembly.GetName().Version;
+ private static readonly ActivitySource AzureSDKActivitySource = new ActivitySource(ActivitySourceName, Version.ToString());
+
// all fetchers must not be reused between DiagnosticSources.
private readonly PropertyFetcher linksPropertyFetcher = new PropertyFetcher("Links");
- public AzureSdkDiagnosticListener(string sourceName, Tracer tracer)
- : base(sourceName, tracer)
+ public AzureSdkDiagnosticListener(string sourceName)
+ : base(sourceName, null)
{
}
@@ -40,91 +45,65 @@ namespace OpenTelemetry.Instrumentation.Dependencies
{
}
- public override void OnStartActivity(Activity current, object valueValue)
+ public override void OnStartActivity(Activity activity, object valueValue)
{
string operationName = null;
- var spanKind = SpanKind.Internal;
+ var activityKind = ActivityKind.Internal;
- foreach (var keyValuePair in current.Tags)
+ foreach (var keyValuePair in activity.Tags)
{
if (keyValuePair.Key == "http.url")
{
operationName = keyValuePair.Value;
- spanKind = SpanKind.Client;
+ activityKind = ActivityKind.Client;
break;
}
if (keyValuePair.Key == "kind")
{
- if (Enum.TryParse(keyValuePair.Value, true, out SpanKind parsedSpanKind))
+ if (Enum.TryParse(keyValuePair.Value, true, out ActivityKind parsedActivityKind))
{
- spanKind = parsedSpanKind;
+ activityKind = parsedActivityKind;
}
}
}
if (operationName == null)
{
- operationName = this.GetOperationName(current);
+ operationName = this.GetOperationName(activity);
}
- List parentLinks = null;
+ List links = null;
if (this.linksPropertyFetcher.Fetch(valueValue) is IEnumerable activityLinks)
{
if (activityLinks.Any())
{
- parentLinks = new List();
+ links = new List();
foreach (var link in activityLinks)
{
if (link != null)
{
- parentLinks.Add(new Link(new SpanContext(link.TraceId, link.ParentSpanId, link.ActivityTraceFlags)));
+ links.Add(new ActivityLink(new ActivityContext(link.TraceId, link.ParentSpanId, link.ActivityTraceFlags)));
}
}
}
}
- this.Tracer.StartSpanFromActivity(operationName, current, spanKind, parentLinks);
+ // Ignore the activity and create a new one using ActivitySource.
+ // The new one will have Sampling decision made using extracted Links as well.
+ AzureSDKActivitySource.StartActivity(operationName, activityKind, activity.Id, activity.Tags, links);
}
public override void OnStopActivity(Activity current, object valueValue)
{
- var span = this.Tracer.CurrentSpan;
- try
- {
- if (span == null || !span.Context.IsValid)
- {
- InstrumentationEventSource.Log.NullOrBlankSpan(this.SourceName + ".OnStopActivity");
- return;
- }
-
- if (span.IsRecording)
- {
- foreach (var keyValuePair in current.Tags)
- {
- span.SetAttribute(keyValuePair.Key, keyValuePair.Value);
- }
- }
- }
- finally
- {
- span?.End();
- }
+ // nothing to be done.
}
- public override void OnException(Activity current, object valueValue)
+ public override void OnException(Activity activity, object valueValue)
{
- var span = this.Tracer.CurrentSpan;
-
- if (span == null || !span.Context.IsValid)
- {
- InstrumentationEventSource.Log.NullOrBlankSpan(this.SourceName + ".OnException");
- return;
- }
-
- span.Status = Status.Unknown.WithDescription(valueValue?.ToString());
-
- // Note: Span.End() is not called here on purpose, OnStopActivity is called after OnException for this listener.
+ Status status = Status.Unknown;
+ activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode));
+ activity.AddTag(SpanAttributeConstants.StatusDescriptionKey, valueValue?.ToString());
}
private string GetOperationName(Activity activity)
diff --git a/src/OpenTelemetry.Instrumentation.Dependencies/Implementation/HttpHandlerDiagnosticListener.cs b/src/OpenTelemetry.Instrumentation.Dependencies/Implementation/HttpHandlerDiagnosticListener.cs
index b1b0aeb68..9deb57f85 100644
--- a/src/OpenTelemetry.Instrumentation.Dependencies/Implementation/HttpHandlerDiagnosticListener.cs
+++ b/src/OpenTelemetry.Instrumentation.Dependencies/Implementation/HttpHandlerDiagnosticListener.cs
@@ -23,6 +23,7 @@ using System.Text.RegularExpressions;
using System.Threading.Tasks;
using OpenTelemetry.Context.Propagation;
using OpenTelemetry.Trace;
+using OpenTelemetry.Trace.Samplers;
namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
{
@@ -30,6 +31,11 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
{
private static readonly Regex CoreAppMajorVersionCheckRegex = new Regex("^\\.NETCoreApp,Version=v(\\d+)\\.", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+ // hard-coded Sampler here, just to prototype.
+ // Either .NET will provide an new API to avoid Instrumentation being aware of sampling.
+ // or we'll move the Sampler to come from OpenTelemetryBuilder, and not hardcoded.
+ private readonly ActivitySampler sampler = new AlwaysOnActivitySampler();
+
private readonly PropertyFetcher startRequestFetcher = new PropertyFetcher("Request");
private readonly PropertyFetcher stopResponseFetcher = new PropertyFetcher("Response");
private readonly PropertyFetcher stopExceptionFetcher = new PropertyFetcher("Exception");
@@ -37,8 +43,8 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
private readonly bool httpClientSupportsW3C = false;
private readonly HttpClientInstrumentationOptions options;
- public HttpHandlerDiagnosticListener(Tracer tracer, HttpClientInstrumentationOptions options)
- : base("HttpHandlerDiagnosticListener", tracer)
+ public HttpHandlerDiagnosticListener(HttpClientInstrumentationOptions options)
+ : base("HttpHandlerDiagnosticListener", null)
{
var framework = Assembly
.GetEntryAssembly()?
@@ -73,84 +79,87 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
return;
}
- this.Tracer.StartActiveSpanFromActivity(HttpTagHelper.GetOperationNameForHttpMethod(request.Method), activity, SpanKind.Client, out var span);
+ // TODO: Avoid the reflection hack once .NET ships new Activity with Kind settable.
+ activity.GetType().GetProperty("Kind").SetValue(activity, ActivityKind.Client);
+ activity.DisplayName = HttpTagHelper.GetOperationNameForHttpMethod(request.Method);
- if (span.IsRecording)
+ var samplingParameters = new ActivitySamplingParameters(
+ activity.Context,
+ activity.TraceId,
+ activity.DisplayName,
+ activity.Kind,
+ activity.Tags,
+ activity.Links);
+
+ // TODO: Find a way to avoid Instrumentation being tied to Sampler
+ var samplingDecision = this.sampler.ShouldSample(samplingParameters);
+ activity.IsAllDataRequested = samplingDecision.IsSampled;
+ if (samplingDecision.IsSampled)
{
- span.PutComponentAttribute("http");
- span.PutHttpMethodAttribute(HttpTagHelper.GetNameForHttpMethod(request.Method));
- span.PutHttpHostAttribute(HttpTagHelper.GetHostTagValueFromRequestUri(request.RequestUri));
- span.PutHttpRawUrlAttribute(request.RequestUri.OriginalString);
+ activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
+ }
+
+ if (activity.IsAllDataRequested)
+ {
+ activity.AddTag(SpanAttributeConstants.ComponentKey, "http");
+ activity.AddTag(SpanAttributeConstants.HttpMethodKey, HttpTagHelper.GetNameForHttpMethod(request.Method));
+ activity.AddTag(SpanAttributeConstants.HttpHostKey, HttpTagHelper.GetHostTagValueFromRequestUri(request.RequestUri));
+ activity.AddTag(SpanAttributeConstants.HttpUrlKey, request.RequestUri.OriginalString);
if (this.options.SetHttpFlavor)
{
- span.PutHttpFlavorAttribute(HttpTagHelper.GetFlavorTagValueFromProtocolVersion(request.Version));
+ activity.AddTag(SpanAttributeConstants.HttpFlavorKey, HttpTagHelper.GetFlavorTagValueFromProtocolVersion(request.Version));
}
}
if (!(this.httpClientSupportsW3C && this.options.TextFormat is TraceContextFormat))
{
- this.options.TextFormat.Inject(span.Context, request, (r, k, v) => r.Headers.Add(k, v));
+ // TODO: implement this
+ // this.options.TextFormat.Inject(span.Context, request, (r, k, v) => r.Headers.Add(k, v));
}
}
public override void OnStopActivity(Activity activity, object payload)
{
- const string EventNameSuffix = ".OnStopActivity";
- var span = this.Tracer.CurrentSpan;
- try
+ if (activity.IsAllDataRequested)
{
- if (span == null || !span.Context.IsValid)
- {
- InstrumentationEventSource.Log.NullOrBlankSpan(nameof(HttpHandlerDiagnosticListener) + EventNameSuffix);
- return;
- }
+ var requestTaskStatus = this.stopRequestStatusFetcher.Fetch(payload) as TaskStatus?;
- if (span.IsRecording)
+ if (requestTaskStatus.HasValue)
{
- var requestTaskStatus = this.stopRequestStatusFetcher.Fetch(payload) as TaskStatus?;
-
- if (requestTaskStatus.HasValue)
+ if (requestTaskStatus != TaskStatus.RanToCompletion)
{
- if (requestTaskStatus != TaskStatus.RanToCompletion)
+ if (requestTaskStatus == TaskStatus.Canceled)
{
- if (requestTaskStatus == TaskStatus.Canceled)
- {
- span.Status = Status.Cancelled;
- }
- else if (requestTaskStatus != TaskStatus.Faulted)
- {
- // Faults are handled in OnException and should already have a span.Status of Unknown w/ Description.
- span.Status = Status.Unknown;
- }
+ Status status = Status.Cancelled;
+ activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode));
+ }
+ else if (requestTaskStatus != TaskStatus.Faulted)
+ {
+ // Faults are handled in OnException and should already have a span.Status of Unknown w/ Description.
+ Status status = Status.Unknown;
+ activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode));
}
}
-
- if (this.stopResponseFetcher.Fetch(payload) is HttpResponseMessage response)
- {
- // response could be null for DNS issues, timeouts, etc...
- span.PutHttpStatusCode((int)response.StatusCode, response.ReasonPhrase);
- }
}
- }
- finally
- {
- span?.End();
+
+ if (this.stopResponseFetcher.Fetch(payload) is HttpResponseMessage response)
+ {
+ // response could be null for DNS issues, timeouts, etc...
+ activity.AddTag(SpanAttributeConstants.HttpStatusCodeKey, response.StatusCode.ToString());
+
+ Status status = SpanHelper.ResolveSpanStatusForHttpStatusCode((int)response.StatusCode);
+ activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode));
+ activity.AddTag(SpanAttributeConstants.StatusDescriptionKey, response.ReasonPhrase);
+ }
}
}
public override void OnException(Activity activity, object payload)
{
const string EventNameSuffix = ".OnException";
- var span = this.Tracer.CurrentSpan;
- if (span == null || !span.Context.IsValid)
- {
- InstrumentationEventSource.Log.NullOrBlankSpan(nameof(HttpHandlerDiagnosticListener) + EventNameSuffix);
- return;
- }
-
- if (span.IsRecording)
+ if (activity.IsAllDataRequested)
{
if (!(this.stopExceptionFetcher.Fetch(payload) is Exception exc))
{
@@ -165,19 +174,21 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
switch (exception.SocketErrorCode)
{
case SocketError.HostNotFound:
- span.Status = Status.InvalidArgument.WithDescription(exc.Message);
+ Status status = Status.InvalidArgument;
+ activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode));
+ activity.AddTag(SpanAttributeConstants.StatusDescriptionKey, exc.Message);
return;
}
}
if (exc.InnerException != null)
{
- span.Status = Status.Unknown.WithDescription(exc.Message);
+ Status status = Status.Unknown;
+ activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode));
+ activity.AddTag(SpanAttributeConstants.StatusDescriptionKey, exc.Message);
}
}
}
-
- // Note: Span.End() is not called here on purpose, OnStopActivity is called after OnException for this listener.
}
}
}
diff --git a/src/OpenTelemetry.Instrumentation.Dependencies/Implementation/SqlClientDiagnosticListener.cs b/src/OpenTelemetry.Instrumentation.Dependencies/Implementation/SqlClientDiagnosticListener.cs
index 46ed0d294..3b8c48d58 100644
--- a/src/OpenTelemetry.Instrumentation.Dependencies/Implementation/SqlClientDiagnosticListener.cs
+++ b/src/OpenTelemetry.Instrumentation.Dependencies/Implementation/SqlClientDiagnosticListener.cs
@@ -17,6 +17,7 @@ using System;
using System.Data;
using System.Diagnostics;
using OpenTelemetry.Trace;
+using OpenTelemetry.Trace.Samplers;
namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
{
@@ -42,8 +43,13 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
private readonly PropertyFetcher exceptionFetcher = new PropertyFetcher("Exception");
private readonly SqlClientInstrumentationOptions options;
- public SqlClientDiagnosticListener(string sourceName, Tracer tracer, SqlClientInstrumentationOptions options)
- : base(sourceName, tracer)
+ // hard-coded Sampler here, just to prototype.
+ // Either .NET will provide an new API to avoid Instrumentation being aware of sampling.
+ // or we'll move the Sampler to come from OpenTelemetryBuilder, and not hardcoded.
+ private readonly ActivitySampler sampler = new AlwaysOnActivitySampler();
+
+ public SqlClientDiagnosticListener(string sourceName, SqlClientInstrumentationOptions options)
+ : base(sourceName, null)
{
this.options = options ?? throw new ArgumentNullException(nameof(options));
}
@@ -70,29 +76,46 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
var connection = this.connectionFetcher.Fetch(command);
var database = this.databaseFetcher.Fetch(connection);
- this.Tracer.StartActiveSpan((string)database, SpanKind.Client, out var span);
+ // TODO: Avoid the reflection hack once .NET ships new Activity with Kind settable.
+ activity.GetType().GetProperty("Kind").SetValue(activity, ActivityKind.Client);
+ activity.DisplayName = (string)database;
- if (span.IsRecording)
+ var samplingParameters = new ActivitySamplingParameters(
+ activity.Context,
+ activity.TraceId,
+ activity.DisplayName,
+ activity.Kind,
+ activity.Tags,
+ activity.Links);
+
+ // TODO: Find a way to avoid Instrumentation being tied to Sampler
+ var samplingDecision = this.sampler.ShouldSample(samplingParameters);
+ activity.IsAllDataRequested = samplingDecision.IsSampled;
+ if (samplingDecision.IsSampled)
+ {
+ activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
+ }
+
+ if (activity.IsAllDataRequested)
{
var dataSource = this.dataSourceFetcher.Fetch(connection);
var commandText = this.commandTextFetcher.Fetch(command);
- span.PutComponentAttribute("sql");
-
- span.PutDatabaseTypeAttribute("sql");
- span.PutPeerServiceAttribute((string)dataSource);
- span.PutDatabaseInstanceAttribute((string)database);
+ activity.AddTag(SpanAttributeConstants.ComponentKey, "sql");
+ activity.AddTag(SpanAttributeConstants.DatabaseTypeKey, "sql");
+ activity.AddTag(SpanAttributeConstants.PeerServiceKey, (string)dataSource);
+ activity.AddTag(SpanAttributeConstants.DatabaseInstanceKey, (string)database);
if (this.commandTypeFetcher.Fetch(command) is CommandType commandType)
{
- span.SetAttribute(DatabaseStatementTypeSpanAttributeKey, commandType.ToString());
+ activity.AddTag(DatabaseStatementTypeSpanAttributeKey, commandType.ToString());
switch (commandType)
{
case CommandType.StoredProcedure:
if (this.options.CaptureStoredProcedureCommandName)
{
- span.PutDatabaseStatementAttribute((string)commandText);
+ activity.AddTag(SpanAttributeConstants.DatabaseStatementKey, (string)commandText);
}
break;
@@ -100,7 +123,7 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
case CommandType.Text:
if (this.options.CaptureTextCommandContent)
{
- span.PutDatabaseStatementAttribute((string)commandText);
+ activity.AddTag(SpanAttributeConstants.DatabaseStatementKey, (string)commandText);
}
break;
@@ -113,50 +136,25 @@ namespace OpenTelemetry.Instrumentation.Dependencies.Implementation
case SqlDataAfterExecuteCommand:
case SqlMicrosoftAfterExecuteCommand:
{
- var span = this.Tracer.CurrentSpan;
- try
- {
- if (span == null || !span.Context.IsValid)
- {
- InstrumentationEventSource.Log.NullOrBlankSpan($"{nameof(SqlClientDiagnosticListener)}-{name}");
- return;
- }
- }
- finally
- {
- span?.End();
- }
}
break;
case SqlDataWriteCommandError:
case SqlMicrosoftWriteCommandError:
{
- var span = this.Tracer.CurrentSpan;
- try
+ if (activity.IsAllDataRequested)
{
- if (span == null || !span.Context.IsValid)
+ if (this.exceptionFetcher.Fetch(payload) is Exception exception)
{
- InstrumentationEventSource.Log.NullOrBlankSpan($"{nameof(SqlClientDiagnosticListener)}-{name}");
- return;
+ Status status = Status.Unknown;
+ activity.AddTag(SpanAttributeConstants.StatusCodeKey, SpanHelper.GetCachedCanonicalCodeString(status.CanonicalCode));
+ activity.AddTag(SpanAttributeConstants.StatusDescriptionKey, exception.Message);
}
-
- if (span.IsRecording)
+ else
{
- if (this.exceptionFetcher.Fetch(payload) is Exception exception)
- {
- span.Status = Status.Unknown.WithDescription(exception.Message);
- }
- else
- {
- InstrumentationEventSource.Log.NullPayload($"{nameof(SqlClientDiagnosticListener)}-{name}");
- }
+ InstrumentationEventSource.Log.NullPayload($"{nameof(SqlClientDiagnosticListener)}-{name}");
}
}
- finally
- {
- span?.End();
- }
}
break;
diff --git a/src/OpenTelemetry.Instrumentation.Dependencies/OpenTelemetryBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.Dependencies/OpenTelemetryBuilderExtensions.cs
index 36304f98f..5fecef6f7 100644
--- a/src/OpenTelemetry.Instrumentation.Dependencies/OpenTelemetryBuilderExtensions.cs
+++ b/src/OpenTelemetry.Instrumentation.Dependencies/OpenTelemetryBuilderExtensions.cs
@@ -15,6 +15,7 @@
//
using System;
+using OpenTelemetry.Instrumentation.Dependencies;
using OpenTelemetry.Instrumentation.Dependencies.Implementation;
namespace OpenTelemetry.Trace.Configuration
@@ -36,12 +37,132 @@ namespace OpenTelemetry.Trace.Configuration
throw new ArgumentNullException(nameof(builder));
}
+ builder.AddHttpClientDependencyInstrumentation();
+ builder.AddSqlClientDependencyInstrumentation();
+ builder.AddAzureClientsDependencyInstrumentation();
#if NET461
builder.AddHttpWebRequestDependencyInstrumentation();
#endif
return builder;
}
+ ///
+ /// Enables the outgoing requests automatic data collection for all supported activity sources.
+ ///
+ /// being configured.
+ /// HttpClient configuration options.
+ /// SqlClient configuration options.
+ /// The instance of to chain the calls.
+ public static OpenTelemetryBuilder AddDependencyInstrumentation(
+ this OpenTelemetryBuilder builder,
+ Action configureHttpClientInstrumentationOptions = null,
+ Action configureSqlClientInstrumentationOptions = null)
+ {
+ if (builder == null)
+ {
+ throw new ArgumentNullException(nameof(builder));
+ }
+
+ builder.AddHttpClientDependencyInstrumentation(configureHttpClientInstrumentationOptions);
+ builder.AddSqlClientDependencyInstrumentation(configureSqlClientInstrumentationOptions);
+ builder.AddAzureClientsDependencyInstrumentation();
+#if NET461
+ builder.AddHttpWebRequestDependencyInstrumentation();
+#endif
+ return builder;
+ }
+
+ ///
+ /// Enables the outgoing requests automatic data collection for HttpClient.
+ ///
+ /// being configured.
+ /// The instance of to chain the calls.
+ public static OpenTelemetryBuilder AddHttpClientDependencyInstrumentation(
+ this OpenTelemetryBuilder builder)
+ {
+ return builder.AddHttpClientDependencyInstrumentation(null);
+ }
+
+ ///
+ /// Enables the outgoing requests automatic data collection for HttpClient.
+ ///
+ /// being configured.
+ /// HttpClient configuration options.
+ /// The instance of to chain the calls.
+ public static OpenTelemetryBuilder AddHttpClientDependencyInstrumentation(
+ this OpenTelemetryBuilder builder,
+ Action configureHttpClientInstrumentationOptions)
+ {
+ if (builder == null)
+ {
+ throw new ArgumentNullException(nameof(builder));
+ }
+
+ // HttpClient is not instrumented with ActivitySource, hence
+ // it'll have a default ActivitySource with name string.Empty.
+ builder.AddActivitySource(string.Empty);
+ var httpClientOptions = new HttpClientInstrumentationOptions();
+ configureHttpClientInstrumentationOptions?.Invoke(httpClientOptions);
+
+ builder.AddInstrumentation(() => new HttpClientInstrumentation(httpClientOptions));
+ return builder;
+ }
+
+ ///
+ /// Enables the outgoing requests automatic data collection for SqlClient.
+ ///
+ /// being configured.
+ /// The instance of to chain the calls.
+ public static OpenTelemetryBuilder AddSqlClientDependencyInstrumentation(
+ this OpenTelemetryBuilder builder)
+ {
+ return builder.AddSqlClientDependencyInstrumentation(null);
+ }
+
+ ///
+ /// Enables the outgoing requests automatic data collection for SqlClient.
+ ///
+ /// being configured.
+ /// SqlClient configuration options.
+ /// The instance of to chain the calls.
+ public static OpenTelemetryBuilder AddSqlClientDependencyInstrumentation(
+ this OpenTelemetryBuilder builder,
+ Action configureSqlClientInstrumentationOptions)
+ {
+ if (builder == null)
+ {
+ throw new ArgumentNullException(nameof(builder));
+ }
+
+ // HttpClient is not instrumented with ActivitySource, hence
+ // it'll have a default ActivitySource with name string.Empty.
+ builder.AddActivitySource(string.Empty);
+ var sqlOptions = new SqlClientInstrumentationOptions();
+ configureSqlClientInstrumentationOptions?.Invoke(sqlOptions);
+
+ builder.AddInstrumentation(() => new SqlClientInstrumentation(sqlOptions));
+
+ return builder;
+ }
+
+ ///
+ /// Enables instrumentation for Azure clients.
+ ///
+ /// being configured.
+ /// The instance of to chain the calls.
+ public static OpenTelemetryBuilder AddAzureClientsDependencyInstrumentation(
+ this OpenTelemetryBuilder builder)
+ {
+ if (builder == null)
+ {
+ throw new ArgumentNullException(nameof(builder));
+ }
+
+ builder.AddActivitySource(AzureSdkDiagnosticListener.ActivitySourceName);
+ builder.AddInstrumentation(() => new AzureClientsInstrumentation());
+ return builder;
+ }
+
#if NET461
///
/// Enables the outgoing requests automatic data collection for .NET Framework HttpWebRequest activity source.
diff --git a/src/OpenTelemetry.Instrumentation.Dependencies/SqlClientInstrumentation.cs b/src/OpenTelemetry.Instrumentation.Dependencies/SqlClientInstrumentation.cs
index 32e0c6c4c..14b974aa1 100644
--- a/src/OpenTelemetry.Instrumentation.Dependencies/SqlClientInstrumentation.cs
+++ b/src/OpenTelemetry.Instrumentation.Dependencies/SqlClientInstrumentation.cs
@@ -15,7 +15,6 @@
//
using System;
using OpenTelemetry.Instrumentation.Dependencies.Implementation;
-using OpenTelemetry.Trace;
namespace OpenTelemetry.Instrumentation.Dependencies
{
@@ -31,21 +30,19 @@ namespace OpenTelemetry.Instrumentation.Dependencies
///
/// Initializes a new instance of the class.
///
- /// Tracer to record traced with.
- public SqlClientInstrumentation(Tracer tracer)
- : this(tracer, new SqlClientInstrumentationOptions())
+ public SqlClientInstrumentation()
+ : this(new SqlClientInstrumentationOptions())
{
}
///
/// Initializes a new instance of the class.
///
- /// Tracer to record traced with.
/// Configuration options for sql instrumentation.
- public SqlClientInstrumentation(Tracer tracer, SqlClientInstrumentationOptions options)
+ public SqlClientInstrumentation(SqlClientInstrumentationOptions options)
{
this.diagnosticSourceSubscriber = new DiagnosticSourceSubscriber(
- name => new SqlClientDiagnosticListener(name, tracer, options),
+ name => new SqlClientDiagnosticListener(name, options),
listener => listener.Name == SqlClientDiagnosticListenerName,
null);
this.diagnosticSourceSubscriber.Subscribe();
diff --git a/src/OpenTelemetry.Instrumentation.Dependencies/TracerBuilderExtensions.cs b/src/OpenTelemetry.Instrumentation.Dependencies/TracerBuilderExtensions.cs
deleted file mode 100644
index 7efae709d..000000000
--- a/src/OpenTelemetry.Instrumentation.Dependencies/TracerBuilderExtensions.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-//
-// 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.
-//
-
-using System;
-using OpenTelemetry.Instrumentation.Dependencies;
-
-namespace OpenTelemetry.Trace.Configuration
-{
- ///
- /// Extension methods to simplify registering of data collection.
- ///
- public static class TracerBuilderExtensions
- {
- ///
- /// Enables the outgoing requests automatic data collection.
- ///
- /// Trace builder to use.
- /// The instance of to chain the calls.
- public static TracerBuilder AddDependencyInstrumentation(this TracerBuilder builder)
- {
- if (builder == null)
- {
- throw new ArgumentNullException(nameof(builder));
- }
-
- return builder
- .AddInstrumentation((t) => new AzureClientsInstrumentation(t))
- .AddInstrumentation((t) => new AzurePipelineInstrumentation(t))
- .AddInstrumentation((t) => new HttpClientInstrumentation(t))
- .AddInstrumentation((t) => new SqlClientInstrumentation(t));
- }
-
- ///
- /// Enables the outgoing requests automatic data collection.
- ///
- /// Trace builder to use.
- /// Http configuration options.
- /// Sql configuration options.
- /// The instance of to chain the calls.
- public static TracerBuilder AddDependencyInstrumentation(
- this TracerBuilder builder,
- Action configureHttpInstrumentationOptions = null,
- Action configureSqlInstrumentationOptions = null)
- {
- if (builder == null)
- {
- throw new ArgumentNullException(nameof(builder));
- }
-
- var httpOptions = new HttpClientInstrumentationOptions();
- configureHttpInstrumentationOptions?.Invoke(httpOptions);
-
- var sqlOptions = new SqlClientInstrumentationOptions();
- configureSqlInstrumentationOptions?.Invoke(sqlOptions);
-
- return builder
- .AddInstrumentation((t) => new AzureClientsInstrumentation(t))
- .AddInstrumentation((t) => new AzurePipelineInstrumentation(t))
- .AddInstrumentation((t) => new HttpClientInstrumentation(t, httpOptions))
- .AddInstrumentation((t) => new SqlClientInstrumentation(t, sqlOptions));
- }
- }
-}
diff --git a/src/OpenTelemetry/Trace/Configuration/OpenTelemetryBuilder.cs b/src/OpenTelemetry/Trace/Configuration/OpenTelemetryBuilder.cs
index 33a7a5472..0d39af54b 100644
--- a/src/OpenTelemetry/Trace/Configuration/OpenTelemetryBuilder.cs
+++ b/src/OpenTelemetry/Trace/Configuration/OpenTelemetryBuilder.cs
@@ -31,6 +31,8 @@ namespace OpenTelemetry.Trace.Configuration
internal ActivityProcessorPipelineBuilder ProcessingPipeline { get; private set; }
+ internal List InstrumentationFactories { get; private set; }
+
internal ActivitySampler Sampler { get; private set; }
internal HashSet ActivitySourceNames { get; private set; }
@@ -79,5 +81,48 @@ namespace OpenTelemetry.Trace.Configuration
this.ActivitySourceNames.Add(activitySourceName.ToUpperInvariant());
return this;
}
+
+ ///
+ /// Adds auto-instrumentations for spans.
+ ///
+ /// Type of instrumentation class.
+ /// Function that builds instrumentation.
+ /// Returns for chaining.
+ public OpenTelemetryBuilder AddInstrumentation(
+ Func instrumentationFactory)
+ where TInstrumentation : class
+ {
+ if (instrumentationFactory == null)
+ {
+ throw new ArgumentNullException(nameof(instrumentationFactory));
+ }
+
+ if (this.InstrumentationFactories == null)
+ {
+ this.InstrumentationFactories = new List();
+ }
+
+ this.InstrumentationFactories.Add(
+ new InstrumentationFactory(
+ typeof(TInstrumentation).Name,
+ "semver:" + typeof(TInstrumentation).Assembly.GetName().Version,
+ instrumentationFactory));
+
+ return this;
+ }
+
+ internal readonly struct InstrumentationFactory
+ {
+ public readonly string Name;
+ public readonly string Version;
+ public readonly Func