Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
82 changes: 82 additions & 0 deletions ClickHouse.Driver.Tests/ActivitySourceHelperTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using ClickHouse.Driver.Diagnostic;

namespace ClickHouse.Driver.Tests;
Expand Down Expand Up @@ -66,4 +69,83 @@ public void SetQuery_AttachesTruncatedSql_WhenEnabled()
ClassicAssert.That(tagValue, Is.EqualTo("SELEC"));
activity.Stop();
}

[TestCase("", null, "")]
[TestCase("system", null, "system")]
[TestCase("", "system", "system")]
[TestCase("system", "default", "default")]
[TestCase("system", "", "")]
public async Task ExecuteScalarAsync_ConfiguredAndQueryOptionsDatabase_SetsDbNameToDatabaseSentWithRequest(
string configuredDatabase, string queryDatabase, string expectedDbName)
{
var settings = TestUtilities.GetTestClickHouseClientSettings();
settings.Database = configuredDatabase;
using var queryClient = new ClickHouseClient(settings);

var spans = new List<Activity>();
using var listener = ListenToDriverSpans(spans);

await queryClient.ExecuteScalarAsync("SELECT 1", options: new QueryOptions { Database = queryDatabase });

Assert.That(spans.Single().GetTagItem("db.name"), Is.EqualTo(expectedDbName));
}

[Test]
public async Task InsertBinaryAsync_WithInsertOptionsDatabase_SetsDbNameToInsertDatabaseOnEverySpan()
{
var table = CreateTableName();
await client.ExecuteNonQueryAsync($"CREATE TABLE {table} (x UInt32) ENGINE = Memory");
var bareTable = TestUtilities.BareTableName(table);

var spans = new List<Activity>();
using var listener = ListenToDriverSpans(spans);

await client.InsertBinaryAsync(
bareTable,
["x"],
[[1u]],
new InsertOptions { Database = TestUtilities.TestDatabase });

Assert.That(
spans.Select(s => (s.OperationName, s.GetTagItem("db.name"))),
Is.EqualTo(new[]
{
("PostSqlQueryAsync", (object)TestUtilities.TestDatabase),
("PostStreamAsync", TestUtilities.TestDatabase),
}));
}

[Test]
public async Task ExecuteScalarAsync_AfterChangeDatabase_SetsDbNameToSelectedDatabase()
{
using var conn = client.CreateConnection();
await conn.OpenAsync();
conn.ChangeDatabase("system");
using var command = conn.CreateCommand("SELECT currentDatabase()");

var spans = new List<Activity>();
using var listener = ListenToDriverSpans(spans);

var serverDatabase = await command.ExecuteScalarAsync();

Assert.That(serverDatabase, Is.EqualTo("system"));
Assert.That(spans.Single().GetTagItem("db.name"), Is.EqualTo("system"));
}

private static ActivityListener ListenToDriverSpans(List<Activity> spans)
{
var listener = new ActivityListener
{
ShouldListenTo = s => s.Name == ClickHouseDiagnosticsOptions.ActivitySourceName,
SampleUsingParentId = (ref ActivityCreationOptions<string> _) => ActivitySamplingResult.AllDataAndRecorded,
Sample = (ref ActivityCreationOptions<ActivityContext> _) => ActivitySamplingResult.AllDataAndRecorded,
ActivityStopped = a =>
{
lock (spans)
spans.Add(a);
},
};
ActivitySource.AddActivityListener(listener);
return listener;
}
}
10 changes: 5 additions & 5 deletions ClickHouse.Driver/ClickHouseClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -298,10 +298,10 @@ internal async Task<QueryResult> PostSqlQueryAsync(
bool rawBody = false,
CancellationToken cancellationToken = default)
{
using var activity = this.StartActivity("PostSqlQueryAsync");

var uriBuilder = CreateUriBuilder(queryOverride: options);

using var activity = this.StartActivity("PostSqlQueryAsync", uriBuilder.Database);

var logger = GetLogger(ClickHouseLogCategories.Command);
var isDebugLoggingEnabled = logger?.IsEnabled(LogLevel.Debug) ?? false;
Stopwatch stopwatch = null;
Expand Down Expand Up @@ -931,11 +931,11 @@ private Task<HttpResponseMessage> PostStreamAsync(string sql, Func<Stream, Cance

private async Task<HttpResponseMessage> PostStreamAsync(string sql, HttpContent content, string contentEncoding, QueryOptions queryOptions, CancellationToken token)
{
using var activity = this.StartActivity("PostStreamAsync");
activity.SetQuery(sql);

var builder = CreateUriBuilder(sql, queryOptions);

using var activity = this.StartActivity("PostStreamAsync", builder.Database);
activity.SetQuery(sql);

using var postMessage = CreatePostStreamRequest(builder, queryOptions);
// rawBody: this method returns the HttpResponseMessage itself — PostStreamAsync and
// InsertRawStreamAsync are public — so its body belongs to the caller, exactly like a raw result,
Expand Down
8 changes: 6 additions & 2 deletions ClickHouse.Driver/Diagnostic/ActivitySourceHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ internal static class ActivitySourceHelper

internal static ActivitySource ActivitySource { get; } = CreateActivitySource();

internal static Activity StartActivity(this ClickHouseClient client, string name)
/// <summary>
/// Starts a client activity. <paramref name="database"/> is the database sent with the request, reported as
/// <c>db.name</c>; when it is <c>null</c>, the client's configured database is reported.
/// </summary>
internal static Activity StartActivity(this ClickHouseClient client, string name, string database = null)
{
if (client is null) throw new ArgumentNullException(nameof(client));
if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));
Expand All @@ -38,7 +42,7 @@ internal static Activity StartActivity(this ClickHouseClient client, string name
activity.SetTag(TagThreadId, Environment.CurrentManagedThreadId.ToString(CultureInfo.InvariantCulture));
activity.SetTag(TagDbSystem, "clickhouse");
activity.SetTag(TagDbConnectionString, client.RedactedConnectionString);
activity.SetTag(TagDbName, client.Settings.Database);
activity.SetTag(TagDbName, database ?? client.Settings.Database);
activity.SetTag(TagUser, client.Settings.Username);
activity.SetTag(TagService, $"{client.ServerUri.Host}:{client.ServerUri.Port}{client.ServerUri.AbsolutePath}");
}
Expand Down
4 changes: 4 additions & 0 deletions changelog.d/685-otel-db-name.fixes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
* Fixed the OpenTelemetry `db.name` span tag reporting the client's configured database when a query is
sent to a different one ([#685](https://github.com/ClickHouse/clickhouse-cs/issues/685)). The tag now
reports the database sent with the request: `QueryOptions.Database` / `InsertOptions.Database` when
set, or the database selected with `ClickHouseConnection.ChangeDatabase` for `ClickHouseCommand`.
2 changes: 1 addition & 1 deletion docs/http.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2371,7 +2371,7 @@ Each span includes standard OpenTelemetry database attributes plus ClickHouse-sp
| Attribute | Description |
|-----------|-------------|
| `db.system` | Always `"clickhouse"` |
| `db.name` | Database name |
| `db.name` | Database sent with the request: `QueryOptions.Database` / `InsertOptions.Database` when set (for `ClickHouseCommand`, the database selected with `ClickHouseConnection.ChangeDatabase`), otherwise the client's `Database` setting. Empty when neither is set |
| `db.user` | Username |
| `db.statement` | SQL query (if enabled) |
| `db.clickhouse.read_rows` | Rows read by the query |
Expand Down
Loading