//----------------------------------------------------------------------------- // // Copyright 2016 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. // //----------------------------------------------------------------------------- using System; using System.Data; using System.Data.Common; using System.Data.SqlClient; using System.Threading; using System.Threading.Tasks; using System.Xml; using Amazon.XRay.Recorder.Core; using Amazon.XRay.Recorder.Core.Internal.Utils; namespace Amazon.XRay.Recorder.Handlers.SqlServer { /// /// Intercepts DbCommands and records them in new Subsegments. /// public interface IDbCommandInterceptor { /// /// Begins a new Subsegment, executes the provided async operation, /// and records the request in the "sql" member of the subsegment. /// /// /// /// await InterceptAsync(() => dbCommand.ExecuteNonQueryAsync(cancellationToken), dbCommand); /// /// Task InterceptAsync(Func> method, DbCommand command); /// /// Begins a new Subsegment, executes the provided operation, /// and records the request in the "sql" member of the subsegment. /// /// /// /// await Intercept(() => dbCommand.ExecuteNonQuery(), dbCommand); /// /// TResult Intercept(Func method, DbCommand command); } /// public class DbCommandInterceptor : IDbCommandInterceptor { private const string DataBaseTypeString = "sqlserver"; private readonly AWSXRayRecorder _recorder; private readonly bool? _collectSqlQueriesOverride; public DbCommandInterceptor(AWSXRayRecorder recorder, bool? collectSqlQueries = null) { _recorder = recorder; _collectSqlQueriesOverride = collectSqlQueries; } /// public async Task InterceptAsync(Func> method, DbCommand command) { _recorder.BeginSubsegment(DbCommandInterceptor.BuildSubsegmentName(command)); try { _recorder.SetNamespace("remote"); var ret = await method(); CollectSqlInformation(command); return ret; } catch (Exception e) { _recorder.AddException(e); throw; } finally { _recorder.EndSubsegment(); } } /// public TResult Intercept(Func method, DbCommand command) { _recorder.BeginSubsegment(DbCommandInterceptor.BuildSubsegmentName(command)); try { _recorder.SetNamespace("remote"); var ret = method(); CollectSqlInformation(command); return ret; } catch (Exception e) { _recorder.AddException(e); throw; } finally { _recorder.EndSubsegment(); } } /// /// Records the SQL information on the current subsegment, /// protected virtual void CollectSqlInformation(DbCommand command) { _recorder.AddSqlInformation("database_type", DataBaseTypeString); _recorder.AddSqlInformation("database_version", command.Connection.ServerVersion); SqlConnectionStringBuilder connectionStringBuilder = new SqlConnectionStringBuilder(command.Connection.ConnectionString); // Remove sensitive information from connection string connectionStringBuilder.Remove("Password"); // Do a pre-check for UserID since in the case of TrustedConnection, a UserID may not be available. if (!string.IsNullOrEmpty(connectionStringBuilder.UserID)) { _recorder.AddSqlInformation("user", connectionStringBuilder.UserID); } _recorder.AddSqlInformation("connection_string", connectionStringBuilder.ToString()); if(ShouldCollectSqlText()) { _recorder.AddSqlInformation("sanitized_query", command.CommandText); } } /// /// Builds the name of the subsegment in the format database@datasource /// /// /// Returns the formed subsegment name as a string. private static string BuildSubsegmentName(DbCommand command) => command.Connection.Database + "@" + SqlUtil.RemovePortNumberFromDataSource(command.Connection.DataSource); #if !NETFRAMEWORK private bool ShouldCollectSqlText() => _collectSqlQueriesOverride ?? _recorder.XRayOptions.CollectSqlQueries; #else private bool ShouldCollectSqlText() => _collectSqlQueriesOverride ?? AppSettings.CollectSqlQueries; #endif } }