-
Notifications
You must be signed in to change notification settings - Fork 6
Description
Hey folks,
I would like to discuss telemetry support with you. As we are using Open Telemetry for traces and metrics and we would benefit when generated code would have some sort of integration.
Unfortunately, not all database drivers have native integration for this. For npgsql, for example, there is a NuGet for telemetry support. To my knowledge, however, this does not exist for microsoft.data.sqlite or for the mysqlconnector.
Therefore, I have been considering a possible integration into the generated code. I would like to make a suggestion below. If you are interested, I could start working on an implementation.
// Telemetry.cs
public static class TelemetryConstants
{
public const string SourceName = "{{GeneratedProjectName}}";
}
internal static class Telemetry
{
private static readonly ActivitySource _activitySource = new (TelemetryConstants.SourceName);
internal static Activity? StartActivity(string query, Dictionary<string, object?> parameter,
[CallerMemberName] string name = "")
{
if (!_activitySource.HasListener())
return null; // optimize Szenarios when no one is interested in the information
List<KeyValuePair<string, object?>> tags =
[
new ("db.system", "{{Driver}}"),
new ("db.statement", query),
];
foreach (var (key, value) in parameter)
{
tags.Add(new KeyValuePair<string, object?>($"db.parameter.{key}", value?.ToString() ?? "null"));
}
return _activitySource.StartActivity(ActivityKind.Internal, default, tags, null, default, name);
}
}And for the generated code we would just need
public async Task<List<ListItemsPaginatedRow>> ListItemsPaginated(ListItemsPaginatedArgs args)
{
var queryParams = new Dictionary<string, object?>();
queryParams.Add("offset", args.Offset);
queryParams.Add("limit", args.Limit);
using var activity = Telemetry.StartActivity(ListItemsPaginatedSql, queryParams);
try
{
if (this.Transaction == null)
{
using (var connection = new SqliteConnection(ConnectionString))
{
var result = await connection.QueryAsync<ListItemsPaginatedRow>(ListItemsPaginatedSql, queryParams);
return result.AsList();
}
}
if (this.Transaction?.Connection == null || this.Transaction?.Connection.State != System.Data.ConnectionState.Open)
throw new InvalidOperationException("Transaction is provided, but its connection is null.");
return (await this.Transaction.Connection.QueryAsync<ListItemsPaginatedRow>(ListItemsPaginatedSql, queryParams, transaction: this.Transaction)).AsList();
}
catch (Exception e)
{
activity?.SetStatus(System.Diagnostics.ActivityStatusCode.Error, e.Message);
activity?.RecordException(e);
throw;
}
}This would result in something like below for the observability stack in jaeger. It provides super useful information for optimizing queries by seeing the exact timing. Errors would also be reported and we are able to trace the exact query with all parameters.
