From a9ccb14c934edb5b404b40507340c495cc682199 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Wed, 2 Oct 2024 15:28:31 +0000
Subject: [PATCH 1/9] [Instrumentation.EntityFrameworkCore] Update few
attributes to align with latest Semantic Conventions
---
.../EntityFrameworkInstrumentationOptions.cs | 4 ++--
.../EntityFrameworkDiagnosticListener.cs | 21 ++++++++++---------
.../EntityFrameworkDiagnosticListenerTests.cs | 6 +++---
3 files changed, 16 insertions(+), 15 deletions(-)
diff --git a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/EntityFrameworkInstrumentationOptions.cs b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/EntityFrameworkInstrumentationOptions.cs
index 04b9cabf4a..0f92a2a95d 100644
--- a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/EntityFrameworkInstrumentationOptions.cs
+++ b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/EntityFrameworkInstrumentationOptions.cs
@@ -12,12 +12,12 @@ namespace OpenTelemetry.Instrumentation.EntityFrameworkCore;
public class EntityFrameworkInstrumentationOptions
{
///
- /// Gets or sets a value indicating whether or not the should add the names of commands as the tag. Default value: True.
+ /// Gets or sets a value indicating whether or not the should add the names of commands as the tag. Default value: True.
///
public bool SetDbStatementForStoredProcedure { get; set; } = true;
///
- /// Gets or sets a value indicating whether or not the should add the text of commands as the tag. Default value: False.
+ /// Gets or sets a value indicating whether or not the should add the text of commands as the tag. Default value: False.
///
public bool SetDbStatementForText { get; set; }
diff --git a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/Implementation/EntityFrameworkDiagnosticListener.cs b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/Implementation/EntityFrameworkDiagnosticListener.cs
index 6f04b69f86..ebb26c585d 100644
--- a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/Implementation/EntityFrameworkDiagnosticListener.cs
+++ b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/Implementation/EntityFrameworkDiagnosticListener.cs
@@ -18,10 +18,10 @@ internal sealed class EntityFrameworkDiagnosticListener : ListenerHandler
internal const string EntityFrameworkCoreCommandExecuted = "Microsoft.EntityFrameworkCore.Database.Command.CommandExecuted";
internal const string EntityFrameworkCoreCommandError = "Microsoft.EntityFrameworkCore.Database.Command.CommandError";
- internal const string AttributePeerService = "peer.service";
+ internal const string AttributeServerAddress = "server.address";
internal const string AttributeDbSystem = "db.system";
- internal const string AttributeDbName = "db.name";
- internal const string AttributeDbStatement = "db.statement";
+ internal const string AttributeDbNamespace = "db.namespace";
+ internal const string AttributeDbQueryText = "db.query.text";
internal static readonly Assembly Assembly = typeof(EntityFrameworkDiagnosticListener).Assembly;
internal static readonly string ActivitySourceName = Assembly.GetName().Name;
@@ -33,7 +33,7 @@ internal sealed class EntityFrameworkDiagnosticListener : ListenerHandler
private readonly PropertyFetcher dbContextFetcher = new("Context");
private readonly PropertyFetcher dbContextDatabaseFetcher = new("Database");
private readonly PropertyFetcher providerNameFetcher = new("ProviderName");
- private readonly PropertyFetcher dataSourceFetcher = new("DataSource");
+ private readonly PropertyFetcher hostFetcher = new("Host");
private readonly PropertyFetcher databaseFetcher = new("Database");
private readonly PropertyFetcher commandTypeFetcher = new("CommandType");
private readonly PropertyFetcher commandTextFetcher = new("CommandText");
@@ -138,11 +138,12 @@ public override void OnEventWritten(string name, object? payload)
break;
}
- var dataSource = (string)this.dataSourceFetcher.Fetch(connection);
- activity.AddTag(AttributeDbName, database);
- if (!string.IsNullOrEmpty(dataSource))
+ activity.AddTag(AttributeDbNamespace, database);
+
+ var host = (string)this.hostFetcher.Fetch(connection);
+ if (!string.IsNullOrEmpty(host))
{
- activity.AddTag(AttributePeerService, dataSource);
+ activity.AddTag(AttributeServerAddress, host);
}
}
}
@@ -196,7 +197,7 @@ public override void OnEventWritten(string name, object? payload)
case CommandType.StoredProcedure:
if (this.options.SetDbStatementForStoredProcedure)
{
- activity.AddTag(AttributeDbStatement, commandText);
+ activity.AddTag(AttributeDbQueryText, commandText);
}
break;
@@ -204,7 +205,7 @@ public override void OnEventWritten(string name, object? payload)
case CommandType.Text:
if (this.options.SetDbStatementForText)
{
- activity.AddTag(AttributeDbStatement, commandText);
+ activity.AddTag(AttributeDbQueryText, commandText);
}
break;
diff --git a/test/OpenTelemetry.Instrumentation.EntityFrameworkCore.Tests/EntityFrameworkDiagnosticListenerTests.cs b/test/OpenTelemetry.Instrumentation.EntityFrameworkCore.Tests/EntityFrameworkDiagnosticListenerTests.cs
index 30accde1dd..46cf9c06fc 100644
--- a/test/OpenTelemetry.Instrumentation.EntityFrameworkCore.Tests/EntityFrameworkDiagnosticListenerTests.cs
+++ b/test/OpenTelemetry.Instrumentation.EntityFrameworkCore.Tests/EntityFrameworkDiagnosticListenerTests.cs
@@ -66,7 +66,7 @@ public void EntityFrameworkEnrichDisplayNameWithEnrichWithIDbCommand()
{
var stateDisplayName = $"{command.CommandType} main";
activity1.DisplayName = stateDisplayName;
- activity1.SetTag("db.name", stateDisplayName);
+ activity1.SetTag("db.namespace", stateDisplayName);
};
}).Build();
@@ -235,9 +235,9 @@ private static void VerifyActivityData(Activity activity, bool isError = false,
Assert.Equal("sqlite", activity.Tags.FirstOrDefault(t => t.Key == EntityFrameworkDiagnosticListener.AttributeDbSystem).Value);
// TBD: SqlLite not setting the DataSource so it doesn't get set.
- Assert.DoesNotContain(activity.Tags, t => t.Key == EntityFrameworkDiagnosticListener.AttributePeerService);
+ Assert.DoesNotContain(activity.Tags, t => t.Key == EntityFrameworkDiagnosticListener.AttributeServerAddress);
- Assert.Equal(altDisplayName ?? "main", activity.Tags.FirstOrDefault(t => t.Key == EntityFrameworkDiagnosticListener.AttributeDbName).Value);
+ Assert.Equal(altDisplayName ?? "main", activity.Tags.FirstOrDefault(t => t.Key == EntityFrameworkDiagnosticListener.AttributeDbNamespace).Value);
if (!isError)
{
From 7a838c6b0f7fdf96657186ddd74bdbfdf6733031 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Wed, 2 Oct 2024 15:34:31 +0000
Subject: [PATCH 2/9] Add changelog entry
---
.../CHANGELOG.md | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md
index e1c2828603..edf7180e85 100644
--- a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md
+++ b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md
@@ -2,6 +2,13 @@
## Unreleased
+* Some attributes have been updated to align with
+ the latest version of Semantic Conventions:
+ - Rename `db.name` to `db.namespace`
+ - Rename `db.statement` to `db.query.text`
+ - Rename `peer.service` to `server.address`
+ ([#2130](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2130))
+
## 1.0.0-beta.12
Released 2024-Jun-18
From 858946eb2d36eb2a0bea949639dad4c5b709e12a Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Wed, 2 Oct 2024 15:58:35 +0000
Subject: [PATCH 3/9] Update changelog
---
.../CHANGELOG.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md
index edf7180e85..6b5f5492c5 100644
--- a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md
+++ b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md
@@ -4,9 +4,9 @@
* Some attributes have been updated to align with
the latest version of Semantic Conventions:
- - Rename `db.name` to `db.namespace`
- - Rename `db.statement` to `db.query.text`
- - Rename `peer.service` to `server.address`
+ - Rename `db.name` to `db.namespace`
+ - Rename `db.statement` to `db.query.text`
+ - Rename `peer.service` to `server.address`
([#2130](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2130))
## 1.0.0-beta.12
From bcc0bfa9db61950636e623dac9327225cf30ec9b Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Wed, 2 Oct 2024 16:34:43 +0000
Subject: [PATCH 4/9] Update changelog
---
.../CHANGELOG.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md
index 6b5f5492c5..915422fa5b 100644
--- a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md
+++ b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md
@@ -4,16 +4,16 @@
* Some attributes have been updated to align with
the latest version of Semantic Conventions:
- - Rename `db.name` to `db.namespace`
- - Rename `db.statement` to `db.query.text`
- - Rename `peer.service` to `server.address`
+ * Rename `db.name` to `db.namespace`
+ * Rename `db.statement` to `db.query.text`
+ * Rename `peer.service` to `server.address`
([#2130](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2130))
## 1.0.0-beta.12
Released 2024-Jun-18
-* Update `Microsoft.Extensions.Options` to `8.0.0`.
+* Update `Microsoft.Extensio²ns.Options` to `8.0.0`.
([#1830](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1830))
* Updated OpenTelemetry core component version(s) to `1.9.0`.
From 5540065c72df08c02913c0ceb7776533d243bc2c Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Wed, 2 Oct 2024 16:38:48 +0000
Subject: [PATCH 5/9] Update changelog
---
.../CHANGELOG.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md
index 915422fa5b..18478eecf5 100644
--- a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md
+++ b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md
@@ -13,7 +13,7 @@
Released 2024-Jun-18
-* Update `Microsoft.Extensio²ns.Options` to `8.0.0`.
+* Update `Microsoft.Extensions.Options` to `8.0.0`.
([#1830](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/1830))
* Updated OpenTelemetry core component version(s) to `1.9.0`.
From fa9a1e673daa9345b54bdc5c7d515c0a9148f309 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Wed, 2 Oct 2024 16:40:39 +0000
Subject: [PATCH 6/9] Update changelog
---
.../CHANGELOG.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md
index 18478eecf5..51eebe548e 100644
--- a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md
+++ b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md
@@ -4,9 +4,9 @@
* Some attributes have been updated to align with
the latest version of Semantic Conventions:
- * Rename `db.name` to `db.namespace`
- * Rename `db.statement` to `db.query.text`
- * Rename `peer.service` to `server.address`
+ * Rename `db.name` to `db.namespace`
+ * Rename `db.statement` to `db.query.text`
+ * Rename `peer.service` to `server.address`
([#2130](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2130))
## 1.0.0-beta.12
From 646c90934b2539ad1f3dbdb623fd2e95cca12ea8 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Mon, 14 Oct 2024 10:15:17 +0000
Subject: [PATCH 7/9] Allow users to opt-in for new attributes
---
opentelemetry-dotnet-contrib.sln | 1 +
.../CHANGELOG.md | 25 ++++--
.../EntityFrameworkInstrumentationOptions.cs | 31 ++++++-
.../EntityFrameworkDiagnosticListener.cs | 41 +++++++--
...Instrumentation.EntityFrameworkCore.csproj | 6 +-
.../DatabaseSemanticConventionHelper.cs | 83 +++++++++++++++++++
.../DatabaseSemanticConventionHelperTests.cs | 79 ++++++++++++++++++
.../EntityFrameworkDiagnosticListenerTests.cs | 50 ++++++++++-
...EntityFrameworkInstrumentationOptionsTests | 42 ++++++++++
9 files changed, 339 insertions(+), 19 deletions(-)
create mode 100644 src/Shared/DatabaseSemanticConventionHelper.cs
create mode 100644 test/OpenTelemetry.Instrumentation.EntityFrameworkCore.Tests/DatabaseSemanticConventionHelperTests.cs
create mode 100644 test/OpenTelemetry.Instrumentation.EntityFrameworkCore.Tests/EntityFrameworkInstrumentationOptionsTests
diff --git a/opentelemetry-dotnet-contrib.sln b/opentelemetry-dotnet-contrib.sln
index b4f5fc258c..49bcf2e393 100644
--- a/opentelemetry-dotnet-contrib.sln
+++ b/opentelemetry-dotnet-contrib.sln
@@ -239,6 +239,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{1FCC8E
ProjectSection(SolutionItems) = preProject
src\Shared\ActivityInstrumentationHelper.cs = src\Shared\ActivityInstrumentationHelper.cs
src\Shared\AssemblyVersionExtensions.cs = src\Shared\AssemblyVersionExtensions.cs
+ src\Shared\DatabaseSemanticConventionHelper.cs = src\Shared\DatabaseSemanticConventionHelper.cs
src\Shared\DiagnosticSourceListener.cs = src\Shared\DiagnosticSourceListener.cs
src\Shared\DiagnosticSourceSubscriber.cs = src\Shared\DiagnosticSourceSubscriber.cs
src\Shared\ExceptionExtensions.cs = src\Shared\ExceptionExtensions.cs
diff --git a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md
index 51eebe548e..2c8fc2c1de 100644
--- a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md
+++ b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/CHANGELOG.md
@@ -2,11 +2,26 @@
## Unreleased
-* Some attributes have been updated to align with
- the latest version of Semantic Conventions:
- * Rename `db.name` to `db.namespace`
- * Rename `db.statement` to `db.query.text`
- * Rename `peer.service` to `server.address`
+* The new database semantic conventions can be opted in to by setting
+ the `OTEL_SEMCONV_STABILITY_OPT_IN` environment variable. This allows for a
+ transition period for users to experiment with the new semantic conventions
+ and adapt as necessary. The environment variable supports the following
+ values:
+ * `database` - emit the new, frozen (proposed for stable) database
+ attributes, and stop emitting the old experimental database
+ attributes that the instrumentation emitted previously.
+ * `database/dup` - emit both the old and the frozen (proposed for stable) database
+ attributes, allowing for a more seamless transition.
+ * The default behavior (in the absence of one of these values) is to continue
+ emitting the same database semantic conventions that were emitted in
+ the previous version.
+ * Note: this option will be be removed after the new database
+ semantic conventions is marked stable. At which time this
+ instrumentation can receive a stable release, and the old database
+ semantic conventions will no longer be supported. Refer to the
+ specification for more information regarding the new database
+ semantic conventions for
+ [spans](https://github.com/open-telemetry/semantic-conventions/blob/v1.28.0/docs/database/database-spans.md).
([#2130](https://github.com/open-telemetry/opentelemetry-dotnet-contrib/pull/2130))
## 1.0.0-beta.12
diff --git a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/EntityFrameworkInstrumentationOptions.cs b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/EntityFrameworkInstrumentationOptions.cs
index 0f92a2a95d..60bf2b527d 100644
--- a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/EntityFrameworkInstrumentationOptions.cs
+++ b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/EntityFrameworkInstrumentationOptions.cs
@@ -3,6 +3,8 @@
using System.Data;
using System.Diagnostics;
+using Microsoft.Extensions.Configuration;
+using static OpenTelemetry.Internal.DatabaseSemanticConventionHelper;
namespace OpenTelemetry.Instrumentation.EntityFrameworkCore;
@@ -12,12 +14,27 @@ namespace OpenTelemetry.Instrumentation.EntityFrameworkCore;
public class EntityFrameworkInstrumentationOptions
{
///
- /// Gets or sets a value indicating whether or not the should add the names of commands as the tag. Default value: True.
+ /// Initializes a new instance of the class.
+ ///
+ public EntityFrameworkInstrumentationOptions()
+ : this(new ConfigurationBuilder().AddEnvironmentVariables().Build())
+ {
+ }
+
+ internal EntityFrameworkInstrumentationOptions(IConfiguration configuration)
+ {
+ var databaseSemanticConvention = GetSemanticConventionOptIn(configuration);
+ this.EmitOldAttributes = databaseSemanticConvention.HasFlag(DatabaseSemanticConvention.Old);
+ this.EmitNewAttributes = databaseSemanticConvention.HasFlag(DatabaseSemanticConvention.New);
+ }
+
+ ///
+ /// Gets or sets a value indicating whether or not the should add the names of commands as the tag. Default value: True.
///
public bool SetDbStatementForStoredProcedure { get; set; } = true;
///
- /// Gets or sets a value indicating whether or not the should add the text of commands as the tag. Default value: False.
+ /// Gets or sets a value indicating whether or not the should add the text of commands as the tag. Default value: False.
///
public bool SetDbStatementForText { get; set; }
@@ -50,4 +67,14 @@ public class EntityFrameworkInstrumentationOptions
///
///
public Func? Filter { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the old database attributes should be emitted.
+ ///
+ internal bool EmitOldAttributes { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the new database attributes should be emitted.
+ ///
+ internal bool EmitNewAttributes { get; set; }
}
diff --git a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/Implementation/EntityFrameworkDiagnosticListener.cs b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/Implementation/EntityFrameworkDiagnosticListener.cs
index ebb26c585d..322c35c587 100644
--- a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/Implementation/EntityFrameworkDiagnosticListener.cs
+++ b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/Implementation/EntityFrameworkDiagnosticListener.cs
@@ -18,9 +18,12 @@ internal sealed class EntityFrameworkDiagnosticListener : ListenerHandler
internal const string EntityFrameworkCoreCommandExecuted = "Microsoft.EntityFrameworkCore.Database.Command.CommandExecuted";
internal const string EntityFrameworkCoreCommandError = "Microsoft.EntityFrameworkCore.Database.Command.CommandError";
+ internal const string AttributePeerService = "peer.service";
internal const string AttributeServerAddress = "server.address";
internal const string AttributeDbSystem = "db.system";
+ internal const string AttributeDbName = "db.name";
internal const string AttributeDbNamespace = "db.namespace";
+ internal const string AttributeDbStatement = "db.statement";
internal const string AttributeDbQueryText = "db.query.text";
internal static readonly Assembly Assembly = typeof(EntityFrameworkDiagnosticListener).Assembly;
@@ -33,7 +36,7 @@ internal sealed class EntityFrameworkDiagnosticListener : ListenerHandler
private readonly PropertyFetcher dbContextFetcher = new("Context");
private readonly PropertyFetcher dbContextDatabaseFetcher = new("Database");
private readonly PropertyFetcher providerNameFetcher = new("ProviderName");
- private readonly PropertyFetcher hostFetcher = new("Host");
+ private readonly PropertyFetcher dataSourceFetcher = new("DataSource");
private readonly PropertyFetcher databaseFetcher = new("Database");
private readonly PropertyFetcher commandTypeFetcher = new("CommandType");
private readonly PropertyFetcher commandTextFetcher = new("CommandText");
@@ -138,12 +141,20 @@ public override void OnEventWritten(string name, object? payload)
break;
}
- activity.AddTag(AttributeDbNamespace, database);
+ var dataSource = (string)this.dataSourceFetcher.Fetch(connection);
+ if (!string.IsNullOrEmpty(dataSource))
+ {
+ activity.AddTag(AttributeServerAddress, dataSource);
+ }
+
+ if (this.options.EmitOldAttributes)
+ {
+ activity.AddTag(AttributeDbName, database);
+ }
- var host = (string)this.hostFetcher.Fetch(connection);
- if (!string.IsNullOrEmpty(host))
+ if (this.options.EmitNewAttributes)
{
- activity.AddTag(AttributeServerAddress, host);
+ activity.AddTag(AttributeDbNamespace, database);
}
}
}
@@ -197,7 +208,15 @@ public override void OnEventWritten(string name, object? payload)
case CommandType.StoredProcedure:
if (this.options.SetDbStatementForStoredProcedure)
{
- activity.AddTag(AttributeDbQueryText, commandText);
+ if (this.options.EmitOldAttributes)
+ {
+ activity.AddTag(AttributeDbStatement, commandText);
+ }
+
+ if (this.options.EmitNewAttributes)
+ {
+ activity.AddTag(AttributeDbQueryText, commandText);
+ }
}
break;
@@ -205,7 +224,15 @@ public override void OnEventWritten(string name, object? payload)
case CommandType.Text:
if (this.options.SetDbStatementForText)
{
- activity.AddTag(AttributeDbQueryText, commandText);
+ if (this.options.EmitOldAttributes)
+ {
+ activity.AddTag(AttributeDbStatement, commandText);
+ }
+
+ if (this.options.EmitNewAttributes)
+ {
+ activity.AddTag(AttributeDbQueryText, commandText);
+ }
}
break;
diff --git a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/OpenTelemetry.Instrumentation.EntityFrameworkCore.csproj b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/OpenTelemetry.Instrumentation.EntityFrameworkCore.csproj
index 4af147744b..361404b0af 100644
--- a/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/OpenTelemetry.Instrumentation.EntityFrameworkCore.csproj
+++ b/src/OpenTelemetry.Instrumentation.EntityFrameworkCore/OpenTelemetry.Instrumentation.EntityFrameworkCore.csproj
@@ -7,24 +7,28 @@
Instrumentation.EntityFrameworkCore-
-
true
+
+
+
+
diff --git a/src/Shared/DatabaseSemanticConventionHelper.cs b/src/Shared/DatabaseSemanticConventionHelper.cs
new file mode 100644
index 0000000000..ef1c85cbc1
--- /dev/null
+++ b/src/Shared/DatabaseSemanticConventionHelper.cs
@@ -0,0 +1,83 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+#nullable enable
+
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.Extensions.Configuration;
+
+namespace OpenTelemetry.Internal;
+
+///
+/// Helper class for Database Semantic Conventions.
+///
+///
+/// Due to a breaking change in the semantic convention, affected instrumentation libraries
+/// must inspect an environment variable to determine which attributes to emit.
+/// This is expected to be removed when the instrumentation libraries reach Stable.
+/// .
+///
+internal static class DatabaseSemanticConventionHelper
+{
+ internal const string SemanticConventionOptInKeyName = "OTEL_SEMCONV_STABILITY_OPT_IN";
+ internal static readonly char[] Separator = new[] { ',', ' ' };
+
+ [Flags]
+ public enum DatabaseSemanticConvention
+ {
+ ///
+ /// Instructs an instrumentation library to emit the old experimental Database attributes.
+ ///
+ Old = 0x1,
+
+ ///
+ /// Instructs an instrumentation library to emit the new, v1.28.0 Database attributes.
+ ///
+ New = 0x2,
+
+ ///
+ /// Instructs an instrumentation library to emit both the old and new attributes.
+ ///
+ Dupe = Old | New,
+ }
+
+ public static DatabaseSemanticConvention GetSemanticConventionOptIn(IConfiguration configuration)
+ {
+ if (TryGetConfiguredValues(configuration, out var values))
+ {
+ if (values.Contains("database/dup"))
+ {
+ return DatabaseSemanticConvention.Dupe;
+ }
+ else if (values.Contains("database"))
+ {
+ return DatabaseSemanticConvention.New;
+ }
+ }
+
+ return DatabaseSemanticConvention.Old;
+ }
+
+ private static bool TryGetConfiguredValues(IConfiguration configuration, [NotNullWhen(true)] out HashSet? values)
+ {
+ try
+ {
+ var stringValue = configuration[SemanticConventionOptInKeyName];
+
+ if (string.IsNullOrWhiteSpace(stringValue))
+ {
+ values = null;
+ return false;
+ }
+
+ var stringValues = stringValue!.Split(separator: Separator, options: StringSplitOptions.RemoveEmptyEntries);
+ values = new HashSet(stringValues, StringComparer.OrdinalIgnoreCase);
+ return true;
+ }
+ catch
+ {
+ values = null;
+ return false;
+ }
+ }
+}
diff --git a/test/OpenTelemetry.Instrumentation.EntityFrameworkCore.Tests/DatabaseSemanticConventionHelperTests.cs b/test/OpenTelemetry.Instrumentation.EntityFrameworkCore.Tests/DatabaseSemanticConventionHelperTests.cs
new file mode 100644
index 0000000000..748ac1ea90
--- /dev/null
+++ b/test/OpenTelemetry.Instrumentation.EntityFrameworkCore.Tests/DatabaseSemanticConventionHelperTests.cs
@@ -0,0 +1,79 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+using Microsoft.Extensions.Configuration;
+using Xunit;
+using static OpenTelemetry.Internal.DatabaseSemanticConventionHelper;
+
+namespace OpenTelemetry.Internal.Tests;
+
+public class DatabaseSemanticConventionHelperTests
+{
+ public static IEnumerable TestCases => new List
+ {
+ new object[] { null!, DatabaseSemanticConvention.Old },
+ new object[] { string.Empty, DatabaseSemanticConvention.Old },
+ new object[] { " ", DatabaseSemanticConvention.Old },
+ new object[] { "junk", DatabaseSemanticConvention.Old },
+ new object[] { "none", DatabaseSemanticConvention.Old },
+ new object[] { "NONE", DatabaseSemanticConvention.Old },
+ new object[] { "database", DatabaseSemanticConvention.New },
+ new object[] { "DATABASE", DatabaseSemanticConvention.New },
+ new object[] { "database/dup", DatabaseSemanticConvention.Dupe },
+ new object[] { "DATABASE/DUP", DatabaseSemanticConvention.Dupe },
+ new object[] { "junk,,junk", DatabaseSemanticConvention.Old },
+ new object[] { "junk,JUNK", DatabaseSemanticConvention.Old },
+ new object[] { "junk1,junk2", DatabaseSemanticConvention.Old },
+ new object[] { "junk,database", DatabaseSemanticConvention.New },
+ new object[] { "junk,database , database ,junk", DatabaseSemanticConvention.New },
+ new object[] { "junk,database/dup", DatabaseSemanticConvention.Dupe },
+ new object[] { "junk, database/dup ", DatabaseSemanticConvention.Dupe },
+ new object[] { "database/dup,database", DatabaseSemanticConvention.Dupe },
+ new object[] { "database,database/dup", DatabaseSemanticConvention.Dupe },
+ };
+
+ [Fact]
+ public void VerifyFlags()
+ {
+ var testValue = DatabaseSemanticConvention.Dupe;
+ Assert.True(testValue.HasFlag(DatabaseSemanticConvention.Old));
+ Assert.True(testValue.HasFlag(DatabaseSemanticConvention.New));
+
+ testValue = DatabaseSemanticConvention.Old;
+ Assert.True(testValue.HasFlag(DatabaseSemanticConvention.Old));
+ Assert.False(testValue.HasFlag(DatabaseSemanticConvention.New));
+
+ testValue = DatabaseSemanticConvention.New;
+ Assert.False(testValue.HasFlag(DatabaseSemanticConvention.Old));
+ Assert.True(testValue.HasFlag(DatabaseSemanticConvention.New));
+ }
+
+ [Theory]
+ [MemberData(nameof(TestCases))]
+ public void VerifyGetSemanticConventionOptIn_UsingEnvironmentVariable(string input, string expectedValue)
+ {
+ try
+ {
+ Environment.SetEnvironmentVariable(SemanticConventionOptInKeyName, input);
+
+ var expected = Enum.Parse(typeof(DatabaseSemanticConvention), expectedValue);
+ Assert.Equal(expected, GetSemanticConventionOptIn(new ConfigurationBuilder().AddEnvironmentVariables().Build()));
+ }
+ finally
+ {
+ Environment.SetEnvironmentVariable(SemanticConventionOptInKeyName, null);
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(TestCases))]
+ public void VerifyGetSemanticConventionOptIn_UsingIConfiguration(string input, string expectedValue)
+ {
+ var configuration = new ConfigurationBuilder()
+ .AddInMemoryCollection(new Dictionary { [SemanticConventionOptInKeyName] = input })
+ .Build();
+
+ var expected = Enum.Parse(typeof(DatabaseSemanticConvention), expectedValue);
+ Assert.Equal(expected, GetSemanticConventionOptIn(configuration));
+ }
+}
diff --git a/test/OpenTelemetry.Instrumentation.EntityFrameworkCore.Tests/EntityFrameworkDiagnosticListenerTests.cs b/test/OpenTelemetry.Instrumentation.EntityFrameworkCore.Tests/EntityFrameworkDiagnosticListenerTests.cs
index 46cf9c06fc..6610d02ffd 100644
--- a/test/OpenTelemetry.Instrumentation.EntityFrameworkCore.Tests/EntityFrameworkDiagnosticListenerTests.cs
+++ b/test/OpenTelemetry.Instrumentation.EntityFrameworkCore.Tests/EntityFrameworkDiagnosticListenerTests.cs
@@ -66,7 +66,7 @@ public void EntityFrameworkEnrichDisplayNameWithEnrichWithIDbCommand()
{
var stateDisplayName = $"{command.CommandType} main";
activity1.DisplayName = stateDisplayName;
- activity1.SetTag("db.namespace", stateDisplayName);
+ activity1.SetTag("db.name", stateDisplayName);
};
}).Build();
@@ -86,6 +86,40 @@ public void EntityFrameworkEnrichDisplayNameWithEnrichWithIDbCommand()
VerifyActivityData(activity, altDisplayName: $"{expectedDisplayName}");
}
+ [Fact]
+ public void EntityFrameworkEnrichDisplayNameWithEnrichWithIDbCommand_New()
+ {
+ var exportedItems = new List();
+ var expectedDisplayName = "Text main";
+ using var shutdownSignal = Sdk.CreateTracerProviderBuilder()
+ .AddInMemoryExporter(exportedItems)
+ .AddEntityFrameworkCoreInstrumentation(options =>
+ {
+ options.EnrichWithIDbCommand = (activity1, command) =>
+ {
+ var stateDisplayName = $"{command.CommandType} main";
+ activity1.DisplayName = stateDisplayName;
+ activity1.SetTag("db.namespace", stateDisplayName);
+ };
+ options.EmitNewAttributes = true;
+ }).Build();
+
+ using (var context = new ItemsContext(this.contextOptions))
+ {
+ var items = context.Set- ().OrderBy(e => e.Name).ToList();
+
+ Assert.Equal(3, items.Count);
+ Assert.Equal("ItemOne", items[0].Name);
+ Assert.Equal("ItemThree", items[1].Name);
+ Assert.Equal("ItemTwo", items[2].Name);
+ }
+
+ Assert.Single(exportedItems);
+ var activity = exportedItems[0];
+
+ VerifyActivityData(activity, altDisplayName: $"{expectedDisplayName}", emitNewAttributes: true);
+ }
+
[Fact]
public void EntityFrameworkContextExceptionEventsInstrumentedTest()
{
@@ -227,7 +261,7 @@ private static SqliteConnection CreateInMemoryDatabase()
return connection;
}
- private static void VerifyActivityData(Activity activity, bool isError = false, string? altDisplayName = null)
+ private static void VerifyActivityData(Activity activity, bool isError = false, string? altDisplayName = null, bool emitNewAttributes = false)
{
Assert.Equal(altDisplayName ?? "main", activity.DisplayName);
@@ -235,9 +269,17 @@ private static void VerifyActivityData(Activity activity, bool isError = false,
Assert.Equal("sqlite", activity.Tags.FirstOrDefault(t => t.Key == EntityFrameworkDiagnosticListener.AttributeDbSystem).Value);
// TBD: SqlLite not setting the DataSource so it doesn't get set.
- Assert.DoesNotContain(activity.Tags, t => t.Key == EntityFrameworkDiagnosticListener.AttributeServerAddress);
+ Assert.DoesNotContain(activity.Tags, t => t.Key == EntityFrameworkDiagnosticListener.AttributePeerService);
- Assert.Equal(altDisplayName ?? "main", activity.Tags.FirstOrDefault(t => t.Key == EntityFrameworkDiagnosticListener.AttributeDbNamespace).Value);
+ if (!emitNewAttributes)
+ {
+ Assert.Equal(altDisplayName ?? "main", activity.Tags.FirstOrDefault(t => t.Key == EntityFrameworkDiagnosticListener.AttributeDbName).Value);
+ }
+
+ if (emitNewAttributes)
+ {
+ Assert.Equal(altDisplayName ?? "main", activity.Tags.FirstOrDefault(t => t.Key == EntityFrameworkDiagnosticListener.AttributeDbNamespace).Value);
+ }
if (!isError)
{
diff --git a/test/OpenTelemetry.Instrumentation.EntityFrameworkCore.Tests/EntityFrameworkInstrumentationOptionsTests b/test/OpenTelemetry.Instrumentation.EntityFrameworkCore.Tests/EntityFrameworkInstrumentationOptionsTests
new file mode 100644
index 0000000000..6f8d54b892
--- /dev/null
+++ b/test/OpenTelemetry.Instrumentation.EntityFrameworkCore.Tests/EntityFrameworkInstrumentationOptionsTests
@@ -0,0 +1,42 @@
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+using Microsoft.Extensions.Configuration;
+using OpenTelemetry.Internal;
+using Xunit;
+
+namespace OpenTelemetry.Instrumentation.EntityFrameworkCore.Tests;
+
+public class EntityFrameworkInstrumentationOptionsTests
+{
+ [Fact]
+ public void ShouldEmitOldAttributesWhenStabilityOptInIsDatabaseDup()
+ {
+ var configuration = new ConfigurationBuilder()
+ .AddInMemoryCollection(new Dictionary
{ [DatabaseSemanticConventionHelper.SemanticConventionOptInKeyName] = "database/dup" })
+ .Build();
+ var options = new EntityFrameworkInstrumentationOptions(configuration);
+ Assert.True(options.EmitOldAttributes);
+ Assert.True(options.EmitNewAttributes);
+ }
+
+ [Fact]
+ public void ShouldEmitOldAttributesWhenStabilityOptInIsNotSpecified()
+ {
+ var configuration = new ConfigurationBuilder().Build();
+ var options = new EntityFrameworkInstrumentationOptions(configuration);
+ Assert.True(options.EmitOldAttributes);
+ Assert.False(options.EmitNewAttributes);
+ }
+
+ [Fact]
+ public void ShouldEmitNewAttributesWhenStabilityOptInIsDatabase()
+ {
+ var configuration = new ConfigurationBuilder()
+ .AddInMemoryCollection(new Dictionary { [DatabaseSemanticConventionHelper.SemanticConventionOptInKeyName] = "database" })
+ .Build();
+ var options = new EntityFrameworkInstrumentationOptions(configuration);
+ Assert.False(options.EmitOldAttributes);
+ Assert.True(options.EmitNewAttributes);
+ }
+}
From c503d8a588114b51e3d61711af0f22702502498f Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Thu, 17 Oct 2024 05:43:37 +0000
Subject: [PATCH 8/9] Move DatabaseSemanticConventionHelperTests
---
.../DatabaseSemanticConventionHelperTests.cs | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename test/{OpenTelemetry.Instrumentation.EntityFrameworkCore.Tests => OpenTelemetry.Contrib.Shared.Tests}/DatabaseSemanticConventionHelperTests.cs (100%)
diff --git a/test/OpenTelemetry.Instrumentation.EntityFrameworkCore.Tests/DatabaseSemanticConventionHelperTests.cs b/test/OpenTelemetry.Contrib.Shared.Tests/DatabaseSemanticConventionHelperTests.cs
similarity index 100%
rename from test/OpenTelemetry.Instrumentation.EntityFrameworkCore.Tests/DatabaseSemanticConventionHelperTests.cs
rename to test/OpenTelemetry.Contrib.Shared.Tests/DatabaseSemanticConventionHelperTests.cs
From 46f1e987e2b0448a4ee70aa2f29cf9f9d0bd69c2 Mon Sep 17 00:00:00 2001
From: joegoldman2 <147369450+joegoldman@users.noreply.github.com>
Date: Thu, 17 Oct 2024 05:58:05 +0000
Subject: [PATCH 9/9] Add missing references
---
.../OpenTelemetry.Contrib.Shared.Tests.csproj | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/test/OpenTelemetry.Contrib.Shared.Tests/OpenTelemetry.Contrib.Shared.Tests.csproj b/test/OpenTelemetry.Contrib.Shared.Tests/OpenTelemetry.Contrib.Shared.Tests.csproj
index 53e2d966e5..f6b04bb9c4 100644
--- a/test/OpenTelemetry.Contrib.Shared.Tests/OpenTelemetry.Contrib.Shared.Tests.csproj
+++ b/test/OpenTelemetry.Contrib.Shared.Tests/OpenTelemetry.Contrib.Shared.Tests.csproj
@@ -7,14 +7,18 @@
+
+
+
+