1
1
using System ;
2
+ using System . Collections ;
2
3
using System . Collections . Generic ;
3
4
using System . Reflection ;
4
5
using System . Text ;
6
+ using Datadog . Trace . Headers ;
7
+ using Datadog . Trace . Logging ;
5
8
6
9
namespace Datadog . Trace . ClrProfiler . Integrations
7
10
{
@@ -13,6 +16,8 @@ public sealed class AspNetCoreMvc2Integration : IDisposable
13
16
internal const string OperationName = "aspnet-coremvc.request" ;
14
17
private const string HttpContextKey = "__Datadog.Trace.ClrProfiler.Integrations." + nameof ( AspNetCoreMvc2Integration ) ;
15
18
19
+ private static readonly ILog Log = LogProvider . GetLogger ( typeof ( AspNetCoreMvc2Integration ) ) ;
20
+
16
21
private static Action < object , object , object , object > _beforeAction ;
17
22
private static Action < object , object , object , object > _afterAction ;
18
23
@@ -36,7 +41,33 @@ public AspNetCoreMvc2Integration(object actionDescriptorObj, object httpContextO
36
41
string httpMethod = _httpContext . Request . Method . ToUpperInvariant ( ) ;
37
42
string url = GetDisplayUrl ( _httpContext . Request ) . ToLowerInvariant ( ) ;
38
43
39
- _scope = Tracer . Instance . StartActive ( OperationName ) ;
44
+ SpanContext propagatedContext = null ;
45
+
46
+ if ( Tracer . Instance . ActiveScope == null )
47
+ {
48
+ try
49
+ {
50
+ // extract propagated http headers
51
+ IEnumerable requestHeaders = _httpContext . Request . Headers ;
52
+ int headerCount = _httpContext . Request . Headers . Count ;
53
+ var headersCollection = new DictionaryHeadersCollection ( headerCount ) ;
54
+
55
+ foreach ( dynamic header in requestHeaders )
56
+ {
57
+ string key = header . Key ;
58
+ string [ ] values = header . Value . ToArray ( ) ;
59
+ headersCollection . Add ( key , values ) ;
60
+ }
61
+
62
+ propagatedContext = SpanContextPropagator . Instance . Extract ( headersCollection ) ;
63
+ }
64
+ catch ( Exception ex )
65
+ {
66
+ Log . ErrorException ( "Error extracting propagated HTTP headers." , ex ) ;
67
+ }
68
+ }
69
+
70
+ _scope = Tracer . Instance . StartActive ( OperationName , propagatedContext ) ;
40
71
var span = _scope . Span ;
41
72
span . Type = SpanTypes . Web ;
42
73
span . ResourceName = $ "{ httpMethod } { controllerName } .{ actionName } ";
@@ -45,9 +76,10 @@ public AspNetCoreMvc2Integration(object actionDescriptorObj, object httpContextO
45
76
span . SetTag ( Tags . AspNetController , controllerName ) ;
46
77
span . SetTag ( Tags . AspNetAction , actionName ) ;
47
78
}
48
- catch
79
+ catch ( Exception ) when ( DisposeObject ( _scope ) )
49
80
{
50
- // TODO: logging
81
+ // unreachable code
82
+ throw ;
51
83
}
52
84
}
53
85
@@ -63,22 +95,25 @@ public AspNetCoreMvc2Integration(object actionDescriptorObj, object httpContextO
63
95
TargetAssembly = "Microsoft.AspNetCore.Mvc.Core" ,
64
96
TargetType = "Microsoft.AspNetCore.Mvc.Internal.MvcCoreDiagnosticSourceExtensions" ) ]
65
97
public static void BeforeAction (
66
- object diagnosticSource ,
67
- object actionDescriptor ,
68
- dynamic httpContext ,
69
- object routeData )
98
+ object diagnosticSource ,
99
+ object actionDescriptor ,
100
+ object httpContext ,
101
+ object routeData )
70
102
{
71
103
AspNetCoreMvc2Integration integration = null ;
72
104
73
105
try
74
106
{
75
107
integration = new AspNetCoreMvc2Integration ( actionDescriptor , httpContext ) ;
76
- IDictionary < object , object > contextItems = httpContext . Items ;
77
- contextItems [ HttpContextKey ] = integration ;
108
+
109
+ if ( httpContext . TryGetPropertyValue ( "Items" , out IDictionary < object , object > contextItems ) )
110
+ {
111
+ contextItems [ HttpContextKey ] = integration ;
112
+ }
78
113
}
79
- catch
114
+ catch ( Exception ex )
80
115
{
81
- // TODO: log this as an instrumentation error, but continue calling instrumented method
116
+ Log . ErrorExceptionForFilter ( $ "Error creating { nameof ( AspNetCoreMvc2Integration ) } ." , ex ) ;
82
117
}
83
118
84
119
try
@@ -89,25 +124,24 @@ public static void BeforeAction(
89
124
var type = assembly . GetType ( "Microsoft.AspNetCore.Mvc.Internal.MvcCoreDiagnosticSourceExtensions" ) ;
90
125
91
126
_beforeAction = DynamicMethodBuilder < Action < object , object , object , object > > . CreateMethodCallDelegate (
92
- type ,
93
- "BeforeAction" ) ;
127
+ type ,
128
+ "BeforeAction" ) ;
94
129
}
95
130
}
96
- catch
131
+ catch ( Exception ex )
97
132
{
98
- // TODO: log this as an instrumentation error, we cannot call instrumented method,
99
133
// profiled app will continue working without DiagnosticSource
134
+ Log . ErrorException ( "Error calling Microsoft.AspNetCore.Mvc.Internal.MvcCoreDiagnosticSourceExtensions.BeforeAction()" , ex ) ;
100
135
}
101
136
102
137
try
103
138
{
104
139
// call the original method, catching and rethrowing any unhandled exceptions
105
140
_beforeAction ? . Invoke ( diagnosticSource , actionDescriptor , httpContext , routeData ) ;
106
141
}
107
- catch ( Exception ex )
142
+ catch ( Exception ex ) when ( integration ? . SetException ( ex ) ?? false )
108
143
{
109
- integration ? . SetException ( ex ) ;
110
-
144
+ // unreachable code
111
145
throw ;
112
146
}
113
147
}
@@ -124,21 +158,23 @@ public static void BeforeAction(
124
158
TargetAssembly = "Microsoft.AspNetCore.Mvc.Core" ,
125
159
TargetType = "Microsoft.AspNetCore.Mvc.Internal.MvcCoreDiagnosticSourceExtensions" ) ]
126
160
public static void AfterAction (
127
- object diagnosticSource ,
128
- object actionDescriptor ,
129
- dynamic httpContext ,
130
- object routeData )
161
+ object diagnosticSource ,
162
+ object actionDescriptor ,
163
+ object httpContext ,
164
+ object routeData )
131
165
{
132
166
AspNetCoreMvc2Integration integration = null ;
133
167
134
168
try
135
169
{
136
- IDictionary < object , object > contextItems = httpContext ? . Items ;
137
- integration = contextItems ? [ HttpContextKey ] as AspNetCoreMvc2Integration ;
170
+ if ( httpContext . TryGetPropertyValue ( "Items" , out IDictionary < object , object > contextItems ) )
171
+ {
172
+ integration = contextItems ? [ HttpContextKey ] as AspNetCoreMvc2Integration ;
173
+ }
138
174
}
139
- catch
175
+ catch ( Exception ex )
140
176
{
141
- // TODO: log this as an instrumentation error, but continue calling instrumented method
177
+ Log . ErrorExceptionForFilter ( $ "Error accessing { nameof ( AspNetCoreMvc2Integration ) } ." , ex ) ;
142
178
}
143
179
144
180
try
@@ -148,8 +184,8 @@ public static void AfterAction(
148
184
var type = actionDescriptor . GetType ( ) . Assembly . GetType ( "Microsoft.AspNetCore.Mvc.Internal.MvcCoreDiagnosticSourceExtensions" ) ;
149
185
150
186
_afterAction = DynamicMethodBuilder < Action < object , object , object , object > > . CreateMethodCallDelegate (
151
- type ,
152
- "AfterAction" ) ;
187
+ type ,
188
+ "AfterAction" ) ;
153
189
}
154
190
}
155
191
catch
@@ -179,9 +215,11 @@ public static void AfterAction(
179
215
/// Tags the current span as an error. Called when an unhandled exception is thrown in the instrumented method.
180
216
/// </summary>
181
217
/// <param name="ex">The exception that was thrown and not handled in the instrumented method.</param>
182
- public void SetException ( Exception ex )
218
+ /// <returns>Always <c>false</c>.</returns>
219
+ public bool SetException ( Exception ex )
183
220
{
184
221
_scope ? . Span ? . SetException ( ex ) ;
222
+ return false ;
185
223
}
186
224
187
225
/// <summary>
@@ -217,5 +255,11 @@ private static string GetDisplayUrl(dynamic request)
217
255
. Append ( queryString )
218
256
. ToString ( ) ;
219
257
}
258
+
259
+ private bool DisposeObject ( IDisposable disposable )
260
+ {
261
+ disposable ? . Dispose ( ) ;
262
+ return false ;
263
+ }
220
264
}
221
265
}
0 commit comments