Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Instrumentation Adapters to support Activity API #701

Merged
merged 33 commits into from
Jun 11, 2020
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
831d712
use the same activity created by existing instrumentation. Simply enh…
cijothomas May 26, 2020
737f9b6
Merge branch 'master' into cijothomas/activity3adapter1a_alternate
cijothomas Jun 1, 2020
9b840f0
remove sampling from instrumentation
cijothomas Jun 2, 2020
036a9bb
Merge branch 'master' into cijothomas/activity3adapter1a_alternate
cijothomas Jun 2, 2020
5b836a4
Merge branch 'cijothomas/activity3adapter1a_alternate' of https://git…
cijothomas Jun 2, 2020
382d85f
Merge branch 'master' into cijothomas/activity3adapter1a_alternate
cijothomas Jun 2, 2020
f1e5108
add asp.net core instrumentation
cijothomas Jun 2, 2020
170ad87
Merge branch 'cijothomas/activity3adapter1a_alternate' of https://git…
cijothomas Jun 2, 2020
0832e71
Merge branch 'master' into cijothomas/activity3adapter1a_alternate
cijothomas Jun 2, 2020
3b4cbb7
Merge branch 'master' into cijothomas/activity3adapter1a_alternate
cijothomas Jun 2, 2020
19d138f
move sampling to Instrumentation for now.
cijothomas Jun 4, 2020
121e4b5
Merge branch 'master' into cijothomas/activity3adapter1a_alternate
cijothomas Jun 4, 2020
823fee6
Add httpClient .net core instrumentation
cijothomas Jun 4, 2020
c759d8a
Ad SqlClientInstrumentation
cijothomas Jun 4, 2020
6df93de
remove sqlclientinstrumentation from previous instrumentation
cijothomas Jun 4, 2020
0d07263
Quick implementation for AzureClients - not validated as there are un…
cijothomas Jun 5, 2020
692b0dc
fix examples
cijothomas Jun 5, 2020
7185426
Merge branch 'master' into cijothomas/activity3adapter1a_alternate
cijothomas Jun 5, 2020
37071c5
fix sampling flag
cijothomas Jun 8, 2020
b889a8c
Merge branch 'cijothomas/activity3adapter1a_alternate' of https://git…
cijothomas Jun 8, 2020
8630782
made sample app work with jaeger
cijothomas Jun 8, 2020
c4834ae
Mark todos and fix AspNet tests
cijothomas Jun 8, 2020
581cd1e
Fix asp.net core tests and mark TODOs
cijothomas Jun 8, 2020
7341c59
Add TODO for httpclient .net core test and fix test
cijothomas Jun 8, 2020
d9c9fff
add todo and fix httpclient test
cijothomas Jun 9, 2020
514d89c
add todos and fil sqlclienttests
cijothomas Jun 9, 2020
f939361
Make OpenTelemetrySDK disposable and take care of disposing all ds su…
cijothomas Jun 9, 2020
a220d9e
Added OpenTelemetry.Default instead of static method
cijothomas Jun 9, 2020
99b91bf
Merge branch 'master' into cijothomas/activity3adapter1a_alternate
cijothomas Jun 9, 2020
63dc16f
conflict resolve
cijothomas Jun 9, 2020
11a175a
AspNet, AspNetCore fix Dispose issue
cijothomas Jun 9, 2020
efdb1d3
conflict
cijothomas Jun 10, 2020
3646fa1
stylecop stuff lost i merge
cijothomas Jun 10, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this is only for tests and not part of the final merge.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I can remove that. The testhttpClient example was doing something similar.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To provide a little history, when I was building the .NET Framework instrumentation I added in those calls so I could do integration testing, basically. Make sure incoming & outgoing requests would show up in Jaeger/Zipkin correctly. In the end it made the samples more interesting so I left them in. 🤷‍♂️

Copy link
Contributor

@pjanotti pjanotti Jun 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, what about adding a comment stating that it exists to also include outgoing on the sample?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes adding a comment.

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" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will create the Activity exporter for Zipkin this week or the next.

<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 =>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved to jaeger as zipkin exporter with activity is not yet ready

{
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",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note: it feels strange that we need to set Jaeger Host and Port to what it should be default values. Anyway, to be addressed in a separate PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The defaults look like they are in there:

public string AgentHost { get; set; } = "localhost";
/// <summary>
/// Gets or sets the Jaeger agent "compact thrift protocol" port. Default value: 6831.
/// </summary>
public int AgentPort { get; set; } = 6831;

But IMO it's still worthwhile to show how you can change them via settings in the samples.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's still worthwhile to show how you can change them via settings in the samples.

Makes sense, perhaps we should comment where appropriate so the code setting (again) the default values don't get copied and pasted everywhere.

"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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to the risk of high-cardinality on the path OTel spec suggests using the HTTP method instead. That said the path is much more useful in general and having an option to add it (more likely the absolute path) will be useful. There are other locations also using the path as DisplayName, but other correctly use the helper that gets the name per OTel spec. BTW, the link on the comment above is broken.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pjanotti We updated the outgoing calls (#633) but it looks like we didn't do incoming.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes incoming requests were still using path as SpanName, hence I used it as activity.DisplayName.
This shouldn't block this PR, as we can (and should) address this separate from this PR.


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