Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor operation name for new schema #37657

Merged
merged 2 commits into from
Jul 18, 2023
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 @@ -11,12 +11,6 @@ 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 All @@ -37,7 +31,6 @@ public RequestData(int version, Activity activity, ref ActivityTagsProcessor act
}

Id = activity.Context.SpanId.ToHexString();
Name = Name ?? activity.DisplayName;
Duration = activity.Duration < SchemaConstants.RequestData_Duration_LessThanDays
? activity.Duration.ToString("c", CultureInfo.InvariantCulture)
: SchemaConstants.Duration_MaxValue;
Expand Down Expand Up @@ -76,7 +69,6 @@ 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);
responseCode = AzMonList.GetTagValue(ref httpTagObjects, SemanticConventions.AttributeHttpStatusCode)
?.ToString().Truncate(SchemaConstants.RequestData_ResponseCode_MaxLength)
?? "0";
Expand All @@ -85,7 +77,6 @@ private void SetHttpRequestPropertiesAndResponseCode(Activity activity, ref AzMo
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);
responseCode = AzMonList.GetTagValue(ref httpTagObjects, SemanticConventions.AttributeHttpResponseStatusCode)
?.ToString().Truncate(SchemaConstants.RequestData_ResponseCode_MaxLength)
?? "0";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ public TelemetryItem(Activity activity, ref ActivityTagsProcessor activityTagsPr

Tags[ContextTagKeys.AiOperationId.ToString()] = activity.TraceId.ToHexString();

if (activity.Kind == ActivityKind.Server && activityTagsProcessor.activityType.HasFlag(OperationType.V2))
{
Tags[ContextTagKeys.AiOperationName.ToString()] = TraceHelper.GetOperationNameV2(activity, ref activityTagsProcessor.MappedTags);
Tags[ContextTagKeys.AiLocationIp.ToString()] = TraceHelper.GetLocationIp(ref activityTagsProcessor.MappedTags);
}
else if (activity.Kind == ActivityKind.Server && activityTagsProcessor.activityType.HasFlag(OperationType.Http))
{
Tags[ContextTagKeys.AiOperationName.ToString()] = TraceHelper.GetOperationName(activity, ref activityTagsProcessor.MappedTags);
Tags[ContextTagKeys.AiLocationIp.ToString()] = TraceHelper.GetLocationIp(ref activityTagsProcessor.MappedTags);
}

var userAgent = AzMonList.GetTagValue(ref activityTagsProcessor.MappedTags, SemanticConventions.AttributeUserAgentOriginal)?.ToString()
?? AzMonList.GetTagValue(ref activityTagsProcessor.MappedTags, SemanticConventions.AttributeHttpUserAgent)?.ToString();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,26 +45,13 @@ internal static List<TelemetryItem> OtelToAzureMonitorTrace(Batch<Activity> batc
switch (activity.GetTelemetryType())
{
case TelemetryType.Request:
if (activity.Kind == ActivityKind.Server && activityTagsProcessor.activityType.HasFlag(OperationType.Http))
{
var (requestUrl, operationName) = GetHttpOperationNameAndUrl(activity.DisplayName, activityTagsProcessor.activityType, ref activityTagsProcessor.MappedTags);
telemetryItem.Tags[ContextTagKeys.AiOperationName.ToString()] = operationName;
telemetryItem.Tags[ContextTagKeys.AiLocationIp.ToString()] = TraceHelper.GetLocationIp(ref activityTagsProcessor.MappedTags);

telemetryItem.Data = new MonitorBase
{
BaseType = "RequestData",
BaseData = new RequestData(Version, operationName, requestUrl, activity, ref activityTagsProcessor)
};
}
else
var requestData = new RequestData(Version, activity, ref activityTagsProcessor);
requestData.Name = telemetryItem.Tags.TryGetValue(ContextTagKeys.AiOperationName.ToString(), out var operationName) ? operationName.Truncate(SchemaConstants.RequestData_Name_MaxLength) : activity.DisplayName.Truncate(SchemaConstants.RequestData_Name_MaxLength);
telemetryItem.Data = new MonitorBase
{
telemetryItem.Data = new MonitorBase
{
BaseType = "RequestData",
BaseData = new RequestData(Version, activity, ref activityTagsProcessor)
};
}
BaseType = "RequestData",
BaseData = requestData,
};
break;
case TelemetryType.Dependency:
telemetryItem.Data = new MonitorBase
Expand Down Expand Up @@ -186,66 +173,30 @@ internal static string GetOperationName(Activity activity, ref AzMonList MappedT
return activity.DisplayName;
}

internal static string GetNewSchemaOperationName(Activity activity, string? url, ref AzMonList MappedTags)
internal static string GetOperationNameV2(Activity activity, ref AzMonList MappedTags)
{
var httpMethod = AzMonList.GetTagValue(ref MappedTags, SemanticConventions.AttributeHttpRequestMethod)?.ToString();
if (!string.IsNullOrWhiteSpace(httpMethod))
{
var httpRoute = AzMonList.GetTagValue(ref MappedTags, SemanticConventions.AttributeHttpRoute)?.ToString();

// ASP.NET instrumentation assigns route as {controller}/{action}/{id} which would result in the same name for different operations.
// To work around that we will use path from httpUrl.
if (httpRoute?.Contains("{controller}") == false)
// To work around that we will use path from url.path.
if (!string.IsNullOrWhiteSpace(httpRoute) && !httpRoute!.Contains("{controller}"))
{
return $"{httpMethod} {httpRoute}";
}

url ??= MappedTags.GetNewSchemaRequestUrl();
if (url != null)
var httpPath = AzMonList.GetTagValue(ref MappedTags, SemanticConventions.AttributeUrlPath)?.ToString();
if (!string.IsNullOrWhiteSpace(httpPath))
{
return $"{httpMethod} {url}";
return $"{httpMethod} {httpPath}";
}
}

return activity.DisplayName;
}

internal static (string? RequestUrl, string? OperationName) GetHttpOperationNameAndUrl(string activityDisplayName, OperationType operationType, ref AzMonList httpMappedTags)
{
string? httpMethod;
string? httpUrl;

if (operationType.HasFlag(OperationType.V2))
{
httpUrl = httpMappedTags.GetNewSchemaRequestUrl();
httpMethod = AzMonList.GetTagValue(ref httpMappedTags, SemanticConventions.AttributeHttpRequestMethod)?.ToString();
}
else
{
httpUrl = AzMonList.GetTagValue(ref httpMappedTags, SemanticConventions.AttributeHttpUrl)?.ToString();
httpMethod = AzMonList.GetTagValue(ref httpMappedTags, SemanticConventions.AttributeHttpMethod)?.ToString();
}

if (!string.IsNullOrWhiteSpace(httpMethod))
{
var httpRoute = AzMonList.GetTagValue(ref httpMappedTags, SemanticConventions.AttributeHttpRoute)?.ToString();

// ASP.NET instrumentation assigns route as {controller}/{action}/{id} which would result in the same name for different operations.
// To work around that we will use path from httpUrl.
if (httpRoute?.Contains("{controller}") == false)
{
return (RequestUrl: httpUrl, OperationName: $"{httpMethod} {httpRoute}");
}

if (!string.IsNullOrWhiteSpace(httpUrl) && Uri.TryCreate(httpUrl!.ToString(), UriKind.RelativeOrAbsolute, out var uri) && uri.IsAbsoluteUri)
{
return (RequestUrl: httpUrl, OperationName: $"{httpMethod} {uri.AbsolutePath}");
}
}

return (RequestUrl: httpUrl, OperationName: activityDisplayName);
}

private static void AddTelemetryFromActivityEvents(Activity activity, TelemetryItem telemetryItem, List<TelemetryItem> telemetryItems)
{
foreach (ref readonly var @event in activity.EnumerateEvents())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ public void ValidateHttpRequestData()

var requestData = new RequestData(2, activity, ref activityTagsProcessor);

Assert.Equal("GET /search", requestData.Name);
// Name is set later via operation name on TelemetryItem
Assert.Null(requestData.Name);
Assert.Equal(activity.Context.SpanId.ToHexString(), requestData.Id);
Assert.Equal(httpUrl, requestData.Url);
Assert.Equal("0", requestData.ResponseCode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ public void ValidateHttpRequestData()

var requestData = new RequestData(2, activity, ref activityTagsProcessor);

Assert.Equal("GET /search", requestData.Name);
// Name is set later via operation name on TelemetryItem
Assert.Null(requestData.Name);
Assert.Equal(activity.Context.SpanId.ToHexString(), requestData.Id);
Assert.Equal(httpUrl, requestData.Url);
Assert.Equal("200", requestData.ResponseCode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,12 +366,41 @@ public void RequestNameMatchesOperationName()
activity.DisplayName = "displayname";

activity.SetTag(SemanticConventions.AttributeHttpMethod, "GET");
activity.SetTag(SemanticConventions.AttributeHttpRoute, "/api/test");

var activityTagsProcessor = TraceHelper.EnumerateActivityTags(activity);
var telemetryItems = TraceHelper.OtelToAzureMonitorTrace(new Batch<Activity>(new Activity[] { activity }, 1), null, "instrumentationKey");
var telemetryItem = telemetryItems.FirstOrDefault();
var requestData = new RequestData(2, activity, ref activityTagsProcessor);
var requestData = telemetryItem?.Data.BaseData as RequestData;

Assert.NotNull(requestData);
Assert.Equal("GET /api/test", requestData.Name);
Assert.Equal(requestData.Name, telemetryItem?.Tags[ContextTagKeys.AiOperationName.ToString()]);
}

[Fact]
public void RequestNameMatchesOperationNameV2()
{
using ActivitySource activitySource = new ActivitySource(ActivitySourceName);
using var activity = activitySource.StartActivity(
ActivityName,
ActivityKind.Server,
null,
startTime: DateTime.UtcNow);

Assert.NotNull(activity);
activity.DisplayName = "displayname";

activity.SetTag(SemanticConventions.AttributeHttpRequestMethod, "GET");
activity.SetTag(SemanticConventions.AttributeHttpRoute, "/api/test");

var activityTagsProcessor = TraceHelper.EnumerateActivityTags(activity);
var telemetryItems = TraceHelper.OtelToAzureMonitorTrace(new Batch<Activity>(new Activity[] { activity }, 1), null, "instrumentationKey");
var telemetryItem = telemetryItems.FirstOrDefault();
var requestData = telemetryItem?.Data.BaseData as RequestData;

Assert.NotNull(requestData);
Assert.Equal("GET /api/test", requestData.Name);
Assert.Equal(requestData.Name, telemetryItem?.Tags[ContextTagKeys.AiOperationName.ToString()]);
}

Expand Down
Loading