-
Notifications
You must be signed in to change notification settings - Fork 786
/
Copy pathTelemetryHttpModule.cs
147 lines (125 loc) · 6.71 KB
/
TelemetryHttpModule.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// <copyright file="TelemetryHttpModule.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.Reflection;
using System.Web;
namespace OpenTelemetry.Instrumentation.AspNet
{
/// <summary>
/// Http Module sets ambient state using Activity API from DiagnosticsSource package.
/// </summary>
public class TelemetryHttpModule : IHttpModule
{
/// <summary>
/// OpenTelemetry.Instrumentation.AspNet <see cref="ActivitySource"/> name.
/// </summary>
public const string AspNetSourceName = "OpenTelemetry.Instrumentation.AspNet.Telemetry";
/// <summary>
/// <see cref="Activity.OperationName"/> for OpenTelemetry.Instrumentation.AspNet created <see cref="Activity"/> objects.
/// </summary>
public const string AspNetActivityName = "Microsoft.AspNet.HttpReqIn";
// ServerVariable set only on rewritten HttpContext by URL Rewrite module.
private const string URLRewriteRewrittenRequest = "IIS_WasUrlRewritten";
// ServerVariable set on every request if URL module is registered in HttpModule pipeline.
private const string URLRewriteModuleVersion = "IIS_UrlRewriteModule";
private static readonly MethodInfo OnExecuteRequestStepMethodInfo = typeof(HttpApplication).GetMethod("OnExecuteRequestStep");
/// <summary>
/// Gets the <see cref="TelemetryHttpModuleOptions"/> applied to requests processed by the handler.
/// </summary>
public static TelemetryHttpModuleOptions Options { get; } = new TelemetryHttpModuleOptions();
/// <inheritdoc />
public void Dispose()
{
}
/// <inheritdoc />
public void Init(HttpApplication context)
{
context.BeginRequest += this.Application_BeginRequest;
context.EndRequest += this.Application_EndRequest;
context.Error += this.Application_Error;
if (HttpRuntime.UsingIntegratedPipeline && OnExecuteRequestStepMethodInfo != null)
{
// OnExecuteRequestStep is availabile starting with 4.7.1
try
{
OnExecuteRequestStepMethodInfo.Invoke(context, new object[] { (Action<HttpContextBase, Action>)this.OnExecuteRequestStep });
}
catch (Exception e)
{
AspNetTelemetryEventSource.Log.OnExecuteRequestStepInvocationError(e.Message);
}
}
}
private void Application_BeginRequest(object sender, EventArgs e)
{
AspNetTelemetryEventSource.Log.TraceCallback("Application_BeginRequest");
ActivityHelper.StartAspNetActivity(Options.TextMapPropagator, ((HttpApplication)sender).Context, Options.OnRequestStartedCallback);
}
private void OnExecuteRequestStep(HttpContextBase context, Action step)
{
// Called only on 4.7.1+ runtimes
if (context.CurrentNotification == RequestNotification.ExecuteRequestHandler && !context.IsPostNotification)
{
ActivityHelper.RestoreContextIfNeeded(context.ApplicationInstance.Context);
}
step();
}
private void Application_EndRequest(object sender, EventArgs e)
{
AspNetTelemetryEventSource.Log.TraceCallback("Application_EndRequest");
bool trackActivity = true;
var context = ((HttpApplication)sender).Context;
if (!ActivityHelper.HasStarted(context, out Activity aspNetActivity))
{
// Rewrite: In case of rewrite, a new request context is created, called the child request, and it goes through the entire IIS/ASP.NET integrated pipeline.
// The child request can be mapped to any of the handlers configured in IIS, and it's execution is no different than it would be if it was received via the HTTP stack.
// The parent request jumps ahead in the pipeline to the end request notification, and waits for the child request to complete.
// When the child request completes, the parent request executes the end request notifications and completes itself.
// Do not create activity for parent request. Parent request has IIS_UrlRewriteModule ServerVariable with success response code.
// Child request contains an additional ServerVariable named - IIS_WasUrlRewritten.
// Track failed response activity: Different modules in the pipeline has ability to end the response. For example, authentication module could set HTTP 401 in OnBeginRequest and end the response.
if (context.Request.ServerVariables != null && context.Request.ServerVariables[URLRewriteRewrittenRequest] == null && context.Request.ServerVariables[URLRewriteModuleVersion] != null && context.Response.StatusCode == 200)
{
trackActivity = false;
}
else
{
// Activity has never been started
aspNetActivity = ActivityHelper.StartAspNetActivity(Options.TextMapPropagator, context, Options.OnRequestStartedCallback);
}
}
if (trackActivity)
{
ActivityHelper.StopAspNetActivity(Options.TextMapPropagator, aspNetActivity, context, Options.OnRequestStoppedCallback);
}
}
private void Application_Error(object sender, EventArgs e)
{
AspNetTelemetryEventSource.Log.TraceCallback("Application_Error");
var context = ((HttpApplication)sender).Context;
var exception = context.Error;
if (exception != null)
{
if (!ActivityHelper.HasStarted(context, out Activity aspNetActivity))
{
aspNetActivity = ActivityHelper.StartAspNetActivity(Options.TextMapPropagator, context, Options.OnRequestStartedCallback);
}
ActivityHelper.WriteActivityException(aspNetActivity, context, exception, Options.OnExceptionCallback);
}
}
}
}