Open CumpsD opened 6 years ago
It's been a while since i've used entity framework. I assume it uses ADO .NET under the hood. If that's the case and it offers a way to provide an alternative DbConnection object then you could inject a TraceDbConnection instead.
I've tried this, but apparently it needs a DbConnection
and TraceDbConnection
is an IDbConnection
services
.AddDbContextPool<BackofficeContext>(options => options
.UseLoggerFactory(loggerFactory)
.UseSqlServer(new TraceDbConnection(new SqlConnection(backofficeProjectionsConnectionString)), sqlServerOptions =>
{
sqlServerOptions.EnableRetryOnFailure();
sqlServerOptions.MigrationsHistoryTable(MigrationTables.Backoffice, Schema.Backoffice);
}));
Maybe this can help you:
namespace DataDog.Tracing.Sql
{
using System;
using System.Data;
using System.Data.Common;
public class TraceDbConnection : DbConnection
{
private const string ServiceName = "sql";
readonly ISpanSource _spanSource;
readonly DbConnection _connection;
public TraceDbConnection(DbConnection connection)
: this(connection, TraceContextSpanSource.Instance) { }
public TraceDbConnection(DbConnection connection, ISpanSource spanSource)
{
_connection = connection ?? throw new ArgumentNullException(nameof(connection));
_spanSource = spanSource ?? throw new ArgumentNullException(nameof(spanSource));
}
public IDbConnection InnerConnection => _connection;
public new void Dispose() => _connection.Dispose();
protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) => _connection.BeginTransaction(isolationLevel);
protected override DbCommand CreateDbCommand() => new TraceDbCommand(_connection.CreateCommand(), _spanSource);
public override void ChangeDatabase(string databaseName) => _connection.ChangeDatabase(databaseName);
public override void Close() => _connection.Close();
public override void Open()
{
var span = _spanSource.Begin("sql.connect", ServiceName, _connection.Database, ServiceName);
try
{
_connection.Open();
}
catch (Exception ex)
{
span?.SetError(ex);
throw;
}
finally
{
span?.Dispose();
}
}
public override string ConnectionString
{
get => _connection.ConnectionString;
set => _connection.ConnectionString = value;
}
public override int ConnectionTimeout => _connection.ConnectionTimeout;
public override string Database => _connection.Database;
public override string DataSource => _connection.DataSource;
public override string ServerVersion => _connection.ServerVersion;
public override ConnectionState State => _connection.State;
}
public class TraceDbCommand : DbCommand
{
private const string ServiceName = "sql";
private readonly DbCommand _command;
private readonly ISpanSource _spanSource;
public TraceDbCommand(DbCommand command)
: this(command, TraceContextSpanSource.Instance) { }
public TraceDbCommand(DbCommand command, ISpanSource spanSource)
{
_command = command;
_spanSource = spanSource;
}
public IDbCommand InnerCommand => _command;
public new void Dispose() => _command.Dispose();
public override void Cancel() => _command.Cancel();
protected override DbParameter CreateDbParameter() => _command.CreateParameter();
protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
{
const string name = "sql." + nameof(ExecuteReader);
var span = _spanSource.Begin(name, ServiceName, _command.Connection.Database, ServiceName);
try
{
if (span != null)
{
const string metaKey = "sql." + nameof(CommandBehavior);
span.SetMeta(metaKey, behavior.ToString("x"));
SetMeta(span);
}
return _command.ExecuteReader(behavior);
}
catch (Exception ex)
{
span?.SetError(ex);
throw;
}
finally
{
span?.Dispose();
}
}
private void SetMeta(ISpan span)
{
span.SetMeta("sql.CommandText", CommandText);
span.SetMeta("sql.CommandType", CommandType.ToString());
}
public override int ExecuteNonQuery()
{
const string name = "sql." + nameof(ExecuteNonQuery);
var span = _spanSource.Begin(name, ServiceName, _command.Connection.Database, ServiceName);
try
{
var result = _command.ExecuteNonQuery();
if (span != null)
{
span.SetMeta("sql.RowsAffected", result.ToString());
SetMeta(span);
}
return result;
}
catch (Exception ex)
{
span?.SetError(ex);
throw;
}
finally
{
span?.Dispose();
}
}
public override object ExecuteScalar()
{
const string name = "sql." + nameof(ExecuteScalar);
var span = _spanSource.Begin(name, ServiceName, _command.Connection.Database, ServiceName);
try
{
if (span != null) SetMeta(span);
return _command.ExecuteScalar();
}
catch (Exception ex)
{
span?.SetError(ex);
throw;
}
finally
{
span?.Dispose();
}
}
public override void Prepare() => _command.Prepare();
protected override DbParameterCollection DbParameterCollection => _command.Parameters;
public override bool DesignTimeVisible { get; set; }
public override string CommandText
{
get => _command.CommandText;
set => _command.CommandText = value;
}
public override int CommandTimeout
{
get => _command.CommandTimeout;
set => _command.CommandTimeout = value;
}
public override CommandType CommandType
{
get => _command.CommandType;
set => _command.CommandType = value;
}
protected override DbConnection DbConnection
{
get => _command.Connection;
set => _command.Connection = value;
}
protected override DbTransaction DbTransaction
{
get => _command.Transaction;
set => _command.Transaction = value;
}
public override UpdateRowSource UpdatedRowSource
{
get => _command.UpdatedRowSource;
set => _command.UpdatedRowSource = value;
}
}
}
@bcuff I can confirm with the above code I can trace EF :)
Perhaps it is something which can be added?
@CumpsD sure. Feel free to submit that as a pull request.
I invited you to the repository so can push your changes to a new branch without having to fork.
There you go: https://github.com/bcuff/dd-trace-csharp/pull/4
I also added 3 smaller PRs
Nice library, thanks!
How would you use this with Entity Framework?