-
Notifications
You must be signed in to change notification settings - Fork 0
/
OperationCorrelationTelemetryInitializer.cs
150 lines (133 loc) · 5.92 KB
/
OperationCorrelationTelemetryInitializer.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
148
149
150
namespace WcfApplicationInsights
{
using System;
using Microsoft.ApplicationInsights.Channel;
using WcfApplicationInsights.Implementation;
/// <summary>
/// A telemetry initializer that will set the correlation context for all telemetry items in web application.
/// </summary>
public sealed class OperationCorrelationTelemetryInitializer : WcfTelemetryInitializer
{
private const string RequestHeadersChecked = "OCTI_RequestHeadersChecked";
private static readonly object Checked = new object();
/// <summary>
/// Initializes a new instance of the <see cref="OperationCorrelationTelemetryInitializer"/> class.
/// </summary>
public OperationCorrelationTelemetryInitializer()
{
this.RootOperationIdHeaderName = CorrelationHeaders.HttpStandardRootIdHeader;
this.ParentOperationIdHeaderName = CorrelationHeaders.HttpStandardParentIdHeader;
this.SoapHeaderNamespace = CorrelationHeaders.SoapStandardNamespace;
this.SoapParentOperationIdHeaderName = CorrelationHeaders.SoapStandardParentIdHeader;
this.SoapRootOperationIdHeaderName = CorrelationHeaders.SoapStandardRootIdHeader;
}
/// <summary>
/// Gets or sets the name of the HTTP header to get root operation Id from.
/// </summary>
public string RootOperationIdHeaderName { get; set; }
/// <summary>
/// Gets or sets the name of the HTTP header to get parent operation Id from.
/// </summary>
public string ParentOperationIdHeaderName { get; set; }
/// <summary>
/// Gets or sets the name of the SOAP header to get root operation Id from.
/// </summary>
public string SoapRootOperationIdHeaderName { get; set; }
/// <summary>
/// Gets or sets the name of the SOAP header to get parent operation Id from.
/// </summary>
public string SoapParentOperationIdHeaderName { get; set; }
/// <summary>
/// Gets or sets the XML Namespace for the root/parent operation ID SOAP headers.
/// </summary>
public string SoapHeaderNamespace { get; set; }
/// <summary>
/// Initialize the telemetry event.
/// </summary>
/// <param name="telemetry">The telemetry event.</param>
/// <param name="operation">WCF operation context.</param>
protected override void OnInitialize(ITelemetry telemetry, IOperationContext operation)
{
var parentContext = operation.Request.Context.Operation;
// if the parent operation ID is specified in the header
// set it on the current request
if (string.IsNullOrEmpty(parentContext.ParentId))
{
if (!string.IsNullOrEmpty(this.ParentOperationIdHeaderName))
{
var parentId = this.GetHeader(operation, this.ParentOperationIdHeaderName, this.SoapParentOperationIdHeaderName);
if (!string.IsNullOrEmpty(parentId))
{
parentContext.ParentId = parentId;
}
}
}
// if the root operation ID is specified in the header
// set it on the current request
if (string.IsNullOrEmpty(parentContext.Id))
{
if (!string.IsNullOrWhiteSpace(this.RootOperationIdHeaderName))
{
var rootId = this.GetHeader(operation, this.RootOperationIdHeaderName, this.SoapRootOperationIdHeaderName);
if (!string.IsNullOrEmpty(rootId))
{
parentContext.Id = rootId;
}
}
// if the root ID has not been set, set it now
if (string.IsNullOrEmpty(parentContext.Id))
{
parentContext.Id = operation.Request.Id;
}
}
if (telemetry != operation.Request)
{
// tie the current telemetry event to the parent request
if (string.IsNullOrEmpty(telemetry.Context.Operation.ParentId))
{
telemetry.Context.Operation.ParentId = operation.Request.Id;
}
if (string.IsNullOrEmpty(telemetry.Context.Operation.Id))
{
telemetry.Context.Operation.Id = parentContext.Id;
}
}
else
{
// we've initialized the correlation headers for
// this request object. We want to initialize
// all other telemetry events from this request
// without checking the WCF message headers
// ever again, as this can trigger ObjectDisposedException
// errors when TelemetryClient.Initialize() is called
// at the end of the request
((IOperationContextState)operation).SetState(RequestHeadersChecked, Checked);
}
}
private string GetHeader(IOperationContext context, string httpHeader, string soapHeader)
{
if (this.RequestAlreadyChecked(context))
{
return null;
}
var httpHeaders = context.GetHttpRequestHeaders();
if (httpHeaders != null)
{
return httpHeaders.Headers[httpHeader];
}
else
{
return context.GetIncomingMessageHeader<string>(soapHeader, this.SoapHeaderNamespace);
}
}
private bool RequestAlreadyChecked(IOperationContext context)
{
object checkedValue = null;
if (((IOperationContextState)context).TryGetState(RequestHeadersChecked, out checkedValue))
{
return checkedValue == Checked;
}
return false;
}
}
}