Example #1
0
            public void OnNext(KeyValuePair <string, object> value)
            {
                if (value.Key == "Microsoft.AspNetCore.Hosting.HttpRequestIn.Start")
                {
                    // This happens on incoming requests to ASP.NET. Grab the MS-CV header and store
                    // it on the HttpContext.
                    //
                    if (!(value.Value.GetType().GetProperty("HttpContext")?.GetValue(value.Value, null) is HttpContext httpContext))
                    {
                        return;
                    }

                    CorrelationVector correlationVector;

                    if (httpContext.Request.Headers.ContainsKey("MS-CV"))
                    {
                        correlationVector =
                            CorrelationVector.Extend(httpContext.Request.Headers["MS-CV"][0]);
                    }
                    else
                    {
                        correlationVector = new CorrelationVector();
                    }

                    CorrelationVector.Current = correlationVector;
                }
                else if (value.Key == "System.Net.Http.HttpRequestOut.Start")
                {
                    // This happens on outgoing Http requests via Http Client. See if a Correlation
                    // Vector has been stored on the Request Message's properties and use it to
                    // stamp an MS-CV header (after incrementing the CV).
                    //
                    if (!(value.Value.GetType().GetProperty("Request")?.GetValue(value.Value, null) is HttpRequestMessage requestMessage))
                    {
                        return;
                    }

                    requestMessage.Properties.Add("TimeStamp", DateTime.Now.Ticks);

                    // If the application code explicitly passed along the cV header from the incoming request, then increment it prior to the outbound request.
                    CorrelationVector correlationVector = requestMessage.GetCorrelationVector();
                    if (correlationVector != null)
                    {
                        requestMessage.Headers.Add("MS-CV", correlationVector.Increment());
                    }
                    else
                    {
                        // This is the expected case where the application code is unaware of the cV and did not get it from the incoming request and set it on the
                        // outgoing request. Get the current cV from the AsyncLocal Instance, increment it, and add it to the outgoing request.
                        correlationVector = CorrelationVector.Current;
                        if (correlationVector != null)
                        {
                            requestMessage.Headers.Add("MS-CV", correlationVector.Increment());
                        }
                        else
                        {
                            // TODO: Should never hit this case. Just in case we do, initialize a new cV here.
                        }
                    }
                }
                else if (value.Key == "System.Net.Http.HttpRequestOut.Stop")
                {
                    // This happens after an outgoing Http request via Http Client completes. We can use
                    // this as an opportunity to log the Outgoing Service Request event. Many of the
                    // properties used for logging can be gleaned from the Http request and response.
                    // Some of the properties need to be stamped on the request ahead of time by the
                    // service owner, such as the Dependency information.
                    //
                    if (!(value.Value.GetType().GetProperty("Response")?.GetValue(value.Value, null) is HttpResponseMessage responseMessage))
                    {
                        return;
                    }

                    long?latency = null;

                    if (responseMessage.RequestMessage.Properties.ContainsKey("TimeStamp") &&
                        responseMessage.RequestMessage.Properties["TimeStamp"] is long requestTimeStamp)
                    {
                        latency = (long)(DateTime.Now - new DateTime(requestTimeStamp)).TotalMilliseconds;
                    }

                    // Acquire a lock just so the console output isn't jumbled up by multiple threads
                    //
                    lock (_outgoingRequestLoggingLock)
                    {
                        // These properties can be gleaned from the Http request and response
                        //
                        Console.WriteLine("---------------------------------------");
                        Console.WriteLine("Logging the Outgoing Service Request...");
                        Console.WriteLine("Correlation Vector: {0}", responseMessage.RequestMessage.GetCorrelationVectorHeader());
                        Console.WriteLine("Target Uri: {0}", responseMessage.RequestMessage.RequestUri.ToString());
                        Console.WriteLine("Latency ms: {0}", latency.HasValue ? latency.Value.ToString() : string.Empty);
                        // TODO - need code to be able to read the service error code - should this code be common or customizable?
                        Console.WriteLine("Service error code: {0}", null);
                        Console.WriteLine("Succeeded: {0}", responseMessage.IsSuccessStatusCode);
                        Console.WriteLine("Request method: {0}", responseMessage.RequestMessage.Method);
                        Console.WriteLine("Protocol Status Code: {0}", (int)responseMessage.StatusCode);
                        Console.WriteLine("Response Size (bytes): {0}", responseMessage.Content.ReadAsByteArrayAsync().Result.Length);

                        // These properties need to be stamped on the request by the service owner
                        // through the use of DependencyClientHandler and a HttpClient.SendAsync
                        // extension method.
                        //
                        if (responseMessage.RequestMessage.Properties.ContainsKey(nameof(DependencyOperationInfo)) &&
                            responseMessage.RequestMessage.Properties[nameof(DependencyOperationInfo)] is DependencyOperationInfo dependencyInfo)
                        {
                            Console.WriteLine("Dependency Name: {0}", dependencyInfo.DependencyName);
                            Console.WriteLine("Dependency Type: {0}", dependencyInfo.DependencyType);
                            Console.WriteLine("Operation Name: {0}", dependencyInfo.OperationName);
                            Console.WriteLine("Dependency Operation Name: {0}", dependencyInfo.DependencyOperationName);
                            Console.WriteLine("Dependency Operation Version: {0}", dependencyInfo.DependencyOperationVersion);
                        }

                        Console.WriteLine("---------------------------------------");
                    }
                }
            }
 public static void AddCorrelationVector(this HttpRequestMessage requestMessage, CorrelationVector correlationVector)
 {
     requestMessage.Properties.Add(nameof(CorrelationVector), correlationVector);
 }