Skip to content

Commit

Permalink
Instrumentation Adapters to support Activity API (#701)
Browse files Browse the repository at this point in the history
* use the same activity created by existing instrumentation. Simply enhance it.

* remove sampling from instrumentation

* add asp.net core instrumentation

* move sampling to Instrumentation for now.
Make Asp.Net instrumentation work with Activity API

* Add httpClient .net core instrumentation

* Ad SqlClientInstrumentation

* remove sqlclientinstrumentation from previous instrumentation

* Quick implementation for AzureClients - not validated as there are unit tests and this is not planned to be in this repo as well.

* fix examples

* fix sampling flag

* made sample app work with jaeger

* Mark todos and fix AspNet tests

* Fix asp.net core tests and mark TODOs

* Add TODO for httpclient .net core test and fix test

* add todo and fix httpclient test

* add todos and fil sqlclienttests

* Make OpenTelemetrySDK disposable and take care of disposing all ds subscriptions.

* Added OpenTelemetry.Default instead of static method

* AspNet, AspNetCore fix Dispose issue

* stylecop stuff lost i merge
  • Loading branch information
cijothomas committed Jun 11, 2020
1 parent 8ed37a6 commit bb4480e
Show file tree
Hide file tree
Showing 42 changed files with 863 additions and 784 deletions.
17 changes: 2 additions & 15 deletions samples/Exporters/AspNet/Global.asax.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,9 @@ public class WebApiApplication : HttpApplication

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";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@
<PackageReference Include="Microsoft.AspNet.WebApi.WebHost" Version="5.2.7" />
<PackageReference Include="Microsoft.AspNet.Mvc" Version="5.2.7" />
<PackageReference Include="Microsoft.AspNet.WebPages" Version="3.2.7" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe">
<Version>5.0.0-preview.4.20251.6</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\OpenTelemetry.Api\OpenTelemetry.Api.csproj">
Expand Down
4 changes: 4 additions & 0 deletions samples/Exporters/AspNet/Web.config
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
<assemblyIdentity name="System.Memory" publicKeyToken="CC7B13FFCD2DDD51" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.0.1.1" newVersion="4.0.1.1"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="CC7B13FFCD2DDD51" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0"/>
Expand Down
2 changes: 1 addition & 1 deletion samples/Exporters/Console/TestConsoleActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ internal static object Run(ConsoleActivityOptions options)
{
// 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));

Expand Down
14 changes: 8 additions & 6 deletions samples/Exporters/Console/TestHttpClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
// limitations under the License.
// </copyright>
using System;
using System.Diagnostics;
using System.Net.Http;
using OpenTelemetry.Trace;
using OpenTelemetry.Exporter.Console;
using OpenTelemetry.Trace.Configuration;

namespace Samples
Expand All @@ -26,12 +27,13 @@ internal static object Run()
{
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();
Expand Down
2 changes: 1 addition & 1 deletion samples/Exporters/Console/TestJaeger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ internal static object RunWithActivity(string host, int port)
{
// 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")
Expand Down
4 changes: 2 additions & 2 deletions samples/Exporters/Console/TestOtlp.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// <copyright file="TestOtlp.cs" company="OpenTelemetry Authors">
// <copyright file="TestOtlp.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -62,7 +62,7 @@ private static object RunWithActivitySource(string endpoint)
{
// 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")
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -14,9 +16,12 @@ public class WeatherForecastController : ControllerBase
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

private static HttpClient httpClient = new HttpClient();

[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var res = httpClient.GetStringAsync("http://google.com").Result;
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Expand Down
3 changes: 2 additions & 1 deletion samples/Exporters/Web/OpenTelemetry.Exporter.Web.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\OpenTelemetry.Exporter.Console\OpenTelemetry.Exporter.Console.csproj" />
<ProjectReference Include="..\..\..\src\OpenTelemetry.Exporter.Jaeger\OpenTelemetry.Exporter.Jaeger.csproj" />
<ProjectReference Include="..\..\..\src\OpenTelemetry.Instrumentation.AspNetCore\OpenTelemetry.Instrumentation.AspNetCore.csproj" />
<ProjectReference Include="..\..\..\src\OpenTelemetry.Instrumentation.Dependencies\OpenTelemetry.Instrumentation.Dependencies.csproj" />
<ProjectReference Include="..\..\..\src\OpenTelemetry.Exporter.Zipkin\OpenTelemetry.Exporter.Zipkin.csproj" />
<ProjectReference Include="..\..\..\src\OpenTelemetry.Extensions.Hosting\OpenTelemetry.Extensions.Hosting.csproj" />
</ItemGroup>

Expand Down
22 changes: 10 additions & 12 deletions samples/Exporters/Web/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using OpenTelemetry.Exporter.Console;
using OpenTelemetry.Trace.Configuration;

namespace API
Expand Down Expand Up @@ -36,18 +37,15 @@ public void ConfigureServices(IServiceCollection services)
}
});

services.AddOpenTelemetry((sp, builder) =>
{
builder
//.SetSampler(Samplers.AlwaysSample)
.UseZipkin(options =>
{
options.ServiceName = "test-zipkin";
options.Endpoint = new Uri(this.Configuration.GetValue<string>("Zipkin:Endpoint"));
})
.AddRequestInstrumentation()
.AddDependencyInstrumentation();
});
OpenTelemetrySdk.Default.EnableOpenTelemetry(
(builder) => builder.AddRequestInstrumentation().AddDependencyInstrumentation()
.UseJaegerActivityExporter(o =>
{
o.ServiceName = this.Configuration.GetValue<string>("Jaeger:ServiceName");
o.AgentHost = this.Configuration.GetValue<string>("Jaeger:Host");
o.AgentPort = this.Configuration.GetValue<int>("Jaeger:Port");
})
);
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
Expand Down
6 changes: 4 additions & 2 deletions samples/Exporters/Web/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
}
},
"AllowedHosts": "*",
"Zipkin": {
"Endpoint": "http://localhost:9411/api/v2/spans"
"Jaeger": {
"ServiceName": "jaeger-test",
"Host": "localhost",
"Port": 6831
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ public void ConfigureServices(IServiceCollection services)
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;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
namespace OpenTelemetry.Instrumentation.AspNet
{
/// <summary>
/// Requests instrumentation.
/// Asp.Net Requests instrumentation.
/// </summary>
public class AspNetInstrumentation : IDisposable
{
Expand All @@ -31,21 +31,19 @@ public class AspNetInstrumentation : IDisposable
/// <summary>
/// Initializes a new instance of the <see cref="AspNetInstrumentation"/> class.
/// </summary>
/// <param name="tracer">Tracer to record traced with.</param>
public AspNetInstrumentation(Tracer tracer)
: this(tracer, new AspNetInstrumentationOptions())
public AspNetInstrumentation()
: this(new AspNetInstrumentationOptions())
{
}

/// <summary>
/// Initializes a new instance of the <see cref="AspNetInstrumentation"/> class.
/// </summary>
/// <param name="tracer">Tracer to record traced with.</param>
/// <param name="options">Configuration options for ASP.NET instrumentation.</param>
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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,22 @@
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));
}
Expand All @@ -48,53 +53,75 @@ public override void OnStartActivity(Activity activity, object payload)

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;

TelemetrySpan span;
if (this.options.TextFormat is TraceContextFormat)
activity.DisplayName = path;

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<HttpRequest>(
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)
Expand All @@ -104,8 +131,10 @@ public override void OnStopActivity(Activity activity, object payload)
}

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;

Expand All @@ -131,14 +160,11 @@ public override void OnStopActivity(Activity activity, object payload)

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

0 comments on commit bb4480e

Please sign in to comment.