Skip to content

Commit

Permalink
Refactor operation for new schema (#37657)
Browse files Browse the repository at this point in the history
  • Loading branch information
vishweshbankwar authored Jul 18, 2023
1 parent 53f235f commit f8ac1ac
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 211 deletions.
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

0 comments on commit f8ac1ac

Please sign in to comment.