diff --git a/src/OpenTelemetry.Api/Internal/SemanticConventions.cs b/src/OpenTelemetry.Api/Internal/SemanticConventions.cs index d92e3502518..9f219a68d96 100644 --- a/src/OpenTelemetry.Api/Internal/SemanticConventions.cs +++ b/src/OpenTelemetry.Api/Internal/SemanticConventions.cs @@ -115,8 +115,9 @@ internal static class SemanticConventions public const string AttributeHttpRequestMethod = "http.request.method"; // replaces: "http.method" (AttributeHttpMethod) public const string AttributeHttpResponseStatusCode = "http.response.status_code"; // replaces: "http.status_code" (AttributeHttpStatusCode) public const string AttributeNetworkProtocolVersion = "network.protocol.version"; // replaces: "http.flavor" (AttributeHttpFlavor) - public const string AttributeServerAddress = "server.address"; // replaces: "net.host.name" (AttributeNetHostName) + public const string AttributeServerAddress = "server.address"; // replaces: "net.host.name" (AttributeNetHostName) and "net.peer.name" (AttributeNetPeerName) public const string AttributeServerPort = "server.port"; // replaces: "net.host.port" (AttributeNetHostPort) and "net.peer.port" (AttributeNetPeerPort) + public const string AttributeServerSocketAddress = "server.socket.address"; // replaces: "net.peer.ip" (AttributeNetPeerIp) public const string AttributeUrlFull = "url.full"; // replaces: "http.url" (AttributeHttpUrl) public const string AttributeUrlPath = "url.path"; // replaces: "http.target" (AttributeHttpTarget) public const string AttributeUrlScheme = "url.scheme"; // replaces: "http.scheme" (AttributeHttpScheme) diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md index 9d0ba655f9c..75057e166dc 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md +++ b/src/OpenTelemetry.Instrumentation.SqlClient/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +* Updated [Semantic Conventions](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/database/database-spans.md). + This library can emit either old, new, or both attributes. Users can control + which attributes are emitted by setting the environment variable + `OTEL_SEMCONV_STABILITY_OPT_IN`. + ([#4644](https://github.com/open-telemetry/opentelemetry-dotnet/pull/4644)) + ## 1.5.0-beta.1 Released 2023-Jun-05 diff --git a/src/OpenTelemetry.Instrumentation.SqlClient/SqlClientInstrumentationOptions.cs b/src/OpenTelemetry.Instrumentation.SqlClient/SqlClientInstrumentationOptions.cs index ed02895b391..9c88d6e0e5d 100644 --- a/src/OpenTelemetry.Instrumentation.SqlClient/SqlClientInstrumentationOptions.cs +++ b/src/OpenTelemetry.Instrumentation.SqlClient/SqlClientInstrumentationOptions.cs @@ -32,8 +32,6 @@ namespace OpenTelemetry.Instrumentation.SqlClient /// public class SqlClientInstrumentationOptions { - internal readonly HttpSemanticConvention HttpSemanticConvention; - /* * Match... * protocol[ ]:[ ]serverName @@ -67,6 +65,9 @@ public class SqlClientInstrumentationOptions private static readonly ConcurrentDictionary ConnectionDetailCache = new(StringComparer.OrdinalIgnoreCase); + private readonly bool emitOldAttributes; + private readonly bool emitNewAttributes; + /// /// Initializes a new instance of the class. /// @@ -79,7 +80,9 @@ internal SqlClientInstrumentationOptions(IConfiguration configuration) { Debug.Assert(configuration != null, "configuration was null"); - this.HttpSemanticConvention = GetSemanticConventionOptIn(configuration); + var httpSemanticConvention = GetSemanticConventionOptIn(configuration); + this.emitOldAttributes = httpSemanticConvention.HasFlag(HttpSemanticConvention.Old); + this.emitNewAttributes = httpSemanticConvention.HasFlag(HttpSemanticConvention.New); } /// @@ -134,19 +137,23 @@ internal SqlClientInstrumentationOptions(IConfiguration configuration) /// langword="false"/>. /// /// - /// EnableConnectionLevelAttributes is supported on all - /// runtimes. - /// The default behavior is to set the SqlConnection DataSource as - /// the tag. If - /// enabled, SqlConnection DataSource will be parsed and the server name - /// will be sent as the or tag, the instance - /// name will be sent as the tag, and - /// the port will be sent as the tag if it is not - /// 1433 (the default port). + /// + /// EnableConnectionLevelAttributes is supported on all runtimes. + /// + /// + /// The default behavior is to set the SqlConnection DataSource as the tag. + /// If enabled, SqlConnection DataSource will be parsed and the server name will be sent as the + /// or tag, + /// the instance name will be sent as the tag, + /// and the port will be sent as the tag if it is not 1433 (the default port). + /// + /// + /// If the environment variable OTEL_SEMCONV_STABILITY_OPT_IN is set to "http", the newer Semantic Convention v1.21.0 Attributes will be emitted. + /// SqlConnection DataSource will be parsed and the server name will be sent as the + /// or tag, + /// the instance name will be sent as the tag, + /// and the port will be sent as the tag if it is not 1433 (the default port). + /// /// public bool EnableConnectionLevelAttributes { get; set; } @@ -302,23 +309,45 @@ internal void AddConnectionLevelDetailsToActivity(string dataSource, Activity sq ConnectionDetailCache.TryAdd(dataSource, connectionDetails); } - if (!string.IsNullOrEmpty(connectionDetails.ServerHostName)) - { - sqlActivity.SetTag(SemanticConventions.AttributeNetPeerName, connectionDetails.ServerHostName); - } - else + if (!string.IsNullOrEmpty(connectionDetails.InstanceName)) { - sqlActivity.SetTag(SemanticConventions.AttributeNetPeerIp, connectionDetails.ServerIpAddress); + sqlActivity.SetTag(SemanticConventions.AttributeDbMsSqlInstanceName, connectionDetails.InstanceName); } - if (!string.IsNullOrEmpty(connectionDetails.InstanceName)) + if (this.emitOldAttributes) { - sqlActivity.SetTag(SemanticConventions.AttributeDbMsSqlInstanceName, connectionDetails.InstanceName); + if (!string.IsNullOrEmpty(connectionDetails.ServerHostName)) + { + sqlActivity.SetTag(SemanticConventions.AttributeNetPeerName, connectionDetails.ServerHostName); + } + else + { + sqlActivity.SetTag(SemanticConventions.AttributeNetPeerIp, connectionDetails.ServerIpAddress); + } + + if (!string.IsNullOrEmpty(connectionDetails.Port)) + { + sqlActivity.SetTag(SemanticConventions.AttributeNetPeerPort, connectionDetails.Port); + } } - if (!string.IsNullOrEmpty(connectionDetails.Port)) + // see the spec https://github.com/open-telemetry/semantic-conventions/blob/main/docs/database/database-spans.md + if (this.emitNewAttributes) { - sqlActivity.SetTag(SemanticConventions.AttributeNetPeerPort, connectionDetails.Port); + if (!string.IsNullOrEmpty(connectionDetails.ServerHostName)) + { + sqlActivity.SetTag(SemanticConventions.AttributeServerAddress, connectionDetails.ServerHostName); + } + else + { + sqlActivity.SetTag(SemanticConventions.AttributeServerSocketAddress, connectionDetails.ServerIpAddress); + } + + if (!string.IsNullOrEmpty(connectionDetails.Port)) + { + // TODO: Should we continue to emit this if the default port (1433) is being used? + sqlActivity.SetTag(SemanticConventions.AttributeServerPort, connectionDetails.Port); + } } } } diff --git a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientInstrumentationOptionsTests.cs b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientInstrumentationOptionsTests.cs index 42becbe969d..0a5ae328837 100644 --- a/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientInstrumentationOptionsTests.cs +++ b/test/OpenTelemetry.Instrumentation.SqlClient.Tests/SqlClientInstrumentationOptionsTests.cs @@ -15,8 +15,10 @@ // using System.Diagnostics; +using Microsoft.Extensions.Configuration; using OpenTelemetry.Trace; using Xunit; +using static OpenTelemetry.Internal.HttpSemanticConventionHelper; namespace OpenTelemetry.Instrumentation.SqlClient.Tests { @@ -102,5 +104,110 @@ public void SqlClientInstrumentationOptions_EnableConnectionLevelAttributes( Assert.Equal(expectedInstanceName, activity.GetTagValue(SemanticConventions.AttributeDbMsSqlInstanceName)); Assert.Equal(expectedPort, activity.GetTagValue(SemanticConventions.AttributeNetPeerPort)); } + + // Tests for v1.21.0 Semantic Conventions for database client calls. + // see the spec https://github.com/open-telemetry/semantic-conventions/blob/main/docs/database/database-spans.md + // This test emits the new attributes. + // This test method can replace the other (old) test method when this library is GA. + [Theory] + [InlineData(true, "localhost", "localhost", null, null, null)] + [InlineData(true, "127.0.0.1,1433", null, "127.0.0.1", null, null)] + [InlineData(true, "127.0.0.1,1434", null, "127.0.0.1", null, "1434")] + [InlineData(true, "127.0.0.1\\instanceName, 1818", null, "127.0.0.1", "instanceName", "1818")] + [InlineData(false, "localhost", "localhost", null, null, null)] + public void SqlClientInstrumentationOptions_EnableConnectionLevelAttributes_New( + bool enableConnectionLevelAttributes, + string dataSource, + string expectedServerHostName, + string expectedServerIpAddress, + string expectedInstanceName, + string expectedPort) + { + var source = new ActivitySource("sql-client-instrumentation"); + var activity = source.StartActivity("Test Sql Activity"); + + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary { [SemanticConventionOptInKeyName] = "http" }) + .Build(); + + var options = new SqlClientInstrumentationOptions(configuration) + { + EnableConnectionLevelAttributes = enableConnectionLevelAttributes, + }; + options.AddConnectionLevelDetailsToActivity(dataSource, activity); + + if (!enableConnectionLevelAttributes) + { + Assert.Equal(expectedServerHostName, activity.GetTagValue(SemanticConventions.AttributePeerService)); + } + else + { + Assert.Equal(expectedServerHostName, activity.GetTagValue(SemanticConventions.AttributeServerAddress)); + } + + Assert.Equal(expectedServerIpAddress, activity.GetTagValue(SemanticConventions.AttributeServerSocketAddress)); + Assert.Equal(expectedInstanceName, activity.GetTagValue(SemanticConventions.AttributeDbMsSqlInstanceName)); + Assert.Equal(expectedPort, activity.GetTagValue(SemanticConventions.AttributeServerPort)); + } + + // Tests for v1.21.0 Semantic Conventions for database client calls. + // see the spec https://github.com/open-telemetry/semantic-conventions/blob/main/docs/database/database-spans.md + // This test emits both the new and older attributes. + // This test method can be deleted when this library is GA. + [Theory] + [InlineData(true, "localhost", "localhost", null, null, null)] + [InlineData(true, "127.0.0.1,1433", null, "127.0.0.1", null, null)] + [InlineData(true, "127.0.0.1,1434", null, "127.0.0.1", null, "1434")] + [InlineData(true, "127.0.0.1\\instanceName, 1818", null, "127.0.0.1", "instanceName", "1818")] + [InlineData(false, "localhost", "localhost", null, null, null)] + public void SqlClientInstrumentationOptions_EnableConnectionLevelAttributes_Dupe( + bool enableConnectionLevelAttributes, + string dataSource, + string expectedServerHostName, + string expectedServerIpAddress, + string expectedInstanceName, + string expectedPort) + { + var source = new ActivitySource("sql-client-instrumentation"); + var activity = source.StartActivity("Test Sql Activity"); + + var configuration = new ConfigurationBuilder() + .AddInMemoryCollection(new Dictionary { [SemanticConventionOptInKeyName] = "http/dup" }) + .Build(); + + var options = new SqlClientInstrumentationOptions(configuration) + { + EnableConnectionLevelAttributes = enableConnectionLevelAttributes, + }; + options.AddConnectionLevelDetailsToActivity(dataSource, activity); + + // New + if (!enableConnectionLevelAttributes) + { + Assert.Equal(expectedServerHostName, activity.GetTagValue(SemanticConventions.AttributePeerService)); + } + else + { + Assert.Equal(expectedServerHostName, activity.GetTagValue(SemanticConventions.AttributeServerAddress)); + } + + Assert.Equal(expectedServerIpAddress, activity.GetTagValue(SemanticConventions.AttributeServerSocketAddress)); + Assert.Equal(expectedInstanceName, activity.GetTagValue(SemanticConventions.AttributeDbMsSqlInstanceName)); + Assert.Equal(expectedPort, activity.GetTagValue(SemanticConventions.AttributeServerPort)); + + // Old + if (!enableConnectionLevelAttributes) + { + Assert.Equal(expectedServerHostName, activity.GetTagValue(SemanticConventions.AttributePeerService)); + } + else + { + Assert.Equal(expectedServerHostName, activity.GetTagValue(SemanticConventions.AttributeNetPeerName)); + } + + Assert.Equal(expectedServerIpAddress, activity.GetTagValue(SemanticConventions.AttributeNetPeerIp)); + Assert.Equal(expectedInstanceName, activity.GetTagValue(SemanticConventions.AttributeDbMsSqlInstanceName)); + Assert.Equal(expectedPort, activity.GetTagValue(SemanticConventions.AttributeNetPeerPort)); + } } }