diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md index 1a3b4168e7..0cd67e6192 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md @@ -26,6 +26,7 @@ [spans](https://github.com/open-telemetry/semantic-conventions/blob/v1.28.0/docs/database/database-spans.md). ([#2229](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2229), [#2277](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2277), + [#2262](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2262), [#2279](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2279)) * **Breaking change**: The `peer.service` and `server.socket.address` attributes are no longer emitted. Users should rely on the `server.address` attribute diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs b/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs index b8b20b72ac..defc740632 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs +++ b/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlClientDiagnosticListener.cs @@ -4,10 +4,11 @@ #if !NETFRAMEWORK using System.Data; using System.Diagnostics; -using OpenTelemetry.Trace; #if NET using System.Diagnostics.CodeAnalysis; #endif +using System.Globalization; +using OpenTelemetry.Trace; namespace OpenTelemetry.Instrumentation.SqlClient.Implementation; @@ -32,6 +33,7 @@ internal sealed class SqlClientDiagnosticListener : ListenerHandler private readonly PropertyFetcher commandTypeFetcher = new("CommandType"); private readonly PropertyFetcher commandTextFetcher = new("CommandText"); private readonly PropertyFetcher exceptionFetcher = new("Exception"); + private readonly PropertyFetcher exceptionNumberFetcher = new("Number"); private readonly SqlClientTraceInstrumentationOptions options; public SqlClientDiagnosticListener(string sourceName, SqlClientTraceInstrumentationOptions? options) @@ -189,6 +191,13 @@ public override void OnEventWritten(string name, object? payload) { if (this.exceptionFetcher.TryFetch(payload, out Exception? exception) && exception != null) { + activity.AddTag(SemanticConventions.AttributeErrorType, exception.GetType().FullName); + + if (this.exceptionNumberFetcher.TryFetch(exception, out var exceptionNumber)) + { + activity.AddTag(SemanticConventions.AttributeDbResponseStatusCode, exceptionNumber.ToString(CultureInfo.InvariantCulture)); + } + activity.SetStatus(ActivityStatusCode.Error, exception.Message); if (this.options.RecordException) diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlEventSourceListener.netfx.cs b/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlEventSourceListener.netfx.cs index 19a6f7526d..371cae85a3 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlEventSourceListener.netfx.cs +++ b/src/OpenTelemetry.Instrumentation.SqlClient/Implementation/SqlEventSourceListener.netfx.cs @@ -175,8 +175,14 @@ private void OnEndExecute(EventWrittenEventArgs eventData) { if ((compositeState & 0b010) == 0b010) { - var errorText = $"SqlExceptionNumber {eventData.Payload[2]} thrown."; - activity.SetStatus(ActivityStatusCode.Error, errorText); + var errorNumber = $"{eventData.Payload[2]}"; + activity.SetStatus(ActivityStatusCode.Error, errorNumber); + activity.SetTag(SemanticConventions.AttributeDbResponseStatusCode, errorNumber); + + var exceptionType = eventData.EventSource.Name == MdsEventSourceName + ? "Microsoft.Data.SqlClient.SqlException" + : "System.Data.SqlClient.SqlException"; + activity.SetTag(SemanticConventions.AttributeErrorType, exceptionType); } else { diff --git a/src/Shared/SemanticConventions.cs b/src/Shared/SemanticConventions.cs index cd3847cc2a..fe87612551 100644 --- a/src/Shared/SemanticConventions.cs +++ b/src/Shared/SemanticConventions.cs @@ -141,7 +141,7 @@ internal static class SemanticConventions public const string AttributeDbCollectionName = "db.collection.name"; public const string AttributeDbNamespace = "db.namespace"; public const string AttributeDbOperationName = "db.operation.name"; - public const string AttributeResponseStatusCode = "db.response.status_code"; + public const string AttributeDbResponseStatusCode = "db.response.status_code"; public const string AttributeDbOperationBatchSize = "db.operation.batch.size"; public const string AttributeDbQuerySummary = "db.query.summary"; public const string AttributeDbQueryText = "db.query.text"; diff --git a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs index 9086c31490..a22e70cac9 100644 --- a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs +++ b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientIntegrationTests.cs @@ -93,6 +93,23 @@ public void SuccessfulCommandTest( SqlClientTests.VerifyActivityData(commandType, commandText, captureStoredProcedureCommandName, captureTextCommandContent, isFailure, recordException, shouldEnrich, activity); SqlClientTests.VerifySamplingParameters(sampler.LatestSamplingParameters); + + if (isFailure) + { +#if NET + var status = activity.GetStatus(); + Assert.Equal(ActivityStatusCode.Error, activity.Status); + Assert.Equal("Divide by zero error encountered.", activity.StatusDescription); + Assert.EndsWith("SqlException", activity.GetTagValue(SemanticConventions.AttributeErrorType) as string); + Assert.Equal("8134", activity.GetTagValue(SemanticConventions.AttributeDbResponseStatusCode)); +#else + var status = activity.GetStatus(); + Assert.Equal(ActivityStatusCode.Error, activity.Status); + Assert.Equal("8134", activity.StatusDescription); + Assert.EndsWith("SqlException", activity.GetTagValue(SemanticConventions.AttributeErrorType) as string); + Assert.Equal("8134", activity.GetTagValue(SemanticConventions.AttributeDbResponseStatusCode)); +#endif + } } private string GetConnectionString()