Skip to content

Commit

Permalink
[AzureMonitorExporter] Add support new HTTP semantics - Dependency Te…
Browse files Browse the repository at this point in the history
…lemetry (#37464)

* Add support new HTTP semantics - Dependency Telemetry

* Merge two conditions.

* PR feedback
  • Loading branch information
rajkumar-rangaraj authored Jul 7, 2023
1 parent a919c48 commit 0a0ba46
Show file tree
Hide file tree
Showing 15 changed files with 452 additions and 166 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

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

namespace Azure.Monitor.OpenTelemetry.Exporter.Models;

internal partial class RemoteDependencyData
{
public RemoteDependencyData(int version, Activity activity, ref ActivityTagsProcessor activityTagsProcessor, string schemaVersion) : base(version)
{
Properties = new ChangeTrackingDictionary<string, string>();
Measurements = new ChangeTrackingDictionary<string, double>();

string? httpUrl = null;
string dependencyName;

if (activityTagsProcessor.activityType.HasFlag(OperationType.Http))
{
httpUrl = AzMonList.GetTagValue(ref activityTagsProcessor.MappedTags, SemanticConventions.AttributeUrlFull)?.ToString();
dependencyName = activityTagsProcessor.MappedTags.GetNewSchemaHttpDependencyName(httpUrl) ?? activity.DisplayName;
Data = httpUrl.Truncate(SchemaConstants.RemoteDependencyData_Data_MaxLength);
Target = activityTagsProcessor.MappedTags.GetNewSchemaHttpDependencyTarget().Truncate(SchemaConstants.RemoteDependencyData_Target_MaxLength);
Type = "Http";
ResultCode = AzMonList.GetTagValue(ref activityTagsProcessor.MappedTags, SemanticConventions.AttributeHttpResponseStatusCode)
?.ToString().Truncate(SchemaConstants.RemoteDependencyData_ResultCode_MaxLength)
?? "0";
}
else
{
dependencyName = activity.DisplayName;
}

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)
: SchemaConstants.Duration_MaxValue;
Success = activity.Status != ActivityStatusCode.Error;

// TODO: Other operation types.

if (activityTagsProcessor.AzureNamespace != null)
{
if (activity.Kind == ActivityKind.Internal)
{
Type = $"InProc | {activityTagsProcessor.AzureNamespace}";
}
else if (activity.Kind == ActivityKind.Producer)
{
Type = $"Queue Message | {activityTagsProcessor.AzureNamespace}";
}
else
{
// The Azure SDK sets az.namespace with its resource provider information.
// When ActivityKind is not internal and az.namespace is present, set the value of Type to az.namespace.
Type = activityTagsProcessor.AzureNamespace ?? Type;
}
}
else if (activity.Kind == ActivityKind.Internal)
{
Type = "InProc";
}

TraceHelper.AddActivityLinksToProperties(activity, ref activityTagsProcessor.UnMappedTags);
TraceHelper.AddPropertiesToTelemetry(Properties, ref activityTagsProcessor.UnMappedTags);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

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

namespace Azure.Monitor.OpenTelemetry.Exporter.Models;

internal partial class RequestData
{
public RequestData(int version, Activity activity, ref ActivityTagsProcessor activityTagsProcessor, string schemaVersion) : base(version)
{
string? url = null;

if (activityTagsProcessor.activityType.HasFlag(OperationType.Http))
{
url = activityTagsProcessor.MappedTags.GetNewSchemaRequestUrl();
}

Id = activity.Context.SpanId.ToHexString();
Name = TraceHelper.GetNewSchemaOperationName(activity, url, ref activityTagsProcessor.MappedTags).Truncate(SchemaConstants.RequestData_Name_MaxLength);
Duration = activity.Duration < SchemaConstants.RequestData_Duration_LessThanDays
? activity.Duration.ToString("c", CultureInfo.InvariantCulture)
: SchemaConstants.Duration_MaxValue;
ResponseCode = AzMonList.GetTagValue(ref activityTagsProcessor.MappedTags, SemanticConventions.AttributeHttpResponseStatusCode)
?.ToString().Truncate(SchemaConstants.RequestData_ResponseCode_MaxLength)
?? "0";

Success = IsSuccess(activity, ResponseCode, activityTagsProcessor.activityType);

Url = url.Truncate(SchemaConstants.RequestData_Url_MaxLength);
Properties = new ChangeTrackingDictionary<string, string>();
Measurements = new ChangeTrackingDictionary<string, double>();

if (activity.Kind == ActivityKind.Consumer)
{
TraceHelper.AddEnqueuedTimeToMeasurementsAndLinksToProperties(activity, Measurements, ref activityTagsProcessor.UnMappedTags);
}
else
{
TraceHelper.AddActivityLinksToProperties(activity, ref activityTagsProcessor.UnMappedTags);
}

TraceHelper.AddPropertiesToTelemetry(Properties, ref activityTagsProcessor.UnMappedTags);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public TelemetryItem(Activity activity, ref ActivityTagsProcessor activityTagsPr
if (activity.Kind == ActivityKind.Server)
{
Tags[ContextTagKeys.AiOperationName.ToString()] = activityTagsProcessor.activityType.HasFlag(OperationType.V2)
? TraceHelper.GetV2OperationName(activity, null, ref activityTagsProcessor.MappedTags)
? TraceHelper.GetNewSchemaOperationName(activity, null, ref activityTagsProcessor.MappedTags)
: TraceHelper.GetOperationName(activity, ref activityTagsProcessor.MappedTags);
Tags[ContextTagKeys.AiLocationIp.ToString()] = TraceHelper.GetLocationIp(ref activityTagsProcessor.MappedTags);
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Runtime.CompilerServices;

namespace Azure.Monitor.OpenTelemetry.Exporter.Internals;

internal static class AzMonNewListExtensions
{
///<summary>
/// Gets http request url from activity tag objects.
///</summary>
internal static string? GetNewSchemaRequestUrl(this AzMonList tagObjects)
{
try
{
var serverAddress = AzMonList.GetTagValue(ref tagObjects, SemanticConventions.AttributeServerAddress)?.ToString();
if (serverAddress != null)
{
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()
};

if (int.TryParse(AzMonList.GetTagValue(ref tagObjects, SemanticConventions.AttributeServerPort)?.ToString(), out int port))
{
uriBuilder.Port = port;
}

return uriBuilder.Uri.AbsoluteUri;
}
}
catch
{
// If URI building fails, there is no need to throw an exception. Instead, we can simply return null.
}

return null;
}

///<summary>
/// Gets Http dependency target from activity tag objects.
///</summary>
internal static string? GetNewSchemaHttpDependencyTarget(this AzMonList tagObjects)
{
var tagValues = AzMonList.GetTagValues(ref tagObjects, SemanticConventions.AttributeServerAddress, SemanticConventions.AttributeServerPort);
var serverAddress = tagValues[0]?.ToString(); // tagValues[0] => SemanticConventions.AttributeServerAddress.
var serverPort = tagValues[1]?.ToString(); // tagValues[1] => SemanticConventions.AttributeServerPort.

if (string.IsNullOrWhiteSpace(serverAddress))
{
return null;
}

if (int.TryParse(serverPort, out int port) && !IsDefaultPort(port))
{
return $"{serverAddress}:{serverPort}";
}

return serverAddress;
}

internal static string? GetNewSchemaHttpDependencyName(this AzMonList tagObjects, string? httpUrl)
{
if (string.IsNullOrWhiteSpace(httpUrl))
{
return null;
}

var httpMethod = AzMonList.GetTagValue(ref tagObjects, SemanticConventions.AttributeHttpRequestMethod)?.ToString();
if (!string.IsNullOrWhiteSpace(httpMethod))
{
if (Uri.TryCreate(httpUrl!.ToString(), UriKind.Absolute, out var uri) && uri.IsAbsoluteUri)
{
return $"{httpMethod} {uri.AbsolutePath}";
}
}

return null;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsDefaultPort(int port)
{
return port == 0 || port == 80 || port == 443;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ internal enum OperationType
Http = 16,
Messaging = 32,
Rpc = 64,
// TODO: https://github.com/Azure/azure-sdk-for-net/pull/37357/files#r1253383825
// Check if V2 could be moved outside of this Enum.
V2 = 128
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,5 +115,7 @@ internal static class SchemaConstants
public const int TelemetryEnvelope_Name_MaxLength = 1024;
public const int TelemetryEnvelope_Time_MaxLength = 64;
public const int TelemetryEnvelope_InstrumentationKey_MaxLength = 40;

public const string DefaultSchemaVersion = "1.21.0";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ internal static List<TelemetryItem> OtelToAzureMonitorTrace(Batch<Activity> batc
{
BaseType = "RequestData",
BaseData = activityTagsProcessor.activityType.HasFlag(OperationType.V2)
? new RequestData(Version, activity, ref activityTagsProcessor, schemaVersion: "V2")
? new RequestData(Version, activity, ref activityTagsProcessor, schemaVersion: SchemaConstants.DefaultSchemaVersion)
: new RequestData(Version, activity, ref activityTagsProcessor)
};
break;
Expand Down Expand Up @@ -173,7 +173,7 @@ internal static string GetOperationName(Activity activity, ref AzMonList MappedT
return activity.DisplayName;
}

internal static string GetV2OperationName(Activity activity, string? url, ref AzMonList MappedTags)
internal static string GetNewSchemaOperationName(Activity activity, string? url, ref AzMonList MappedTags)
{
var httpMethod = AzMonList.GetTagValue(ref MappedTags, SemanticConventions.AttributeHttpRequestMethod)?.ToString();
if (!string.IsNullOrWhiteSpace(httpMethod))
Expand All @@ -187,7 +187,7 @@ internal static string GetV2OperationName(Activity activity, string? url, ref A
return $"{httpMethod} {httpRoute}";
}

url ??= MappedTags.GetV2RequestUrl();
url ??= MappedTags.GetNewSchemaRequestUrl();
if (url != null)
{
return $"{httpMethod} {url}";
Expand Down

This file was deleted.

Loading

0 comments on commit 0a0ba46

Please sign in to comment.