|
8 | using System.Reflection; | 8 | using System.Reflection; |
9 | using System.Reflection.Emit; | 9 | using System.Reflection.Emit; |
10 | using System.Runtime.Serialization; | 10 | using System.Runtime.Serialization; |
| | 11 | +using System.Text; |
11 |
| 12 |
|
12 | // This HttpHandlerDiagnosticListener class is applicable only for .NET 4.6, and not for .NET core. | 13 | // This HttpHandlerDiagnosticListener class is applicable only for .NET 4.6, and not for .NET core. |
13 |
| 14 |
|
@@ -557,33 +558,69 @@ private HttpHandlerDiagnosticListener() : base(DiagnosticListenerName) |
557 |
| 558 |
|
558 | private void RaiseRequestEvent(HttpWebRequest request) | 559 | private void RaiseRequestEvent(HttpWebRequest request) |
559 | { | 560 | { |
560 | - // If System.Net.Http.Request is on, raise the event | 561 | + if (request.Headers.Get(RequestIdHeaderName) != null) |
561 | - if (this.IsEnabled(RequestWriteName)) | | |
562 | { | 562 | { |
563 | - long timestamp = Stopwatch.GetTimestamp(); | 563 | + // this request was instrumented by previous RaiseRequestEvent |
564 | - this.Write(RequestWriteName, | 564 | + return; |
565 | - new | 565 | + } |
| | 566 | + |
| | 567 | + if (this.IsEnabled(ActivityName, request)) |
| | 568 | + { |
| | 569 | + var activity = new Activity(ActivityName); |
| | 570 | + |
| | 571 | + // Only send start event to users who subscribed for it, but start activity anyway |
| | 572 | + if (this.IsEnabled(RequestStartName)) |
| | 573 | + { |
| | 574 | + long timestamp = Stopwatch.GetTimestamp(); |
| | 575 | + this.StartActivity(activity, |
| | 576 | + new |
| | 577 | + { |
| | 578 | + Request = request, |
| | 579 | + Timestamp = timestamp |
| | 580 | + }); |
| | 581 | + } |
| | 582 | + else |
| | 583 | + { |
| | 584 | + activity.Start(); |
| | 585 | + } |
| | 586 | + |
| | 587 | + request.Headers.Add(RequestIdHeaderName, activity.Id); |
| | 588 | + //we expect baggage to be empty or contain a few items |
| | 589 | + using (IEnumerator<KeyValuePair<string, string>> e = activity.Baggage.GetEnumerator()) |
| | 590 | + { |
| | 591 | + if (e.MoveNext()) |
566 | { | 592 | { |
567 | - Request = request, | 593 | + StringBuilder baggage = new StringBuilder(); |
568 | - Timestamp = timestamp | 594 | + do |
| | 595 | + { |
| | 596 | + KeyValuePair<string, string> item = e.Current; |
| | 597 | + baggage.Append(item.Key).Append('=').Append(item.Value).Append(','); |
| | 598 | + } |
| | 599 | + while (e.MoveNext()); |
| | 600 | + baggage.Remove(baggage.Length - 1, 1); |
| | 601 | + request.Headers.Add(CorrelationContextHeaderName, baggage.ToString()); |
569 | } | 602 | } |
570 | - ); | 603 | + } |
| | 604 | + |
| | 605 | + // There is no gurantee that Activity.Current will flow to the Response, so let's stop it here |
| | 606 | + activity.Stop(); |
571 | } | 607 | } |
572 | } | 608 | } |
573 |
| 609 |
|
574 | private void RaiseResponseEvent(HttpWebRequest request, HttpWebResponse response) | 610 | private void RaiseResponseEvent(HttpWebRequest request, HttpWebResponse response) |
575 | { | 611 | { |
576 | - if (this.IsEnabled(ResponseWriteName)) | 612 | + // Response event could be received several times for the same request |
| | 613 | + if (request.Headers[RequestIdHeaderName] != null) |
577 | { | 614 | { |
| | 615 | + // only send Stop if request was instrumented |
578 | long timestamp = Stopwatch.GetTimestamp(); | 616 | long timestamp = Stopwatch.GetTimestamp(); |
579 | - this.Write(ResponseWriteName, | 617 | + this.Write(RequestStopName, |
580 | new | 618 | new |
581 | { | 619 | { |
582 | Request = request, | 620 | Request = request, |
583 | Response = response, | 621 | Response = response, |
584 | Timestamp = timestamp | 622 | Timestamp = timestamp |
585 | - } | 623 | + }); |
586 | - ); | | |
587 | } | 624 | } |
588 | } | 625 | } |
589 |
| 626 |
|
@@ -647,9 +684,12 @@ private static void PerformInjection() |
647 |
| 684 |
|
648 | #region private fields | 685 | #region private fields |
649 | private const string DiagnosticListenerName = "System.Net.Http.Desktop"; | 686 | private const string DiagnosticListenerName = "System.Net.Http.Desktop"; |
650 | - private const string RequestWriteName = "System.Net.Http.Request"; | 687 | + private const string ActivityName = "System.Net.Http.Desktop.HttpRequestOut"; |
651 | - private const string ResponseWriteName = "System.Net.Http.Response"; | 688 | + private const string RequestStartName = "System.Net.Http.Desktop.HttpRequestOut.Start"; |
| | 689 | + private const string RequestStopName = "System.Net.Http.Desktop.HttpRequestOut.Stop"; |
652 | private const string InitializationFailed = "System.Net.Http.InitializationFailed"; | 690 | private const string InitializationFailed = "System.Net.Http.InitializationFailed"; |
| | 691 | + private const string RequestIdHeaderName = "Request-Id"; |
| | 692 | + private const string CorrelationContextHeaderName = "Correlation-Context"; |
653 |
| 693 |
|
654 | // Fields for controlling initialization of the HttpHandlerDiagnosticListener singleton | 694 | // Fields for controlling initialization of the HttpHandlerDiagnosticListener singleton |
655 | private bool initialized = false; | 695 | private bool initialized = false; |
|
0 commit comments