Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add command text to SQL command #49

Merged
merged 7 commits into from
Jul 2, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 31 additions & 28 deletions sdk/src/Core/Internal/Utils/AppSettings.netcore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,6 @@ namespace Amazon.XRay.Recorder.Core.Internal.Utils
/// </summary>
public class XRayOptions
{
private string _pluginSetting;
private string _samplingRuleManifest;
private string _awsServiceHandlerManifest;
private bool _isXRayTracingDisabled;
private bool _useRuntimeErrors = true;

/// <summary>
/// Default constructor.
/// </summary>
Expand All @@ -43,49 +37,58 @@ public XRayOptions()
/// <param name="isXRayTracingDisabled">Tracing disabled value, either true or false.</param>
public XRayOptions(string pluginSetting, string samplingRuleManifest, string awsServiceHandlerManifest,
bool isXRayTracingDisabled) : this(pluginSetting, samplingRuleManifest, awsServiceHandlerManifest, isXRayTracingDisabled, true)
{
}
/// <summary>
/// Creates instance of <see cref="XRayOptions"/>
/// </summary>
/// <param name="pluginSetting">Plugin setting.</param>
/// <param name="samplingRuleManifest">Sampling rule file path</param>
/// <param name="awsServiceHandlerManifest">AWS Service manifest file path.</param>
{
}

/// <summary>
/// Creates instance of <see cref="XRayOptions"/>
/// </summary>
/// <param name="pluginSetting">Plugin setting.</param>
/// <param name="samplingRuleManifest">Sampling rule file path</param>
/// <param name="awsServiceHandlerManifest">AWS Service manifest file path.</param>
/// <param name="isXRayTracingDisabled">Tracing disabled value, either true or false.</param>
/// <param name="useRuntimeErrors">Should errors be thrown at runtime if segment not started, either true or false.</param>
public XRayOptions(string pluginSetting, string samplingRuleManifest, string awsServiceHandlerManifest, bool isXRayTracingDisabled, bool useRuntimeErrors)
public XRayOptions(string pluginSetting, string samplingRuleManifest, string awsServiceHandlerManifest, bool isXRayTracingDisabled, bool useRuntimeErrors, bool collectSqlQueries = false)
yogiraj07 marked this conversation as resolved.
Show resolved Hide resolved
{
PluginSetting = pluginSetting;
SamplingRuleManifest = samplingRuleManifest;
AwsServiceHandlerManifest = awsServiceHandlerManifest;
IsXRayTracingDisabled = isXRayTracingDisabled;
UseRuntimeErrors = useRuntimeErrors;
CollectSqlQueries = collectSqlQueries;
}

/// <summary>
/// Plugin setting.
/// <summary>
/// Plugin setting.
/// </summary>
public string PluginSetting { get => _pluginSetting; set => _pluginSetting = value; }
public string PluginSetting { get; set; }

/// <summary>
/// Sampling rule file path.
/// <summary>
/// Sampling rule file path.
/// </summary>
public string SamplingRuleManifest { get => _samplingRuleManifest; set => _samplingRuleManifest = value; }
public string SamplingRuleManifest { get; set; }

/// <summary>
/// AWS Service manifest file path.
/// <summary>
/// AWS Service manifest file path.
/// </summary>
public string AwsServiceHandlerManifest { get => _awsServiceHandlerManifest; set => _awsServiceHandlerManifest = value; }
public string AwsServiceHandlerManifest { get; set; }

/// <summary>
/// Tracing disabled value, either true or false.
/// </summary>
public bool IsXRayTracingDisabled { get => _isXRayTracingDisabled; set => _isXRayTracingDisabled = value; }
public bool IsXRayTracingDisabled { get; set; }

/// <summary>
/// For missing Segments/Subsegments, if set to true, runtime exception is thrown, if set to false, runtime exceptions are avoided and logged.
/// </summary>
public bool UseRuntimeErrors { get => _useRuntimeErrors; set => _useRuntimeErrors = value; }
public bool UseRuntimeErrors { get; set; } = true;

/// <summary>
/// Include the <see cref="TraceableSqlCommand.CommandText" /> in the sanitized_query.
yogiraj07 marked this conversation as resolved.
Show resolved Hide resolved
/// Parameterized values will appear in their tokenized form and will not be expanded.
/// You should not enable this flag if you are not including sensitive information as clear text.
yogiraj07 marked this conversation as resolved.
Show resolved Hide resolved
/// This flag can also be overridden for each <see cref="TraceableSqlCommand" /> instance individually.
/// </summary>
public bool CollectSqlQueries { get; set; }
}
}
10 changes: 9 additions & 1 deletion sdk/src/Core/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
using System;
using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

[assembly: ComVisible(false)]
[assembly: CLSCompliant(true)]
[assembly: InternalsVisibleTo("AWSXRayRecorder.UnitTests,PublicKey="+
"0024000004800000940000000602000000240000525341310004000001000100712913451f6deb"
+ "158da1d2129b21119cca7d4eebeef5b310e8acd7f2d9506346071207652f1210a3bfa1545d6897"
+ "a607fc3a515954e660ec6fc5797730022867514e58411e8ecd61c767a319d2c29facee20f5d4f4"
+ "2b5425f27518616a8f4c1e5ac0e3e2b407bd8786d1b360af6b49c2b987478fe76b124c72f48864"
+ "55199df6"
)]
117 changes: 117 additions & 0 deletions sdk/src/Handlers/SqlServer/DbCommandInterceptor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//-----------------------------------------------------------------------------
// <copyright file="DbCommandInterceptor.cs" company="Amazon.com">
// 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.
// </copyright>
//-----------------------------------------------------------------------------

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;

namespace Amazon.XRay.Recorder.Handlers.SqlServer
{
public interface IDbCommandInterceptor
yogiraj07 marked this conversation as resolved.
Show resolved Hide resolved
{
Task<TResult> InterceptAsync<TResult>(Func<Task<TResult>> method, DbCommand command);
TResult Intercept<TResult>(Func<TResult> 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<TResult> InterceptAsync<TResult>(Func<Task<TResult>> method, DbCommand command)
{
_recorder.BeginSubsegment(BuildSegmentName(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<TResult>(Func<TResult> method, DbCommand command)
{
_recorder.BeginSubsegment(BuildSegmentName(command));
try
{
_recorder.SetNamespace("remote");
var ret = method();
CollectSqlInformation(command);

return ret;
}
catch (Exception e)
{
_recorder.AddException(e);
throw;
}
finally
{
_recorder.EndSubsegment();
}
}

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");

_recorder.AddSqlInformation("user", connectionStringBuilder.UserID);
_recorder.AddSqlInformation("connection_string", connectionStringBuilder.ToString());

if(ShouldCollectSqlText())
{
_recorder.AddSqlInformation("sanitized_query", command.CommandText);
}
}

private string BuildSegmentName(DbCommand command)
yogiraj07 marked this conversation as resolved.
Show resolved Hide resolved
=> command.Connection.Database + "@" + SqlUtil.RemovePortNumberFromDataSource(command.Connection.DataSource);

private bool ShouldCollectSqlText()
=> _collectSqlQueriesOverride ?? _recorder.XRayOptions.CollectSqlQueries;
}
}
107 changes: 41 additions & 66 deletions sdk/src/Handlers/SqlServer/TraceableSqlCommand.netstandard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,33 +33,57 @@ namespace Amazon.XRay.Recorder.Handlers.SqlServer
/// <see cref="SqlCommand" />
public class TraceableSqlCommand : DbCommand, ICloneable
{
private const string DataBaseTypeString = "sqlserver";
private IDbCommandInterceptor _interceptor { get; set; }

/// <summary>
/// Initializes a new instance of the <see cref="TraceableSqlCommand"/> class.
/// </summary>
public TraceableSqlCommand()
/// <param name="collectSqlQueries">
/// Include the <see cref="TraceableSqlCommand.CommandText" /> in the sanitized_query.
yogiraj07 marked this conversation as resolved.
Show resolved Hide resolved
/// Parameterized values will appear in their tokenized form and will not be expanded.
/// You should not enable this flag if you are not including sensitive information as clear text.
/// This flag will overridde any behavior configured by <see cref="XRayOptions.CollectSqlQueries" />.
/// If a value is not provided, then the globally configured value will be used, which is fals by default.
yogiraj07 marked this conversation as resolved.
Show resolved Hide resolved
/// </param>
public TraceableSqlCommand(bool? collectSqlQueries = null)
{
InnerSqlCommand = new SqlCommand();
_interceptor = new DbCommandInterceptor(AWSXRayRecorder.Instance, collectSqlQueries);
}

/// <summary>
/// Initializes a new instance of the <see cref="TraceableSqlCommand"/> class.
/// </summary>
/// <param name="cmdText">The text of the query.</param>
public TraceableSqlCommand(string cmdText)
/// <param name="collectSqlQueries">
/// Include the <see cref="TraceableSqlCommand.CommandText" /> in the sanitized_query.
/// Parameterized values will appear in their tokenized form and will not be expanded.
/// You should not enable this flag if you are not including sensitive information as clear text.
/// This flag will overridde any behavior configured by <see cref="XRayOptions.CollectSqlQueries" />.
/// If a value is not provided, then the globally configured value will be used, which is fals by default.
/// </param>
public TraceableSqlCommand(string cmdText, bool? collectSqlQueries = null)
{
InnerSqlCommand = new SqlCommand(cmdText);
_interceptor = new DbCommandInterceptor(AWSXRayRecorder.Instance, collectSqlQueries);
}

/// <summary>
/// Initializes a new instance of the <see cref="TraceableSqlCommand"/> class.
/// </summary>
/// <param name="cmdText">The text of the query.</param>
/// <param name="connection">The connection to an instance of SQL Server.</param>
public TraceableSqlCommand(string cmdText, SqlConnection connection)
/// <param name="collectSqlQueries">
/// Include the <see cref="TraceableSqlCommand.CommandText" /> in the sanitized_query.
/// Parameterized values will appear in their tokenized form and will not be expanded.
/// You should not enable this flag if you are not including sensitive information as clear text.
/// This flag will overridde any behavior configured by <see cref="XRayOptions.CollectSqlQueries" />.
/// If a value is not provided, then the globally configured value will be used, which is fals by default.
/// </param>
public TraceableSqlCommand(string cmdText, SqlConnection connection, bool? collectSqlQueries = null)
{
InnerSqlCommand = new SqlCommand(cmdText, connection);
_interceptor = new DbCommandInterceptor(AWSXRayRecorder.Instance, collectSqlQueries);
}

/// <summary>
Expand All @@ -68,9 +92,17 @@ public TraceableSqlCommand(string cmdText, SqlConnection connection)
/// <param name="cmdText">The text of the query.</param>
/// <param name="connection">The connection to an instance of SQL Server.</param>
/// <param name="transaction">The <see cref="SqlTransaction"/> in which the <see cref="SqlCommand"/> executes.</param>
public TraceableSqlCommand(string cmdText, SqlConnection connection, SqlTransaction transaction)
/// <param name="collectSqlQueries">
/// Include the <see cref="TraceableSqlCommand.CommandText" /> in the sanitized_query.
/// Parameterized values will appear in their tokenized form and will not be expanded.
/// You should not enable this flag if you are not including sensitive information as clear text.
/// This flag will overridde any behavior configured by <see cref="XRayOptions.CollectSqlQueries" />.
/// If a value is not provided, then the globally configured value will be used, which is fals by default.
/// </param>
public TraceableSqlCommand(string cmdText, SqlConnection connection, SqlTransaction transaction, bool? collectSqlQueries = null)
{
InnerSqlCommand = new SqlCommand(cmdText, connection, transaction);
_interceptor = new DbCommandInterceptor(AWSXRayRecorder.Instance, collectSqlQueries);
}

private TraceableSqlCommand(TraceableSqlCommand from)
Expand Down Expand Up @@ -394,67 +426,10 @@ protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
return InnerSqlCommand.ExecuteReader(behavior);
}

private async Task<TResult> InterceptAsync<TResult>(Func<Task<TResult>> method)
{
AWSXRayRecorder recorder = AWSXRayRecorder.Instance;
recorder.BeginSubsegment(Connection.Database + "@" + SqlUtil.RemovePortNumberFromDataSource(Connection.DataSource));
try
{
recorder.SetNamespace("remote");
var ret = await method();
CollectSqlInformation();

return ret;
}
catch (Exception e)
{
recorder.AddException(e);
throw;
}
finally
{
recorder.EndSubsegment();
}
}

private TResult Intercept<TResult>(Func<TResult> method)
{
AWSXRayRecorder recorder = AWSXRayRecorder.Instance;
recorder.BeginSubsegment(Connection.Database + "@" + SqlUtil.RemovePortNumberFromDataSource(Connection.DataSource));
try
{
recorder.SetNamespace("remote");
var ret = method();
CollectSqlInformation();

return ret;
}
catch (Exception e)
{
recorder.AddException(e);
throw;
}
finally
{
recorder.EndSubsegment();
}
}
protected async Task<TResult> InterceptAsync<TResult>(Func<Task<TResult>> method)
yogiraj07 marked this conversation as resolved.
Show resolved Hide resolved
=> await _interceptor.InterceptAsync(method, this);

private void CollectSqlInformation()
{
AWSXRayRecorder recorder = AWSXRayRecorder.Instance;
recorder.AddSqlInformation("database_type", DataBaseTypeString);

recorder.AddSqlInformation("database_version", Connection.ServerVersion);

SqlConnectionStringBuilder connectionStringBuilder = new SqlConnectionStringBuilder(Connection.ConnectionString);

// Remove sensitive information from connection string
connectionStringBuilder.Remove("Password");

recorder.AddSqlInformation("user", connectionStringBuilder.UserID);
recorder.AddSqlInformation("connection_string", connectionStringBuilder.ToString());
recorder.AddSqlInformation("sanitized_query", CommandText);
}
protected virtual TResult Intercept<TResult>(Func<TResult> method)
=> _interceptor.Intercept(method, this);
}
}
Loading