//----------------------------------------------------------------------------- // // 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; using Amazon.XRay.Recorder.Core.Exceptions; using Amazon.XRay.Recorder.Core.Internal.Entities; using Amazon.XRay.Recorder.AutoInstrumentation.Utils; using System; using System.Collections.Concurrent; using System.Diagnostics.Tracing; using System.Globalization; namespace Amazon.XRay.Recorder.AutoInstrumentation { /// /// Sql event listener for tracing Sql query from System.Data.SqlClient /// public class SqlEventListener : EventListener { private const string SqlEventSourceName = "Microsoft-AdoNet-SystemData"; private const string EventSourceTypeName = "System.Data.SqlEventSource"; private const int SqlCommandExecutedBeforeId = 1; private const int SqlCommandExecutedAfterId = 2; private static readonly AWSXRayRecorder _recorder = AWSXRayRecorder.Instance; private static readonly Logger _logger = Logger.GetLogger(typeof(SqlEventListener)); private static readonly ConcurrentDictionary CurrentSqlEvents = new ConcurrentDictionary(); /// /// Enable receiving events /// protected override void OnEventSourceCreated(EventSource eventSource) { if (eventSource != null && eventSource.Name == SqlEventSourceName && eventSource.GetType().FullName == EventSourceTypeName) { EnableEvents(eventSource, EventLevel.Informational, (EventKeywords)1); } base.OnEventSourceCreated(eventSource); } /// /// Receive events /// protected override void OnEventWritten(EventWrittenEventArgs eventData) { if (eventData?.Payload == null) { return; } try { switch (eventData.EventId) { case SqlCommandExecutedBeforeId: { OnEventStart(eventData); } break; case SqlCommandExecutedAfterId: { OnEventStop(eventData); } break; } } catch (Exception e) { _logger.Error(e, "Invalid Event Id ({0})", eventData.EventId); } } /// /// Trace before executing Sql command /// private void OnEventStart(EventWrittenEventArgs sqlEventData) { if (sqlEventData.Payload.Count != 4) { return; } // Skip EF request if (SqlRequestUtil.IsTraceable()) { SqlRequestUtil.ProcessEventData(sqlEventData); try { var currentSubsegment = _recorder.GetEntity() as Subsegment; int id = Convert.ToInt32(sqlEventData.Payload[0], CultureInfo.InvariantCulture); if (currentSubsegment != null) { CurrentSqlEvents.TryAdd(id, currentSubsegment); } } catch (EntityNotAvailableException e) { AWSXRayRecorder.Instance.TraceContext.HandleEntityMissing(AWSXRayRecorder.Instance, e, "Subsegment is not available in trace context."); } } } /// /// Trace after executing Sql command /// private void OnEventStop(EventWrittenEventArgs sqlEventData) { if (sqlEventData.Payload.Count != 3) { return; } int id = Convert.ToInt32(sqlEventData.Payload[0], CultureInfo.InvariantCulture); int state = Convert.ToInt32(sqlEventData.Payload[1], CultureInfo.InvariantCulture); int exceptionNumber = Convert.ToInt32(sqlEventData.Payload[2], CultureInfo.InvariantCulture); if (CurrentSqlEvents.TryRemove(id, out var currentSubsegment)) { if ((state & 2) == 2) { currentSubsegment.HasFault = true; } SqlRequestUtil.EndSubsegment(currentSubsegment); } } } } #endif