Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
`Sdk.CreateTracerProviderBuilder().AddAzureMonitorTraceExporter(...)` path.
([#52720](https://github.com/Azure/azure-sdk-for-net/pull/52720))

* Added handling of stable database client span semantic conventions
([#53050](https://github.com/Azure/azure-sdk-for-net/pull/53050))

### Breaking Changes

### Bugs Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public RemoteDependencyData(int version, Activity activity, ref ActivityTagsProc
SetHttpDependencyPropertiesAndDependencyName(activity, ref activityTagsProcessor.MappedTags, isNewSchemaVersion, out dependencyName);
break;
case OperationType.Db:
SetDbDependencyProperties(ref activityTagsProcessor.MappedTags);
SetDbDependencyProperties(ref activityTagsProcessor.MappedTags, isNewSchemaVersion);
break;
case OperationType.Messaging:
SetMessagingDependencyProperties(activity, ref activityTagsProcessor.MappedTags);
Expand Down Expand Up @@ -86,11 +86,23 @@ private void SetHttpDependencyPropertiesAndDependencyName(Activity activity, ref
ResultCode = resultCode?.Truncate(SchemaConstants.RemoteDependencyData_ResultCode_MaxLength) ?? "0";
}

private void SetDbDependencyProperties(ref AzMonList dbTagObjects)
private void SetDbDependencyProperties(ref AzMonList dbTagObjects, bool isNewSchemaVersion)
{
var dbAttributeTagObjects = AzMonList.GetTagValues(ref dbTagObjects, SemanticConventions.AttributeDbStatement, SemanticConventions.AttributeDbSystem);
string statementAttributeKey;
string statementSystemKey;
if (isNewSchemaVersion)
{
statementAttributeKey = SemanticConventions.AttributeDbQueryText;
statementSystemKey = SemanticConventions.AttributeDbSystemName;
}
else
{
statementAttributeKey = SemanticConventions.AttributeDbStatement;
statementSystemKey = SemanticConventions.AttributeDbSystem;
}
var dbAttributeTagObjects = AzMonList.GetTagValues(ref dbTagObjects, statementAttributeKey, statementSystemKey);
Data = dbAttributeTagObjects[0]?.ToString().Truncate(SchemaConstants.RemoteDependencyData_Data_MaxLength);
var (DbName, DbTarget) = dbTagObjects.GetDbDependencyTargetAndName();
var (DbName, DbTarget) = dbTagObjects.GetDbDependencyTargetAndName(isNewSchemaVersion);
Target = DbTarget?.Truncate(SchemaConstants.RemoteDependencyData_Target_MaxLength);
Type = AzMonListExtensions.s_dbSystems.Contains(dbAttributeTagObjects[1]?.ToString()) ? "SQL" : dbAttributeTagObjects[1]?.ToString().Truncate(SchemaConstants.RemoteDependencyData_Type_MaxLength);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ internal struct ActivityTagsProcessor
{
private static readonly string[] s_semantics = {
SemanticConventions.AttributeDbStatement,
SemanticConventions.AttributeDbQueryText,
SemanticConventions.AttributeDbSystem,
SemanticConventions.AttributeDbSystemName,
SemanticConventions.AttributeDbName,
SemanticConventions.AttributeDbNamespace,

// required - HTTP
SemanticConventions.AttributeHttpMethod,
Expand Down Expand Up @@ -97,6 +100,9 @@ public void CategorizeTags(Activity activity)
case SemanticConventions.AttributeHttpRequestMethod:
activityType = OperationType.Http | OperationType.V2;
break;
case SemanticConventions.AttributeDbSystemName:
activityType = OperationType.Db | OperationType.V2;
break;
case SemanticConventions.AttributeDbSystem:
activityType = OperationType.Db;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,9 +329,19 @@ internal static string GetDefaultDbPort(string? dbSystem)
///<summary>
/// Gets Database dependency target and name from activity tag objects.
///</summary>
internal static (string? DbName, string? DbTarget) GetDbDependencyTargetAndName(this AzMonList tagObjects)
internal static (string? DbName, string? DbTarget) GetDbDependencyTargetAndName(this AzMonList tagObjects, bool isNewSchemaVersion)
{
var peerServiceAndDbSystem = AzMonList.GetTagValues(ref tagObjects, SemanticConventions.AttributePeerService, SemanticConventions.AttributeDbSystem);
string statementDbNameKey;
string statementDbSystemKey;
if (isNewSchemaVersion) {
statementDbNameKey = SemanticConventions.AttributeDbNamespace;
statementDbSystemKey = SemanticConventions.AttributeDbSystemName;
} else {
statementDbNameKey = SemanticConventions.AttributeDbName;
statementDbSystemKey = SemanticConventions.AttributeDbSystem;
}

var peerServiceAndDbSystem = AzMonList.GetTagValues(ref tagObjects, SemanticConventions.AttributePeerService, statementDbSystemKey);
string? target = peerServiceAndDbSystem[0]?.ToString();
var defaultPort = GetDefaultDbPort(peerServiceAndDbSystem[1]?.ToString());

Expand All @@ -340,7 +350,7 @@ internal static (string? DbName, string? DbTarget) GetDbDependencyTargetAndName(
target = tagObjects.GetTargetUsingServerAttributes(defaultPort) ?? tagObjects.GetTargetUsingNetPeerAttributes(defaultPort);
}

var dbName = AzMonList.GetTagValue(ref tagObjects, SemanticConventions.AttributeDbName)?.ToString();
var dbName = AzMonList.GetTagValue(ref tagObjects, statementDbNameKey)?.ToString();
bool isTargetEmpty = string.IsNullOrWhiteSpace(target);
bool isDbNameEmpty = string.IsNullOrWhiteSpace(dbName);
if (!isTargetEmpty && !isDbNameEmpty)
Expand All @@ -353,7 +363,7 @@ internal static (string? DbName, string? DbTarget) GetDbDependencyTargetAndName(
}
else if (isTargetEmpty && isDbNameEmpty)
{
target = AzMonList.GetTagValue(ref tagObjects, SemanticConventions.AttributeDbSystem)?.ToString();
target = AzMonList.GetTagValue(ref tagObjects, statementDbSystemKey)?.ToString();
}

return (DbName: dbName, DbTarget: target);
Expand All @@ -369,7 +379,7 @@ internal static (string? DbName, string? DbTarget) GetDbDependencyTargetAndName(
case OperationType.Http:
return tagObjects.GetHttpDependencyTarget();
case OperationType.Db:
return tagObjects.GetDbDependencyTargetAndName().DbTarget;
return tagObjects.GetDbDependencyTargetAndName(type.HasFlag(OperationType.V2)).DbTarget;
case OperationType.Messaging:
return tagObjects.GetMessagingUrlAndSourceOrTarget(ActivityKind.Producer).SourceOrTarget;
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ internal static (string? MessagingUrl, string? SourceOrTarget) GetMessagingUrlAn
case OperationType.Http:
return tagObjects.GetNewSchemaHttpDependencyTarget();
case OperationType.Db:
return tagObjects.GetDbDependencyTargetAndName().DbTarget;
return tagObjects.GetDbDependencyTargetAndName(type.HasFlag(OperationType.V2)).DbTarget;
default:
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,5 +164,16 @@ internal static class SemanticConventions
// Messaging v1.21.0 https://github.com/open-telemetry/opentelemetry-specification/blob/v1.21.0/specification/trace/semantic_conventions/messaging.md
public const string AttributeMessagingDestinationName = "messaging.destination.name";
public const string AttributeNetworkProtocolName = "network.protocol.name";

// Database v1.36.0 https://github.com/open-telemetry/semantic-conventions/tree/v1.36.0/docs/database
public const string AttributeDbCollectionName = "db.collection.name";
public const string AttributeDbOperationName = "db.operation.name";
public const string AttributeDbSystemName = "db.system.name";
public const string AttributeDbNamespace = "db.namespace";
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";
public const string AttributeDbStoredProcedureName = "db.stored_procedure.name";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -564,32 +564,32 @@ public void DbNameIsAppendedToTargetDerivedFromNetAttributesforDBDependencyTarge
AzMonList.Add(ref mappedTags, new KeyValuePair<string, object?>(SemanticConventions.AttributeServerAddress, serverAddress));
AzMonList.Add(ref mappedTags, new KeyValuePair<string, object?>(SemanticConventions.AttributeServerPort, serverPort));
AzMonList.Add(ref mappedTags, new KeyValuePair<string, object?>(SemanticConventions.AttributeServerSocketAddress, serverSocketAddress));
AzMonList.Add(ref mappedTags, new KeyValuePair<string, object?>(SemanticConventions.AttributeDbName, "DbName"));
AzMonList.Add(ref mappedTags, new KeyValuePair<string, object?>(SemanticConventions.AttributeDbNamespace, "DbName"));

Assert.Equal(expectedTarget, mappedTags.GetDbDependencyTargetAndName().DbTarget);
Assert.Equal(expectedTarget, mappedTags.GetDbDependencyTargetAndName(true).DbTarget);
}

[Fact]
public void DbDependencyTargetIsSetToDbNameWhenNetAttributesAreNotPresent()
{
var mappedTags = AzMonList.Initialize();
AzMonList.Add(ref mappedTags, new KeyValuePair<string, object?>(SemanticConventions.AttributeDbName, "DbName"));
Assert.Equal("DbName", mappedTags.GetDbDependencyTargetAndName().DbTarget);
AzMonList.Add(ref mappedTags, new KeyValuePair<string, object?>(SemanticConventions.AttributeDbNamespace, "DbName"));
Assert.Equal("DbName", mappedTags.GetDbDependencyTargetAndName(true).DbTarget);
}

[Fact]
public void DbDependencyTargetIsSetToDbSystemWhenNetAndDbNameAttributesAreNotPresent()
{
var mappedTags = AzMonList.Initialize();
AzMonList.Add(ref mappedTags, new KeyValuePair<string, object?>(SemanticConventions.AttributeDbSystem, "DbSystem"));
Assert.Equal("DbSystem", mappedTags.GetDbDependencyTargetAndName().DbTarget);
AzMonList.Add(ref mappedTags, new KeyValuePair<string, object?>(SemanticConventions.AttributeDbSystemName, "DbSystem"));
Assert.Equal("DbSystem", mappedTags.GetDbDependencyTargetAndName(true).DbTarget);
}

[Fact]
public void DbDependencyTargetIsSetToNullByDefault()
{
var mappedTags = AzMonList.Initialize();
Assert.Null(mappedTags.GetDbDependencyTargetAndName().DbTarget);
Assert.Null(mappedTags.GetDbDependencyTargetAndName(true).DbTarget);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ public void ValidateHttpRemoteDependencyData()
}

[Fact]
public void ValidateDbRemoteDependencyData()
public void ValidateOldDbRemoteDependencyData()
{
using ActivitySource activitySource = new ActivitySource(ActivitySourceName);
using var activity = activitySource.StartActivity(
Expand Down Expand Up @@ -169,6 +169,40 @@ public void ValidateDbRemoteDependencyData()
Assert.True(remoteDependencyData.Measurements.Count == 0);
}

[Fact]
public void ValidateNewDbRemoteDependencyData()
{
using ActivitySource activitySource = new ActivitySource(ActivitySourceName);
using var activity = activitySource.StartActivity(
ActivityName,
ActivityKind.Client,
parentContext: new ActivityContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.Recorded),
startTime: DateTime.UtcNow);
Assert.NotNull(activity);
activity.Stop();

activity.SetStatus(ActivityStatusCode.Ok);
activity.SetTag(SemanticConventions.AttributeDbNamespace, "mysqlserver");
activity.SetTag(SemanticConventions.AttributeDbSystemName, "mssql");
activity.SetTag(SemanticConventions.AttributePeerService, "localhost"); // only adding test via peer.service. all possible combinations are covered in AzMonListExtensionsTests.
activity.SetTag(SemanticConventions.AttributeDbQueryText, "Select * from table");

var activityTagsProcessor = TraceHelper.EnumerateActivityTags(activity);

var remoteDependencyData = new RemoteDependencyData(2, activity, ref activityTagsProcessor);

Assert.Equal(ActivityName, remoteDependencyData.Name);
Assert.Equal(activity.Context.SpanId.ToHexString(), remoteDependencyData.Id);
Assert.Equal("Select * from table", remoteDependencyData.Data);
Assert.Equal("localhost | mysqlserver", remoteDependencyData.Target);
Assert.Null(remoteDependencyData.ResultCode);
Assert.Equal(activity.Duration.ToString("c", CultureInfo.InvariantCulture), remoteDependencyData.Duration);
Assert.Equal(activity.Status != ActivityStatusCode.Error, remoteDependencyData.Success);
Assert.True(remoteDependencyData.Properties.Count == 1);
Assert.True(remoteDependencyData.Properties.Contains(new KeyValuePair<string, string>(SemanticConventions.AttributeDbName, "mysqlserver" )));
Assert.True(remoteDependencyData.Measurements.Count == 0);
}

[Fact]
public void HttpDependencyNameIsActivityDisplayNameByDefault()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public void TagObjects_Mapped()
}

[Fact]
public void TagObjects_Mapped_HonorsNewSchema()
public void TagObjects_Mapped_HonorsNewHTTPSchema()
{
var activityTagsProcessor = new ActivityTagsProcessor();

Expand All @@ -140,6 +140,30 @@ public void TagObjects_Mapped_HonorsNewSchema()
Assert.Equal("/test", AzMonList.GetTagValue(ref activityTagsProcessor.MappedTags, SemanticConventions.AttributeUrlPath));
}

[Fact]
public void TagObjects_Mapped_HonorsNewDBSchema()
{
var activityTagsProcessor = new ActivityTagsProcessor();

IEnumerable<KeyValuePair<string, object?>> tagObjects = new Dictionary<string, object?>
{
[SemanticConventions.AttributeDbNamespace] = "mysqlserver",
[SemanticConventions.AttributeDbSystemName] = "mssql",
[SemanticConventions.AttributePeerService] = "localhost",
[SemanticConventions.AttributeDbQueryText] = "Select * from table",
};

using var activity = CreateTestActivity(tagObjects);
activityTagsProcessor.CategorizeTags(activity);

Assert.Equal(OperationType.Db | OperationType.V2, activityTagsProcessor.activityType);
Assert.Equal(4, activityTagsProcessor.MappedTags.Length);
Assert.Equal("mysqlserver", AzMonList.GetTagValue(ref activityTagsProcessor.MappedTags, SemanticConventions.AttributeDbNamespace));
Assert.Equal("mssql", AzMonList.GetTagValue(ref activityTagsProcessor.MappedTags, SemanticConventions.AttributeDbSystemName));
Assert.Equal("localhost", AzMonList.GetTagValue(ref activityTagsProcessor.MappedTags, SemanticConventions.AttributePeerService));
Assert.Equal("Select * from table", AzMonList.GetTagValue(ref activityTagsProcessor.MappedTags, SemanticConventions.AttributeDbQueryText));
}

[Fact]
public void TagObjects_Mapped_UnMapped()
{
Expand Down