Skip to content

Commit

Permalink
[SqlClient] Add support for Filter expression
Browse files Browse the repository at this point in the history
  • Loading branch information
Driedas committed Oct 7, 2022
1 parent 3671e55 commit 87fc4b6
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ OpenTelemetry.Instrumentation.SqlClient.SqlClientInstrumentationOptions.EnableCo
OpenTelemetry.Instrumentation.SqlClient.SqlClientInstrumentationOptions.EnableConnectionLevelAttributes.set -> void
OpenTelemetry.Instrumentation.SqlClient.SqlClientInstrumentationOptions.Enrich.get -> System.Action<System.Diagnostics.Activity, string, object>
OpenTelemetry.Instrumentation.SqlClient.SqlClientInstrumentationOptions.Enrich.set -> void
OpenTelemetry.Instrumentation.SqlClient.SqlClientInstrumentationOptions.Filter.get -> System.Func<object, bool>
OpenTelemetry.Instrumentation.SqlClient.SqlClientInstrumentationOptions.Filter.set -> void
OpenTelemetry.Instrumentation.SqlClient.SqlClientInstrumentationOptions.RecordException.get -> bool
OpenTelemetry.Instrumentation.SqlClient.SqlClientInstrumentationOptions.RecordException.set -> void
OpenTelemetry.Instrumentation.SqlClient.SqlClientInstrumentationOptions.SetDbStatementForStoredProcedure.get -> bool
Expand Down
2 changes: 2 additions & 0 deletions src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

* Add support for Filter expression

## 1.0.0-rc9.7

Released 2022-Sep-29
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,24 @@ public override void OnEventWritten(string name, object payload)

if (activity.IsAllDataRequested)
{
try
{
if (this.options.Filter?.Invoke(command) == false)
{
SqlClientInstrumentationEventSource.Log.CommandIsFilteredOut(activity.OperationName);
activity.IsAllDataRequested = false;
activity.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded;
return;
}
}
catch (Exception ex)
{
SqlClientInstrumentationEventSource.Log.CommandFilterException(ex.Message);
activity.IsAllDataRequested = false;
activity.ActivityTraceFlags &= ~ActivityTraceFlags.Recorded;
return;
}

_ = this.connectionFetcher.TryFetch(command, out var connection);
_ = this.databaseFetcher.TryFetch(connection, out var database);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,17 @@ public void EnrichmentException(string exception)
{
this.WriteEvent(5, exception);
}

[Event(6, Message = "Command is filtered out.", Level = EventLevel.Verbose)]
public void CommandIsFilteredOut(string eventName)
{
this.WriteEvent(6, eventName);
}

[Event(7, Message = "Command filter threw exception. Request will not be collected. Exception {0}.", Level = EventLevel.Error)]
public void CommandFilterException(string exception)
{
this.WriteEvent(7, exception);
}
}
}
28 changes: 28 additions & 0 deletions src/OpenTelemetry.Instrumentation.SqlClient/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,34 @@ using var tracerProvider = Sdk.CreateTracerProviderBuilder()
.Build();
```

## Filter

This option allows to filter out activities based on the properties of the `SqlComnmand`
object being instrumented using a `Func<object, bool>`. The function receives an instance
of the raw `SqlCommand` and should return `true` if the telemetry is to be collected, and `false`
if it should not.
The example below filters out all commands that are not stored procedures.

```csharp
using var traceProvider = Sdk.CreateTracerProviderBuilder()
.AddSqlClientInstrumentation(
opt =>
{
opt.Filter = cmd =>
{
if (cmd is SqlCommand command)
{
return command.CommandType == CommandType.StoredProcedure;
}

return false;
};
})
.AddConsoleExporter()
.Build();
{
```

## References

* [OpenTelemetry Project](https://opentelemetry.io/)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,18 @@ public class SqlClientInstrumentationOptions
/// </remarks>
public bool EnableConnectionLevelAttributes { get; set; }

/// <summary>
/// Gets or sets a Filter function that determines whether or not to collect telemetry about a command
/// The Filter gets the SqlCommand, and should return a boolean.
/// If Filter returns true, the request is collected.
/// If Filter returns false or throw exception, the request is filtered out.
/// </summary>
/// <remarks>
/// <para>object: the raw <c>SqlCommand</c> object to interrogate for a decision on whether to trace or not.</para>
/// </remarks>
/// <returns>true to collect request, false to filter out.</returns>
public Func<object, bool> Filter { get; set; }

/// <summary>
/// Gets or sets an action to enrich an Activity.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
// </copyright>

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
Expand Down Expand Up @@ -308,6 +309,87 @@ public void SqlClientCreatesActivityWithDbSystem(

VerifySamplingParameters(sampler.LatestSamplingParameters);
}

[Fact]
public void ShouldCollectTelemetryWhenFilterEvaluatesToTrue()
{
var activities = RunCommandWithFilter(
cmd =>
{
cmd.CommandText = "select 2";
},
cmd =>
{
if (cmd is SqlCommand command)
{
return command.CommandText == "select 2";
}
return true;
});

Assert.Single(activities);
Assert.True(activities[0].IsAllDataRequested);
Assert.True(activities[0].ActivityTraceFlags.HasFlag(ActivityTraceFlags.Recorded));
}

[Fact]
public void ShouldNotCollectTelemetryWhenFilterEvaluatesToFalse()
{
var activities = RunCommandWithFilter(
cmd =>
{
cmd.CommandText = "select 1";
},
cmd =>
{
if (cmd is SqlCommand command)
{
return command.CommandText == "select 2";
}
return true;
});

Assert.Empty(activities);
}

[Fact]
public void ShouldNotCollectTelemetryAndShouldNotPropagateExceptionWhenFilterThrowsException()
{
var activities = RunCommandWithFilter(
cmd =>
{
cmd.CommandText = "select 1";
},
cmd => throw new InvalidOperationException("foobar"));

Assert.Empty(activities);
}

private static Activity[] RunCommandWithFilter(Action<SqlCommand> sqlCommandSetup, Func<object, bool> filter)
{
using var sqlConnection = new SqlConnection(TestConnectionString);
using var sqlCommand = sqlConnection.CreateCommand();

var activities = new List<Activity>();
using (Sdk.CreateTracerProviderBuilder()
.AddSqlClientInstrumentation(
opt =>
{
opt.Filter = filter;
})
.AddInMemoryExporter(activities)
.Build())
{
sqlCommandSetup(sqlCommand);

sqlConnection.Open();
sqlCommand.ExecuteNonQuery();
}

return activities.ToArray();
}
#endif

private static void VerifyActivityData(
Expand Down

0 comments on commit 87fc4b6

Please sign in to comment.