Skip to content

Commit

Permalink
[AzureMonitorExporter] Add support new Messaging semantics - Request/…
Browse files Browse the repository at this point in the history
…Dependency Telemetry (#37508)

* PR feedback  + tests.

* [AzureMonitorExporter] Add support new Messaging semantics - Request / Dependency Telemetry

* Fix test

* New changes

* nits

* Remove unused method.

* Fix message sematics to support Azure SDK.

* Self-review fix

* Fix build + Test Fix
  • Loading branch information
rajkumar-rangaraj authored Jul 12, 2023
1 parent 616f600 commit 39da8e5
Show file tree
Hide file tree
Showing 14 changed files with 387 additions and 122 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public RemoteDependencyData(int version, Activity activity, ref ActivityTagsProc
}

dependencyName ??= activity.DisplayName;
Name = dependencyName.Truncate(SchemaConstants.RemoteDependencyData_Name_MaxLength);
Name = dependencyName?.Truncate(SchemaConstants.RemoteDependencyData_Name_MaxLength);
Id = activity.Context.SpanId.ToHexString();
Duration = activity.Duration < SchemaConstants.RemoteDependencyData_Duration_LessThanDays
? activity.Duration.ToString("c", CultureInfo.InvariantCulture)
Expand Down Expand Up @@ -101,8 +101,8 @@ private void SetHttpDependencyPropertiesAndDependencyName(Activity activity, ref
}

Type = "Http";
Data = httpUrl.Truncate(SchemaConstants.RemoteDependencyData_Data_MaxLength);
Target = target.Truncate(SchemaConstants.RemoteDependencyData_Target_MaxLength);
Data = httpUrl?.Truncate(SchemaConstants.RemoteDependencyData_Data_MaxLength);
Target = target?.Truncate(SchemaConstants.RemoteDependencyData_Target_MaxLength);
ResultCode = resultCode?.Truncate(SchemaConstants.RemoteDependencyData_ResultCode_MaxLength) ?? "0";
}

Expand All @@ -111,11 +111,11 @@ private void SetDbDependencyProperties(ref AzMonList dbTagObjects)
var dbAttributeTagObjects = AzMonList.GetTagValues(ref dbTagObjects, SemanticConventions.AttributeDbStatement, SemanticConventions.AttributeDbSystem);
Data = dbAttributeTagObjects[0]?.ToString().Truncate(SchemaConstants.RemoteDependencyData_Data_MaxLength);
var (DbName, DbTarget) = dbTagObjects.GetDbDependencyTargetAndName();
Target = DbTarget.Truncate(SchemaConstants.RemoteDependencyData_Target_MaxLength);
Target = DbTarget?.Truncate(SchemaConstants.RemoteDependencyData_Target_MaxLength);
Type = s_sqlDbs.Contains(dbAttributeTagObjects[1]?.ToString()) ? "SQL" : dbAttributeTagObjects[1]?.ToString().Truncate(SchemaConstants.RemoteDependencyData_Type_MaxLength);

// special case for db.name
var sanitizedDbName = DbName.Truncate(SchemaConstants.KVP_MaxValueLength);
var sanitizedDbName = DbName?.Truncate(SchemaConstants.KVP_MaxValueLength);
if (sanitizedDbName != null)
{
Properties.Add(SemanticConventions.AttributeDbName, sanitizedDbName);
Expand All @@ -132,10 +132,10 @@ private void SetRpcDependencyProperties(ref AzMonList rpcTagObjects)

private void SetMessagingDependencyProperties(Activity activity, ref AzMonList messagingTagObjects)
{
var messagingAttributeTagObjects = AzMonList.GetTagValues(ref messagingTagObjects, SemanticConventions.AttributeMessagingUrl, SemanticConventions.AttributeMessagingSystem);
var messagingUrl = messagingAttributeTagObjects[0]?.ToString();
var (messagingUrl, target) = messagingTagObjects.GetMessagingUrlAndSourceOrTarget(activity.Kind);
Data = messagingUrl?.Truncate(SchemaConstants.RemoteDependencyData_Data_MaxLength);
Type = messagingAttributeTagObjects[1]?.ToString().Truncate(SchemaConstants.RemoteDependencyData_Type_MaxLength);
Target = target?.Truncate(SchemaConstants.RemoteDependencyData_Target_MaxLength);
Type = AzMonList.GetTagValue(ref messagingTagObjects, SemanticConventions.AttributeMessagingSystem)?.ToString().Truncate(SchemaConstants.RemoteDependencyData_Type_MaxLength);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@

using System.Diagnostics;
using System.Globalization;
using System.Runtime.CompilerServices;
using Azure.Core;
using Azure.Monitor.OpenTelemetry.Exporter.Internals;

namespace Azure.Monitor.OpenTelemetry.Exporter.Models
{
internal partial class RequestData
{
public RequestData(int version, string? operationName, string? requestUrl, Activity activity, ref ActivityTagsProcessor activityTagsProcessor) : this(version, activity, ref activityTagsProcessor)
{
Name = operationName?.Truncate(SchemaConstants.RequestData_Name_MaxLength);
Url = requestUrl?.Truncate(SchemaConstants.RequestData_Url_MaxLength);
}

public RequestData(int version, Activity activity, ref ActivityTagsProcessor activityTagsProcessor) : base(version)
{
string? responseCode = null;
Expand Down Expand Up @@ -50,6 +57,7 @@ public RequestData(int version, Activity activity, ref ActivityTagsProcessor act
TraceHelper.AddPropertiesToTelemetry(Properties, ref activityTagsProcessor.UnMappedTags);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool IsSuccess(Activity activity, string? responseCode, OperationType operationType)
{
if (operationType.HasFlag(OperationType.Http)
Expand All @@ -67,25 +75,27 @@ internal static bool IsSuccess(Activity activity, string? responseCode, Operatio

private void SetHttpRequestPropertiesAndResponseCode(Activity activity, ref AzMonList httpTagObjects, out string responseCode)
{
Url = httpTagObjects.GetRequestUrl().Truncate(SchemaConstants.RequestData_Url_MaxLength);
Name = TraceHelper.GetOperationName(activity, ref httpTagObjects).Truncate(SchemaConstants.RequestData_Name_MaxLength);
Url ??= httpTagObjects.GetRequestUrl()?.Truncate(SchemaConstants.RequestData_Url_MaxLength);
Name ??= TraceHelper.GetOperationName(activity, ref httpTagObjects)?.Truncate(SchemaConstants.RequestData_Name_MaxLength);
responseCode = AzMonList.GetTagValue(ref httpTagObjects, SemanticConventions.AttributeHttpStatusCode)
?.ToString().Truncate(SchemaConstants.RequestData_ResponseCode_MaxLength)
?? "0";
}

private void SetHttpV2RequestPropertiesAndResponseCode(Activity activity, ref AzMonList httpTagObjects, out string responseCode)
{
Url = httpTagObjects.GetNewSchemaRequestUrl().Truncate(SchemaConstants.RequestData_Url_MaxLength);
Name = TraceHelper.GetNewSchemaOperationName(activity, Url, ref httpTagObjects).Truncate(SchemaConstants.RequestData_Name_MaxLength);
Url ??= httpTagObjects.GetNewSchemaRequestUrl()?.Truncate(SchemaConstants.RequestData_Url_MaxLength);
Name ??= TraceHelper.GetNewSchemaOperationName(activity, Url, ref httpTagObjects)?.Truncate(SchemaConstants.RequestData_Name_MaxLength);
responseCode = AzMonList.GetTagValue(ref httpTagObjects, SemanticConventions.AttributeHttpResponseStatusCode)
?.ToString().Truncate(SchemaConstants.RequestData_ResponseCode_MaxLength)
?? "0";
}

private void SetMessagingRequestProperties(Activity activity, ref AzMonList messagingTagObjects)
{
Url = AzMonList.GetTagValue(ref messagingTagObjects, SemanticConventions.AttributeMessagingUrl)?.ToString().Truncate(SchemaConstants.RequestData_Url_MaxLength);
var (messagingUrl, source) = messagingTagObjects.GetMessagingUrlAndSourceOrTarget(activity.Kind);
Url = messagingUrl?.Truncate(SchemaConstants.RequestData_Url_MaxLength);
Source = source?.Truncate(SchemaConstants.RequestData_Source_MaxLength);
Name = activity.DisplayName;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,6 @@ public TelemetryItem(Activity activity, ref ActivityTagsProcessor activityTagsPr
}

SetAuthenticatedUserId(ref activityTagsProcessor);

// we only have mapping for server spans
// todo: non-server spans
if (activity.Kind == ActivityKind.Server)
{
Tags[ContextTagKeys.AiOperationName.ToString()] = activityTagsProcessor.activityType.HasFlag(OperationType.V2)
? TraceHelper.GetNewSchemaOperationName(activity, null, ref activityTagsProcessor.MappedTags)
: TraceHelper.GetOperationName(activity, ref activityTagsProcessor.MappedTags);
Tags[ContextTagKeys.AiLocationIp.ToString()] = TraceHelper.GetLocationIp(ref activityTagsProcessor.MappedTags);
}

SetResourceSdkVersionAndIkey(resource, instrumentationKey);
if (AzMonList.GetTagValue(ref activityTagsProcessor.MappedTags, "sampleRate") is float sampleRate)
{
Expand All @@ -62,14 +51,6 @@ public TelemetryItem(string name, TelemetryItem telemetryItem, ActivitySpanId ac
Tags["ai.user.userAgent"] = userAgent;
}

// we only have mapping for server spans
// todo: non-server spans
if (kind == ActivityKind.Server)
{
Tags[ContextTagKeys.AiOperationName.ToString()] = telemetryItem.Tags[ContextTagKeys.AiOperationName.ToString()];
Tags[ContextTagKeys.AiLocationIp.ToString()] = telemetryItem.Tags[ContextTagKeys.AiLocationIp.ToString()];
}

Tags[ContextTagKeys.AiCloudRole.ToString()] = telemetryItem.Tags[ContextTagKeys.AiCloudRole.ToString()];
Tags[ContextTagKeys.AiCloudRoleInstance.ToString()] = telemetryItem.Tags[ContextTagKeys.AiCloudRoleInstance.ToString()];
Tags[ContextTagKeys.AiInternalSdkVersion.ToString()] = SdkVersionUtils.s_sdkVersion;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,8 @@ internal struct ActivityTagsProcessor
SemanticConventions.AttributeEndpointAddress,
// required - Messaging
SemanticConventions.AttributeMessagingSystem,
SemanticConventions.AttributeMessagingDestination,
SemanticConventions.AttributeMessagingDestinationKind,
SemanticConventions.AttributeMessagingTempDestination,
SemanticConventions.AttributeMessagingUrl,
SemanticConventions.AttributeMessagingDestinationName,
SemanticConventions.AttributeNetworkProtocolName,

// Others
SemanticConventions.AttributeEnduserId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace Azure.Monitor.OpenTelemetry.Exporter.Internals;
Expand All @@ -15,31 +16,79 @@ internal static class AzMonNewListExtensions
{
try
{
var serverAddress = AzMonList.GetTagValue(ref tagObjects, SemanticConventions.AttributeServerAddress)?.ToString();
if (serverAddress != null)
var requestUrlTagObjects = AzMonList.GetTagValues(ref tagObjects, SemanticConventions.AttributeUrlScheme, SemanticConventions.AttributeServerAddress, SemanticConventions.AttributeServerPort, SemanticConventions.AttributeUrlPath, SemanticConventions.AttributeUrlQuery);

var scheme = requestUrlTagObjects[0]?.ToString() ?? string.Empty; // requestUrlTagObjects[0] => SemanticConventions.AttributeUrlScheme.
var host = requestUrlTagObjects[1]?.ToString() ?? string.Empty; // requestUrlTagObjects[1] => SemanticConventions.AttributeServerAddress.
var port = requestUrlTagObjects[2]?.ToString(); // requestUrlTagObjects[2] => SemanticConventions.AttributeServerPort.
port = port != null ? port = $":{port}" : string.Empty;
var path = requestUrlTagObjects[3]?.ToString() ?? string.Empty; // requestUrlTagObjects[3] => SemanticConventions.AttributeUrlPath.
var queryString = requestUrlTagObjects[4]?.ToString() ?? string.Empty; // requestUrlTagObjects[4] => SemanticConventions.AttributeUrlQuery.

var length = scheme.Length + Uri.SchemeDelimiter.Length + host.Length + port.Length + path.Length + queryString.Length;

var urlStringBuilder = new System.Text.StringBuilder(length)
.Append(scheme)
.Append(Uri.SchemeDelimiter)
.Append(host)
.Append(port)
.Append(path)
.Append(queryString);

return urlStringBuilder.ToString();
}
catch
{
// If URI building fails, there is no need to throw an exception. Instead, we can simply return null.
}

return null;
}

///<summary>
/// Gets messaging url from activity tag objects.
///</summary>
internal static (string? MessagingUrl, string? SourceOrTarget) GetMessagingUrlAndSourceOrTarget(this AzMonList tagObjects, ActivityKind activityKind)
{
string? messagingUrl = null;
string? sourceOrTarget = null;

try
{
var host = AzMonList.GetTagValue(ref tagObjects, SemanticConventions.AttributeServerAddress)?.ToString()
?? AzMonList.GetTagValue(ref tagObjects, SemanticConventions.AttributeNetPeerName)?.ToString();
if (!string.IsNullOrEmpty(host))
{
UriBuilder uriBuilder = new()
{
Scheme = AzMonList.GetTagValue(ref tagObjects, SemanticConventions.AttributeUrlScheme)?.ToString(),
Host = serverAddress,
Path = AzMonList.GetTagValue(ref tagObjects, SemanticConventions.AttributeUrlPath)?.ToString(),
Query = AzMonList.GetTagValue(ref tagObjects, SemanticConventions.AttributeUrlQuery)?.ToString()
};
object?[] messagingTagObjects;

if (int.TryParse(AzMonList.GetTagValue(ref tagObjects, SemanticConventions.AttributeServerPort)?.ToString(), out int port))
messagingTagObjects = AzMonList.GetTagValues(ref tagObjects, SemanticConventions.AttributeNetworkProtocolName, SemanticConventions.AttributeMessagingDestinationName);
var protocolName = messagingTagObjects[0]?.ToString() ?? string.Empty; // messagingTagObjects[0] => SemanticConventions.AttributeNetworkProtocolName.
var destinationName = messagingTagObjects[1]?.ToString() ?? string.Empty; // messagingTagObjects[1] => SemanticConventions.AttributeMessagingDestinationName.

if (destinationName.Length > 0)
{
uriBuilder.Port = port;
destinationName = $"/{destinationName}";
}

return uriBuilder.Uri.AbsoluteUri;
sourceOrTarget = $"{host}{destinationName}";

var length = protocolName.Length + (protocolName?.Length > 0 ? Uri.SchemeDelimiter.Length : 0) + host!.Length + destinationName.Length;

var messagingStringBuilder = new System.Text.StringBuilder(length)
.Append(protocolName)
.Append(string.IsNullOrEmpty(protocolName) ? null : Uri.SchemeDelimiter)
.Append(host)
.Append(destinationName);

messagingUrl = messagingStringBuilder.ToString();
}
}
catch
{
// If URI building fails, there is no need to throw an exception. Instead, we can simply return null.
// If Messaging Url building fails, there is no need to throw an exception. Instead, we can simply return null.
}

return null;
return (MessagingUrl: messagingUrl, SourceOrTarget: sourceOrTarget);
}

///<summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,5 +163,9 @@ internal static class SemanticConventions
public const string AttributeUrlQuery = "url.query";
public const string AttributeUserAgentOriginal = "user_agent.original"; // replaces: "http.user_agent" (AttributeHttpUserAgent)
public const string AttributeServerSocketAddress = "server.socket.address"; // replaces: "net.peer.ip" (AttributeNetPeerIp)

// 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";
}
}
Loading

0 comments on commit 39da8e5

Please sign in to comment.