Skip to content

Commit

Permalink
New design for TelemetryHttpModule using ActivitySource + OpenTelemet…
Browse files Browse the repository at this point in the history
…ry.API part 3 (#2256)

* Update ASP.NET instrumentation to use the new TelemetryHttpModule.

* Fixed TelemetryHttpModule not starting its Activity objects. Added an example of request suppression.

* Tweaks an logging improvements.

* Sealed AspNetInstrumentationEventSource.

* Code review.
  • Loading branch information
CodeBlanch authored Aug 16, 2021
1 parent a012493 commit 8b1fd83
Show file tree
Hide file tree
Showing 16 changed files with 268 additions and 245 deletions.
3 changes: 2 additions & 1 deletion examples/AspNet/Examples.AspNet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
</Compile>
<Compile Include="Models\WeatherForecast.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SuppressInstrumentationHttpModule.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\_ViewStart.cshtml" />
Expand Down Expand Up @@ -149,4 +150,4 @@
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
</Project>
</Project>
58 changes: 58 additions & 0 deletions examples/AspNet/SuppressInstrumentationHttpModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// <copyright file="SuppressInstrumentationHttpModule.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

using System;
using System.Web;
using OpenTelemetry;

namespace Examples.AspNet
{
/// <summary>
/// A demo <see cref="IHttpModule"/> which will suppress ASP.NET
/// instrumentation if a request contains "suppress=true" on the query
/// string. Suppressed spans will not be processed/exported by the
/// OpenTelemetry SDK.
/// </summary>
public class SuppressInstrumentationHttpModule : IHttpModule
{
private IDisposable suppressionScope;

public void Init(HttpApplication context)
{
context.BeginRequest += this.Application_BeginRequest;
context.EndRequest += this.Application_EndRequest;
}

public void Dispose()
{
}

private void Application_BeginRequest(object sender, EventArgs e)
{
var context = ((HttpApplication)sender).Context;

if (context.Request.QueryString["suppress"] == "true")
{
this.suppressionScope = SuppressInstrumentationScope.Begin();
}
}

private void Application_EndRequest(object sender, EventArgs e)
{
this.suppressionScope?.Dispose();
}
}
}
13 changes: 5 additions & 8 deletions examples/AspNet/Web.config
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0"/>
</handlers>
<modules>
<add name="SuppressInstrumentationHttpModule" type="Examples.AspNet.SuppressInstrumentationHttpModule" preCondition="integratedMode,managedHandler"/>
<add name="TelemetryHttpModule" type="OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule, OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule" preCondition="integratedMode,managedHandler"/>
</modules>
</system.webServer>
Expand All @@ -37,20 +38,16 @@
<bindingRedirect oldVersion="0.0.0.0-4.2.0.1" newVersion="4.2.0.1"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Buffers" publicKeyToken="CC7B13FFCD2DDD51" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0"/>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="B03F5F7F11D50A3A" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<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.1"/>
<assemblyIdentity name="System.Buffers" publicKeyToken="CC7B13FFCD2DDD51" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ const OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.AspNetSourceName
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.Dispose() -> void
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.Init(System.Web.HttpApplication context) -> void
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.OnExceptionCallback.get -> System.Action<System.Diagnostics.Activity, System.Exception>
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.OnExceptionCallback.set -> void
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.OnRequestStartedCallback.get -> System.Action<System.Diagnostics.Activity, System.Web.HttpContext>
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.OnRequestStartedCallback.set -> void
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.OnRequestStoppedCallback.get -> System.Action<System.Diagnostics.Activity, System.Web.HttpContext>
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.OnRequestStoppedCallback.set -> void
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.TelemetryHttpModule() -> void
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.TextMapPropagator.get -> OpenTelemetry.Context.Propagation.TraceContextPropagator
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.TextMapPropagator.set -> void
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModuleOptions
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModuleOptions.OnExceptionCallback.get -> System.Action<System.Diagnostics.Activity, System.Web.HttpContext, System.Exception>
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModuleOptions.OnExceptionCallback.set -> void
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModuleOptions.OnRequestStartedCallback.get -> System.Action<System.Diagnostics.Activity, System.Web.HttpContext>
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModuleOptions.OnRequestStartedCallback.set -> void
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModuleOptions.OnRequestStoppedCallback.get -> System.Action<System.Diagnostics.Activity, System.Web.HttpContext>
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModuleOptions.OnRequestStoppedCallback.set -> void
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModuleOptions.TextMapPropagator.get -> OpenTelemetry.Context.Propagation.TextMapPropagator
OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModuleOptions.TextMapPropagator.set -> void
static OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModule.Options.get -> OpenTelemetry.Instrumentation.AspNet.TelemetryHttpModuleOptions
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ internal static class ActivityHelper
/// </summary>
private const string ActivityKey = "__AspnetActivity__";

private static readonly ActivitySource AspNetSource = new ActivitySource(TelemetryHttpModule.AspNetSourceName);
private static readonly Version Version = typeof(ActivityHelper).Assembly.GetName().Version;
private static readonly ActivitySource AspNetSource = new ActivitySource(TelemetryHttpModule.AspNetSourceName, Version.ToString());
private static readonly Func<HttpRequest, string, IEnumerable<string>> HttpRequestHeaderValuesGetter = (request, name) => request.Headers.GetValues(name);
private static readonly object StartedButNotSampledObj = new object();

Expand Down Expand Up @@ -71,7 +72,7 @@ public static Activity StartAspNetActivity(TextMapPropagator textMapPropagator,
{
PropagationContext propagationContext = textMapPropagator.Extract(default, context.Request, HttpRequestHeaderValuesGetter);

Activity activity = AspNetSource.CreateActivity(TelemetryHttpModule.AspNetActivityName, ActivityKind.Server, propagationContext.ActivityContext);
Activity activity = AspNetSource.StartActivity(TelemetryHttpModule.AspNetActivityName, ActivityKind.Server, propagationContext.ActivityContext);

if (activity != null)
{
Expand Down Expand Up @@ -147,13 +148,13 @@ public static void StopAspNetActivity(Activity aspNetActivity, HttpContext conte
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteActivityException(Activity aspNetActivity, Exception exception, Action<Activity, Exception> onExceptionCallback)
public static void WriteActivityException(Activity aspNetActivity, HttpContext context, Exception exception, Action<Activity, HttpContext, Exception> onExceptionCallback)
{
if (aspNetActivity != null)
{
try
{
onExceptionCallback?.Invoke(aspNetActivity, exception);
onExceptionCallback?.Invoke(aspNetActivity, context, exception);
}
catch (Exception callbackEx)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using System;
using System.Diagnostics;
using System.Diagnostics.Tracing;
using OpenTelemetry.Internal;

namespace OpenTelemetry.Instrumentation.AspNet
{
Expand Down Expand Up @@ -63,7 +64,7 @@ public void ActivityException(Activity activity, Exception ex)
{
if (this.IsEnabled(EventLevel.Error, EventKeywords.All))
{
this.ActivityException(activity?.Id, ex.ToString());
this.ActivityException(activity?.Id, ex.ToInvariantString());
}
}

Expand All @@ -72,7 +73,7 @@ public void CallbackException(Activity activity, string eventName, Exception ex)
{
if (this.IsEnabled(EventLevel.Error, EventKeywords.All))
{
this.CallbackException(activity?.Id, eventName, ex.ToString());
this.CallbackException(activity?.Id, eventName, ex.ToInvariantString());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
<RunApiCompat>false</RunApiCompat>
</PropertyGroup>

<ItemGroup>
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\ExceptionExtensions.cs" Link="Includes\ExceptionExtensions.cs" />
</ItemGroup>

<ItemGroup>
<Reference Include="System.Web" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@
// </copyright>

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using System.Web;
using OpenTelemetry.Context.Propagation;

namespace OpenTelemetry.Instrumentation.AspNet
{
Expand All @@ -46,37 +44,10 @@ public class TelemetryHttpModule : IHttpModule

private static readonly MethodInfo OnStepMethodInfo = typeof(HttpApplication).GetMethod("OnExecuteRequestStep");

private TraceContextPropagator traceContextPropagator = new TraceContextPropagator();

/// <summary>
/// Gets or sets the <see cref="TraceContextPropagator"/> to use to
/// extract <see cref="PropagationContext"/> from incoming requests.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public TraceContextPropagator TextMapPropagator
{
get => this.traceContextPropagator;
set => this.traceContextPropagator = value ?? throw new ArgumentNullException(nameof(value));
}

/// <summary>
/// Gets or sets a callback action to be fired when a request is started.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public Action<Activity, HttpContext> OnRequestStartedCallback { get; set; }

/// <summary>
/// Gets or sets a callback action to be fired when a request is stopped.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public Action<Activity, HttpContext> OnRequestStoppedCallback { get; set; }

/// <summary>
/// Gets or sets a callback action to be fired when an unhandled
/// exception is thrown processing a request.
/// Gets the <see cref="TelemetryHttpModuleOptions"/> applied to requests processed by the handler.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public Action<Activity, Exception> OnExceptionCallback { get; set; }
public static TelemetryHttpModuleOptions Options { get; } = new TelemetryHttpModuleOptions();

/// <inheritdoc />
public void Dispose()
Expand Down Expand Up @@ -136,7 +107,7 @@ private void Application_BeginRequest(object sender, EventArgs e)
{
var context = ((HttpApplication)sender).Context;
AspNetTelemetryEventSource.Log.TraceCallback("Application_BeginRequest");
ActivityHelper.StartAspNetActivity(this.TextMapPropagator, context, this.OnRequestStartedCallback);
ActivityHelper.StartAspNetActivity(Options.TextMapPropagator, context, Options.OnRequestStartedCallback);
}

private void Application_PreRequestHandlerExecute(object sender, EventArgs e)
Expand Down Expand Up @@ -168,13 +139,13 @@ private void Application_EndRequest(object sender, EventArgs e)
else
{
// Activity has never been started
aspNetActivity = ActivityHelper.StartAspNetActivity(this.TextMapPropagator, context, this.OnRequestStartedCallback);
aspNetActivity = ActivityHelper.StartAspNetActivity(Options.TextMapPropagator, context, Options.OnRequestStartedCallback);
}
}

if (trackActivity)
{
ActivityHelper.StopAspNetActivity(aspNetActivity, context, this.OnRequestStoppedCallback);
ActivityHelper.StopAspNetActivity(aspNetActivity, context, Options.OnRequestStoppedCallback);
}
}

Expand All @@ -189,10 +160,10 @@ private void Application_Error(object sender, EventArgs e)
{
if (!ActivityHelper.HasStarted(context, out Activity aspNetActivity))
{
aspNetActivity = ActivityHelper.StartAspNetActivity(this.TextMapPropagator, context, this.OnRequestStartedCallback);
aspNetActivity = ActivityHelper.StartAspNetActivity(Options.TextMapPropagator, context, Options.OnRequestStartedCallback);
}

ActivityHelper.WriteActivityException(aspNetActivity, exception, this.OnExceptionCallback);
ActivityHelper.WriteActivityException(aspNetActivity, context, exception, Options.OnExceptionCallback);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// <copyright file="TelemetryHttpModuleOptions.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

using System;
using System.Diagnostics;
using System.Web;
using OpenTelemetry.Context.Propagation;

namespace OpenTelemetry.Instrumentation.AspNet
{
/// <summary>
/// Stores options for the <see cref="TelemetryHttpModule"/>.
/// </summary>
public class TelemetryHttpModuleOptions
{
private TextMapPropagator textMapPropagator = new TraceContextPropagator();

internal TelemetryHttpModuleOptions()
{
}

/// <summary>
/// Gets or sets the <see cref=" Context.Propagation.TextMapPropagator"/> to use to
/// extract <see cref="PropagationContext"/> from incoming requests.
/// </summary>
public TextMapPropagator TextMapPropagator
{
get => this.textMapPropagator;
set => this.textMapPropagator = value ?? throw new ArgumentNullException(nameof(value));
}

/// <summary>
/// Gets or sets a callback action to be fired when a request is started.
/// </summary>
public Action<Activity, HttpContext> OnRequestStartedCallback { get; set; }

/// <summary>
/// Gets or sets a callback action to be fired when a request is stopped.
/// </summary>
public Action<Activity, HttpContext> OnRequestStoppedCallback { get; set; }

/// <summary>
/// Gets or sets a callback action to be fired when an unhandled
/// exception is thrown processing a request.
/// </summary>
public Action<Activity, HttpContext, Exception> OnExceptionCallback { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ OpenTelemetry.Instrumentation.AspNet.AspNetInstrumentationOptions.Enrich.get ->
OpenTelemetry.Instrumentation.AspNet.AspNetInstrumentationOptions.Enrich.set -> void
OpenTelemetry.Instrumentation.AspNet.AspNetInstrumentationOptions.Filter.get -> System.Func<System.Web.HttpContext, bool>
OpenTelemetry.Instrumentation.AspNet.AspNetInstrumentationOptions.Filter.set -> void
OpenTelemetry.Instrumentation.AspNet.AspNetInstrumentationOptions.RecordException.get -> bool
OpenTelemetry.Instrumentation.AspNet.AspNetInstrumentationOptions.RecordException.set -> void
OpenTelemetry.Trace.TracerProviderBuilderExtensions
static OpenTelemetry.Trace.TracerProviderBuilderExtensions.AddAspNetInstrumentation(this OpenTelemetry.Trace.TracerProviderBuilder builder, System.Action<OpenTelemetry.Instrumentation.AspNet.AspNetInstrumentationOptions> configureAspNetInstrumentationOptions = null) -> OpenTelemetry.Trace.TracerProviderBuilder
Loading

0 comments on commit 8b1fd83

Please sign in to comment.