//----------------------------------------------------------------------------- // // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. // A copy of the License is located at // // http://aws.amazon.com/apache2.0 // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. // //----------------------------------------------------------------------------- #if !NET45 using Amazon.Runtime.Internal.Util; using Amazon.XRay.Recorder.Core.Internal.Entities; using Amazon.XRay.Recorder.AutoInstrumentation.Utils; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Net.Http; namespace Amazon.XRay.Recorder.AutoInstrumentation { /// /// Diagnostic listener for processing Http outgoing request /// public class HttpOutDiagnosticListenerNetstandard : DiagnosticListenerBase { private static readonly Logger _logger = Logger.GetLogger(typeof(HttpOutDiagnosticListenerNetstandard)); internal override string Name => "HttpHandlerDiagnosticListener"; private static readonly ConcurrentDictionary CurrentHttpRequestMessages = new ConcurrentDictionary(); protected override void OnEvent(KeyValuePair value) { try { switch (value.Key) { case "System.Net.Http.HttpRequestOut.Start": { OnEventStart(value.Value); } break; case "System.Net.Http.HttpRequestOut.Stop": { OnEventStop(value.Value); } break; case "System.Net.Http.Exception": { OnEventException(value.Value); } break; } } catch (Exception e) { _logger.Error(e, "Invalid diagnostic source key ({0})", value.Key); } } private void OnEventStart(object value) { // The value passed in is not castable, use fetch from reflection instead. var request = AgentUtil.FetchPropertyUsingReflection(value, "Request"); if (request is HttpRequestMessage httpRequestMessage) { // Skip AWS SDK Request since it is instrumented using the SDK if (HttpRequestUtil.IsTraceable(httpRequestMessage) && CurrentHttpRequestMessages.TryAdd(httpRequestMessage, null)) { HttpRequestUtil.ProcessRequest(httpRequestMessage); } } } private void OnEventStop(object value) { // The value passed in is not castable, use fetch from reflection instead. var request = AgentUtil.FetchPropertyUsingReflection(value, "Request"); var response = AgentUtil.FetchPropertyUsingReflection(value, "Response"); if (request is HttpRequestMessage httpRequestMessage && response is HttpResponseMessage httpResponseMessage) { if (CurrentHttpRequestMessages.TryRemove(httpRequestMessage, out _)) { HttpRequestUtil.ProcessResponse(httpResponseMessage); // End subsegment here HttpRequestUtil.EndSubsegment(); } } } private void OnEventException(object value) { // The value passed in is not castable, use fetch from reflection instead. var request = AgentUtil.FetchPropertyUsingReflection(value, "Request"); var exc = AgentUtil.FetchPropertyUsingReflection(value, "Exception"); if (request is HttpRequestMessage httpRequestMessage && exc is Exception exception) { if (CurrentHttpRequestMessages.TryRemove(httpRequestMessage, out _)) { HttpRequestUtil.ProcessException(exception); } } } } } #endif