//-----------------------------------------------------------------------------
//
// 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.AutoInstrumentation.Utils;
using Amazon.XRay.Recorder.Core.Internal.Entities;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data.Common;
namespace Amazon.XRay.Recorder.AutoInstrumentation
{
///
/// Diagnostic listener for processing Sql query for System.Data.SqlClient and Microsoft.Data.SqlClient
///
public class SqlDiagnosticListener : DiagnosticListenerBase
{
private static readonly Logger _logger = Logger.GetLogger(typeof(SqlDiagnosticListener));
internal override string Name => "SqlClientDiagnosticListener";
private static readonly ConcurrentDictionary CurrentDbCommands = new ConcurrentDictionary();
protected override void OnEvent(KeyValuePair value)
{
try
{
switch (value.Key)
{
case "Microsoft.Data.SqlClient.WriteCommandBefore":
{
OnEventStart(value.Value);
}
break;
case "System.Data.SqlClient.WriteCommandBefore":
{
OnEventStart(value.Value);
}
break;
case "Microsoft.Data.SqlClient.WriteCommandAfter":
{
OnEventStop(value.Value);
}
break;
case "System.Data.SqlClient.WriteCommandAfter":
{
OnEventStop(value.Value);
}
break;
case "Microsoft.Data.SqlClient.WriteCommandError":
{
OnEventException(value.Value);
}
break;
case "System.Data.SqlClient.WriteCommandError":
{
OnEventException(value.Value);
}
break;
}
}
catch (Exception e)
{
_logger.Error(e, "Invalid diagnostic source key ({0})", value.Key);
}
}
private void OnEventStart(object value)
{
// This class serves for tracing Sql command from both System.Data.SqlClient and Microsoft.Data.SqlClient and using fetch property works
// fot both of these two cases
var command = AgentUtil.FetchPropertyUsingReflection(value, "Command");
if (command is DbCommand dbcommand)
{
// Skip processing EntityFramework Core request
if (SqlRequestUtil.IsTraceable() && CurrentDbCommands.TryAdd(dbcommand, null))
{
SqlRequestUtil.BeginSubsegment(dbcommand);
SqlRequestUtil.ProcessCommand(dbcommand);
}
}
}
private void OnEventStop(object value)
{
// This class serves for tracing Sql command from both System.Data.SqlClient and Microsoft.Data.SqlClient and using fetch property works
// fot both of these two cases
var command = AgentUtil.FetchPropertyUsingReflection(value, "Command");
if (command is DbCommand dbcommand)
{
if (CurrentDbCommands.TryRemove(dbcommand, out _))
{
SqlRequestUtil.EndSubsegment();
}
}
}
private void OnEventException(object value)
{
// This class serves for tracing Sql command from both System.Data.SqlClient and Microsoft.Data.SqlClient and using fetch property works
// fot both of these two cases
var command = AgentUtil.FetchPropertyUsingReflection(value, "Command");
var exc = AgentUtil.FetchPropertyUsingReflection(value, "Exception");
if (command is DbCommand dbcommand && exc is Exception exception)
{
if (CurrentDbCommands.TryRemove(dbcommand, out _))
{
SqlRequestUtil.ProcessException(exception);
SqlRequestUtil.EndSubsegment();
}
}
}
}
}
#endif