3
3
using Google . Protobuf . WellKnownTypes ;
4
4
using Google . Protobuf ;
5
5
using Microsoft . AspNetCore . Http . Extensions ;
6
+ using Microsoft . AspNetCore . Http ;
7
+ using Microsoft . AspNetCore . Routing ;
6
8
using NUnit . Framework ;
7
9
using Newtonsoft . Json . Linq ;
8
10
using Newtonsoft . Json ;
9
11
using System . Diagnostics ;
10
12
using System . Text . Json ;
11
- using Microsoft . AspNetCore . Http ;
12
- using Microsoft . AspNetCore . Routing ;
13
-
14
-
13
+ using static System . Web . HttpUtility ;
15
14
16
15
namespace ApiToolkit . Net
17
16
{
@@ -35,6 +34,9 @@ public async Task InvokeAsync(HttpContext context)
35
34
var responseBodyStream = new MemoryStream ( ) ;
36
35
var originalResponseBodyStream = context . Response . Body ;
37
36
context . Response . Body = responseBodyStream ;
37
+ Guid uuid = Guid . NewGuid ( ) ;
38
+ var msg_id = uuid . ToString ( ) ;
39
+ context . Items [ "APITOOLKIT_MSG_ID" ] = msg_id ;
38
40
39
41
try
40
42
{
@@ -76,7 +78,7 @@ public async Task InvokeAsync(HttpContext context)
76
78
}
77
79
var payload = _client . BuildPayload ( "DotNet" , stopwatch , context . Request , context . Response . StatusCode ,
78
80
System . Text . Encoding . UTF8 . GetBytes ( requestBody ) , System . Text . Encoding . UTF8 . GetBytes ( responseBody ) ,
79
- responseHeaders , pathParams , urlPath , errors ) ;
81
+ responseHeaders , pathParams , urlPath , errors , msg_id ) ;
80
82
81
83
await _client . PublishMessageAsync ( payload ) ;
82
84
}
@@ -170,9 +172,9 @@ await PubSubClient.PublishAsync(new PubsubMessage
170
172
}
171
173
}
172
174
173
- public ObservingHandler APIToolkitObservingHandler ( HttpContext context )
175
+ public ObservingHandler APIToolkitObservingHandler ( HttpContext context , ATOptions ? options = null )
174
176
{
175
- return new ObservingHandler ( context , PublishMessageAsync , BuildPayload ) ;
177
+ return new ObservingHandler ( PublishMessageAsync , Metadata . ProjectId , context , options ) ;
176
178
}
177
179
178
180
@@ -191,7 +193,7 @@ public void ReportError(HttpContext context, Exception error)
191
193
}
192
194
}
193
195
194
- public Payload BuildPayload ( string SDKType , Stopwatch stopwatch , HttpRequest req , int statusCode , byte [ ] reqBody , byte [ ] respBody , Dictionary < string , List < string > > respHeader , Dictionary < string , string > pathParams , string urlPath , List < ATError > errors )
196
+ public Payload BuildPayload ( string SDKType , Stopwatch stopwatch , HttpRequest req , int statusCode , byte [ ] reqBody , byte [ ] respBody , Dictionary < string , List < string > > respHeader , Dictionary < string , string > pathParams , string urlPath , List < ATError > errors , string msg_id )
195
197
{
196
198
if ( req == null || Metadata is null )
197
199
{
@@ -218,9 +220,9 @@ public Payload BuildPayload(string SDKType, Stopwatch stopwatch, HttpRequest req
218
220
ProjectId = projectId ,
219
221
ProtoMajor = majorVersion ,
220
222
ProtoMinor = minorVersion ,
221
- QueryParams = req . Query . ToDictionary ( kv => kv . Key , kv => kv . Value . ToList ( ) ) ,
223
+ QueryParams = req . Query . ToDictionary ( kv => kv . Key , kv => kv . Value . ToString ( ) ) ,
222
224
RawUrl = req . GetEncodedPathAndQuery ( ) ,
223
- Referer = req . Headers [ " Referer" ] . ToString ( ) ,
225
+ Referer = req . Headers . Referer . ToString ( ) ,
224
226
RequestBody = RedactJSON ( reqBody , Config . RedactRequestBody ) ,
225
227
RequestHeaders = RedactHeaders ( reqHeaders , Config . RedactHeaders ) ,
226
228
ResponseBody = RedactJSON ( respBody , Config . RedactResponseBody ) ,
@@ -229,14 +231,18 @@ public Payload BuildPayload(string SDKType, Stopwatch stopwatch, HttpRequest req
229
231
StatusCode = statusCode ,
230
232
Timestamp = DateTime . UtcNow ,
231
233
UrlPath = urlPath ,
232
- Errors = errors
234
+ Errors = errors ,
235
+ ServiceVersion = Config . ServiceVersion ,
236
+ Tags = Config . Tags ?? new List < string > { } ,
237
+ MsgId = msg_id
238
+
233
239
} ;
234
240
}
235
241
236
242
237
243
238
244
239
- private ATError BuildError ( Exception error )
245
+ private static ATError BuildError ( Exception error )
240
246
{
241
247
// Create an instance of ATError
242
248
var atError = new ATError
@@ -309,6 +315,9 @@ public class Config
309
315
public bool VerboseDebug { get ; set ; }
310
316
public string RootUrl { get ; set ; }
311
317
public string ApiKey { get ; set ; }
318
+ public string ServiceVersion { get ; set ; }
319
+ public List < string > Tags { get ; set ; }
320
+
312
321
public List < string > RedactHeaders { get ; set ; }
313
322
public List < string > RedactRequestBody { get ; set ; }
314
323
public List < string > RedactResponseBody { get ; set ; }
@@ -323,7 +332,7 @@ public class Payload
323
332
public Dictionary < string , List < string > > RequestHeaders { get ; set ; }
324
333
325
334
[ JsonProperty ( "query_params" ) ]
326
- public Dictionary < string , List < string > > QueryParams { get ; set ; }
335
+ public Dictionary < string , string > QueryParams { get ; set ; }
327
336
328
337
[ JsonProperty ( "path_params" ) ]
329
338
public Dictionary < string , string > PathParams { get ; set ; }
@@ -372,6 +381,16 @@ public class Payload
372
381
public long Duration { get ; set ; }
373
382
[ JsonProperty ( "errors" ) ]
374
383
public List < ATError > ? Errors { get ; set ; }
384
+ [ JsonProperty ( "tags" ) ]
385
+ public List < string > Tags { get ; set ; }
386
+ [ JsonProperty ( "service_version" ) ]
387
+ public string ServiceVersion { get ; set ; }
388
+ [ JsonProperty ( "parent_id" ) ]
389
+ public string ? ParentId { get ; set ; }
390
+
391
+ [ JsonProperty ( "msg_id" ) ]
392
+ public string ? MsgId { get ; set ; }
393
+
375
394
}
376
395
377
396
@@ -393,41 +412,128 @@ public class ATError
393
412
public string StackTrace { get ; set ; }
394
413
}
395
414
415
+
416
+ public class ATOptions
417
+ {
418
+ public string ? PathWildCard { get ; set ; }
419
+ public List < string > RedactHeaders { get ; set ; }
420
+ public List < string > RedactRequestBody { get ; set ; }
421
+ public List < string > RedactResponseBody { get ; set ; }
422
+ }
396
423
public class ObservingHandler : DelegatingHandler
397
424
{
398
425
private readonly HttpContext _context ;
399
426
private readonly Func < Payload , Task > _publishMessageAsync ;
400
- private readonly Func < string , Stopwatch , HttpRequest , int , byte [ ] , byte [ ] , Dictionary < string , List < string > > , Dictionary < string , string > , string , List < ATError > , Payload > _buildPayload ;
401
- public ObservingHandler ( HttpContext httpContext , Func < Payload , Task > publishMessage , Func < string , Stopwatch , HttpRequest , int , byte [ ] , byte [ ] , Dictionary < string , List < string > > , Dictionary < string , string > , string , List < ATError > , Payload > buildPayload )
427
+ private readonly ATOptions _options ;
428
+ private readonly string _project_id ;
429
+ private readonly string ? _msg_id ;
430
+ public ObservingHandler ( Func < Payload , Task > publishMessage , string project_id , HttpContext ? httpContext = null , ATOptions ? options = null ) : base ( new HttpClientHandler ( ) )
402
431
{
403
432
_context = httpContext ?? throw new ArgumentNullException ( nameof ( httpContext ) ) ;
404
433
_publishMessageAsync = publishMessage ;
405
- _buildPayload = buildPayload ;
434
+ _options = options ?? new ATOptions { RedactHeaders = new List < string > { } , RedactRequestBody = new List < string > { } , RedactResponseBody = new List < string > { } } ;
435
+ _project_id = project_id ;
436
+ if ( httpContext != null )
437
+ {
438
+ if ( httpContext . Items . TryGetValue ( "APITOOLKIT_MSG_ID" , out var msg_id ) && msg_id != null )
439
+ {
440
+ _msg_id = msg_id . ToString ( ) ;
441
+ }
442
+ }
406
443
}
407
444
445
+
408
446
protected override async Task < HttpResponseMessage > SendAsync ( HttpRequestMessage request , CancellationToken cancellationToken )
409
447
{
410
- var requestInfo = new
448
+
449
+ var StartTime = DateTimeOffset . UtcNow ;
450
+ var Method = request . Method ;
451
+ var RequestUri = request . RequestUri ;
452
+ var reqHeaders = request . Headers . ToDictionary ( h => h . Key , h => h . Value . ToList ( ) ) ;
453
+ var reqBody = "" ;
454
+ if ( request != null && request . Content != null )
411
455
{
412
- StartTime = DateTimeOffset . UtcNow ,
413
- Method = request . Method ,
414
- RequestUri = request . RequestUri ,
415
- Headers = request . Headers ,
416
- Body = await request . Content . ReadAsStringAsync ( ) ,
417
- } ;
456
+ reqBody = await request . Content . ReadAsStringAsync ( cancellationToken ) ;
457
+ }
458
+
459
+ Stopwatch stopwatch = new Stopwatch ( ) ;
460
+ stopwatch . Start ( ) ;
418
461
419
462
var response = await base . SendAsync ( request , cancellationToken ) ;
420
463
421
- var responseInfo = new
464
+ try
422
465
{
423
- Duration = DateTimeOffset . UtcNow - requestInfo . StartTime ,
424
- StatusCode = response . StatusCode ,
425
- Headers = response . Headers ,
426
- Body = await response . Content . ReadAsStringAsync ( ) ,
427
- } ;
466
+ var Duration = DateTimeOffset . UtcNow ;
467
+ var StatusCode = response . StatusCode ;
468
+ var respHeaders = response . Headers . ToDictionary ( h => h . Key , h => h . Value . ToList ( ) ) ;
469
+ var respBody = await response . Content . ReadAsStringAsync ( ) ;
470
+ var queryDictCol = ParseQueryString ( RequestUri ? . Query ?? "" ) ;
471
+
472
+ var queryDict = new Dictionary < string , string > ( ) ;
473
+ foreach ( string key in queryDictCol )
474
+ {
475
+ queryDict [ key ] = queryDictCol [ key ] ?? "" ;
476
+ }
477
+
478
+ stopwatch . Stop ( ) ;
479
+
480
+ var payload = new Payload
481
+ {
482
+ Duration = stopwatch . ElapsedTicks * 100 ,
483
+ Host = RequestUri ? . Host . ToString ( ) ?? "" ,
484
+ Method = Method . ToString ( ) ,
485
+ PathParams = ParsePathPattern ( _options . PathWildCard ?? RequestUri ? . AbsolutePath ?? "" , RequestUri ? . AbsolutePath ?? "" ) ,
486
+ ProjectId = _project_id ,
487
+ ProtoMajor = 1 ,
488
+ ProtoMinor = 1 ,
489
+ QueryParams = queryDict ,
490
+ RawUrl = RequestUri ? . PathAndQuery ?? "" ,
491
+ Referer = "" ,
492
+ RequestBody = Client . RedactJSON ( System . Text . Encoding . UTF8 . GetBytes ( reqBody ) , _options . RedactRequestBody ?? new List < string > { } ) ,
493
+ RequestHeaders = Client . RedactHeaders ( reqHeaders , _options . RedactHeaders ?? new List < string > { } ) ,
494
+ ResponseBody = Client . RedactJSON ( System . Text . Encoding . UTF8 . GetBytes ( respBody ) , _options . RedactResponseBody ?? new List < string > { } ) ,
495
+ ResponseHeaders = Client . RedactHeaders ( respHeaders , _options . RedactHeaders ?? new List < string > { } ) ,
496
+ SdkType = "DotNetOutgoing" ,
497
+ StatusCode = ( int ) StatusCode ,
498
+ ParentId = _msg_id ,
499
+ Timestamp = DateTime . UtcNow ,
500
+ UrlPath = _options . PathWildCard ?? RequestUri ? . AbsolutePath ?? "" ,
501
+
502
+ } ;
503
+
504
+ await _publishMessageAsync ( payload ) ;
505
+ return response ;
506
+ }
507
+ catch ( Exception err )
508
+ {
509
+ return response ;
510
+ }
511
+
512
+ }
513
+ static Dictionary < string , string > ParsePathPattern ( string pattern , string url )
514
+ {
515
+ var result = new Dictionary < string , string > ( ) ;
516
+
517
+ var patternParts = pattern . Split ( '/' ) ;
518
+ var urlParts = url . Split ( '/' ) ;
519
+
520
+ for ( int i = 0 ; i < patternParts . Length ; i ++ )
521
+ {
522
+ string patternPart = patternParts [ i ] ;
428
523
524
+ if ( patternPart . StartsWith ( '{' ) && patternPart . EndsWith ( '}' ) )
525
+ {
526
+ string variableName = patternPart . Trim ( '{' , '}' ) ;
527
+ string urlPart = "" ;
528
+ if ( i < urlParts . Length )
529
+ {
530
+ urlPart = urlParts [ i ] ;
531
+ }
532
+ result [ variableName ] = urlPart ;
533
+ }
534
+ }
429
535
430
- return response ;
536
+ return result ;
431
537
}
432
538
}
433
539
0 commit comments