-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add configuration to record sanitized sql query for .NET Core (#49)
* Add command text to SQL command * Add configuration to allow collection of query text Converts XRayOptions fields to auto-properties Adds InternalsVisibleTo so tests can access internal members * PR feedback * added CollectSqlQueries to configuration * Removed doc references to TraceableSqlCommand to avoid circular referencing * Added tests to testXRayOptions to test for the CollectSqlQueries configuration parameter. * added missing json files for unit tests
- Loading branch information
1 parent
3f60868
commit e9a1ff4
Showing
11 changed files
with
599 additions
and
94 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
)] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
//----------------------------------------------------------------------------- | ||
// <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 | ||
{ | ||
/// <summary> | ||
/// Intercepts DbCommands and records them in new Subsegments. | ||
/// </summary> | ||
public interface IDbCommandInterceptor | ||
{ | ||
/// <summary> | ||
/// Begins a new Subsegment, executes the provided async operation, | ||
/// and records the request in the "sql" member of the subsegment. | ||
/// </summary> | ||
/// <example> | ||
/// <code> | ||
/// await InterceptAsync(() => dbCommand.ExecuteNonQueryAsync(cancellationToken), dbCommand); | ||
/// </code> | ||
/// </example> | ||
Task<TResult> InterceptAsync<TResult>(Func<Task<TResult>> method, DbCommand command); | ||
|
||
/// <summary> | ||
/// Begins a new Subsegment, executes the provided operation, | ||
/// and records the request in the "sql" member of the subsegment. | ||
/// </summary> | ||
/// <example> | ||
/// <code> | ||
/// await Intercept(() => dbCommand.ExecuteNonQuery(), dbCommand); | ||
/// </code> | ||
/// </example> | ||
TResult Intercept<TResult>(Func<TResult> method, DbCommand command); | ||
} | ||
|
||
/// <inheritdoc /> | ||
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; | ||
} | ||
|
||
/// <inheritdoc /> | ||
public async Task<TResult> InterceptAsync<TResult>(Func<Task<TResult>> method, DbCommand command) | ||
{ | ||
_recorder.BeginSubsegment(BuildSubsegmentName(command)); | ||
try | ||
{ | ||
_recorder.SetNamespace("remote"); | ||
var ret = await method(); | ||
CollectSqlInformation(command); | ||
|
||
return ret; | ||
} | ||
catch (Exception e) | ||
{ | ||
_recorder.AddException(e); | ||
throw; | ||
} | ||
finally | ||
{ | ||
_recorder.EndSubsegment(); | ||
} | ||
} | ||
|
||
/// <inheritdoc /> | ||
public TResult Intercept<TResult>(Func<TResult> method, DbCommand command) | ||
{ | ||
_recorder.BeginSubsegment(BuildSubsegmentName(command)); | ||
try | ||
{ | ||
_recorder.SetNamespace("remote"); | ||
var ret = method(); | ||
CollectSqlInformation(command); | ||
|
||
return ret; | ||
} | ||
catch (Exception e) | ||
{ | ||
_recorder.AddException(e); | ||
throw; | ||
} | ||
finally | ||
{ | ||
_recorder.EndSubsegment(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Records the SQL information on the current subsegment, | ||
/// </summary> | ||
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); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Builds the name of the subsegment in the format database@datasource | ||
/// </summary> | ||
/// <param name="command"></param> | ||
/// <returns>Returns the formed subsegment name as a string.</returns> | ||
private string BuildSubsegmentName(DbCommand command) | ||
=> command.Connection.Database + "@" + SqlUtil.RemovePortNumberFromDataSource(command.Connection.DataSource); | ||
|
||
#if !NET45 | ||
private bool ShouldCollectSqlText() | ||
=> _collectSqlQueriesOverride ?? _recorder.XRayOptions.CollectSqlQueries; | ||
#else | ||
private bool ShouldCollectSqlText() | ||
=> _collectSqlQueriesOverride ?? false; | ||
#endif | ||
} | ||
} |
Oops, something went wrong.