Skip to content

Commit 99286ce

Browse files
[AzureMonitorExporter] Add Microsoft override attributes for Application Insights compatibility (#54023)
* Support Microsoft Application Insights Override Attributes * Test updates. * copilot feedback * fix accidental public api change.
1 parent 88b13bb commit 99286ce

File tree

8 files changed

+494
-5
lines changed

8 files changed

+494
-5
lines changed

sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Customizations/Models/RemoteDependencyData.cs

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public RemoteDependencyData(int version, Activity activity, ref ActivityTagsProc
2222
activityTagsProcessor.activityType &= ~OperationType.V2;
2323
}
2424

25+
// Process based on operation type
2526
switch (activityTagsProcessor.activityType)
2627
{
2728
case OperationType.Http:
@@ -38,6 +39,42 @@ public RemoteDependencyData(int version, Activity activity, ref ActivityTagsProc
3839
break;
3940
}
4041

42+
// Check for Microsoft override attributes only if present (avoids overhead for standalone OTel usage)
43+
if (activityTagsProcessor.HasOverrideAttributes)
44+
{
45+
var overrideData = AzMonList.GetTagValue(ref activityTagsProcessor.MappedTags, SemanticConventions.AttributeMicrosoftDependencyData)?.ToString();
46+
var overrideName = AzMonList.GetTagValue(ref activityTagsProcessor.MappedTags, SemanticConventions.AttributeMicrosoftDependencyName)?.ToString();
47+
var overrideTarget = AzMonList.GetTagValue(ref activityTagsProcessor.MappedTags, SemanticConventions.AttributeMicrosoftDependencyTarget)?.ToString();
48+
var overrideType = AzMonList.GetTagValue(ref activityTagsProcessor.MappedTags, SemanticConventions.AttributeMicrosoftDependencyType)?.ToString();
49+
var overrideResultCode = AzMonList.GetTagValue(ref activityTagsProcessor.MappedTags, SemanticConventions.AttributeMicrosoftDependencyResultCode)?.ToString();
50+
51+
// Apply overrides if present (these take precedence)
52+
if (!string.IsNullOrEmpty(overrideData))
53+
{
54+
Data = overrideData.Truncate(SchemaConstants.RemoteDependencyData_Data_MaxLength);
55+
}
56+
57+
if (!string.IsNullOrEmpty(overrideName))
58+
{
59+
dependencyName = overrideName;
60+
}
61+
62+
if (!string.IsNullOrEmpty(overrideTarget))
63+
{
64+
Target = overrideTarget.Truncate(SchemaConstants.RemoteDependencyData_Target_MaxLength);
65+
}
66+
67+
if (!string.IsNullOrEmpty(overrideType))
68+
{
69+
Type = overrideType.Truncate(SchemaConstants.RemoteDependencyData_Type_MaxLength);
70+
}
71+
72+
if (!string.IsNullOrEmpty(overrideResultCode))
73+
{
74+
ResultCode = overrideResultCode.Truncate(SchemaConstants.RemoteDependencyData_ResultCode_MaxLength);
75+
}
76+
}
77+
4178
dependencyName ??= activity.DisplayName;
4279
Name = dependencyName?.Truncate(SchemaConstants.RemoteDependencyData_Name_MaxLength);
4380
Id = activity.Context.SpanId.ToHexString();
@@ -46,13 +83,20 @@ public RemoteDependencyData(int version, Activity activity, ref ActivityTagsProc
4683
: SchemaConstants.Duration_MaxValue;
4784
Success = activity.Status != ActivityStatusCode.Error;
4885

86+
// Set Type from Azure namespace if present (unless already set by override attribute)
4987
if (activityTagsProcessor.AzureNamespace != null)
5088
{
51-
Type = TraceHelper.GetAzureSDKDependencyType(activity.Kind, activityTagsProcessor.AzureNamespace);
89+
if (string.IsNullOrEmpty(Type))
90+
{
91+
Type = TraceHelper.GetAzureSDKDependencyType(activity.Kind, activityTagsProcessor.AzureNamespace);
92+
}
5293
}
5394
else if (activity.Kind == ActivityKind.Internal)
5495
{
55-
Type = "InProc";
96+
if (string.IsNullOrEmpty(Type))
97+
{
98+
Type = "InProc";
99+
}
56100
}
57101

58102
TraceHelper.AddActivityLinksToProperties(activity, ref activityTagsProcessor.UnMappedTags);

sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Customizations/Models/RequestData.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ internal partial class RequestData
1414
public RequestData(int version, Activity activity, ref ActivityTagsProcessor activityTagsProcessor) : base(version)
1515
{
1616
string? responseCode = null;
17+
string? requestName = null;
1718
Properties = new ChangeTrackingDictionary<string, string>();
1819
Measurements = new ChangeTrackingDictionary<string, double>();
1920

21+
// Process based on operation type
2022
switch (activityTagsProcessor.activityType)
2123
{
2224
case OperationType.Http | OperationType.V2:
@@ -30,6 +32,37 @@ public RequestData(int version, Activity activity, ref ActivityTagsProcessor act
3032
break;
3133
}
3234

35+
// Check for Microsoft override attributes only if present (avoids overhead for standalone OTel usage)
36+
if (activityTagsProcessor.HasOverrideAttributes)
37+
{
38+
var overrideUrl = AzMonList.GetTagValue(ref activityTagsProcessor.MappedTags, SemanticConventions.AttributeMicrosoftRequestUrl)?.ToString();
39+
var overrideName = AzMonList.GetTagValue(ref activityTagsProcessor.MappedTags, SemanticConventions.AttributeMicrosoftRequestName)?.ToString();
40+
var overrideSource = AzMonList.GetTagValue(ref activityTagsProcessor.MappedTags, SemanticConventions.AttributeMicrosoftRequestSource)?.ToString();
41+
var overrideResultCode = AzMonList.GetTagValue(ref activityTagsProcessor.MappedTags, SemanticConventions.AttributeMicrosoftRequestResultCode)?.ToString();
42+
43+
// Apply overrides if present (these take precedence)
44+
if (!string.IsNullOrEmpty(overrideUrl))
45+
{
46+
Url = overrideUrl.Truncate(SchemaConstants.RequestData_Url_MaxLength);
47+
}
48+
49+
if (!string.IsNullOrEmpty(overrideName))
50+
{
51+
requestName = overrideName;
52+
}
53+
54+
if (!string.IsNullOrEmpty(overrideSource))
55+
{
56+
Source = overrideSource.Truncate(SchemaConstants.RequestData_Source_MaxLength);
57+
}
58+
59+
if (!string.IsNullOrEmpty(overrideResultCode))
60+
{
61+
responseCode = overrideResultCode;
62+
}
63+
}
64+
65+
Name = requestName?.Truncate(SchemaConstants.RequestData_Name_MaxLength);
3366
Id = activity.Context.SpanId.ToHexString();
3467
Duration = activity.Duration < SchemaConstants.RequestData_Duration_LessThanDays
3568
? activity.Duration.ToString("c", CultureInfo.InvariantCulture)

sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/ActivityTagsProcessor.cs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,19 @@ internal struct ActivityTagsProcessor
5959
// Others
6060
SemanticConventions.AttributeEnduserId,
6161
SemanticConventions.AttributeEnduserPseudoId,
62-
"microsoft.client.ip"
62+
"microsoft.client.ip",
63+
64+
// Microsoft Application Insights Override Attributes
65+
SemanticConventions.AttributeMicrosoftDependencyData,
66+
SemanticConventions.AttributeMicrosoftDependencyName,
67+
SemanticConventions.AttributeMicrosoftOperationName,
68+
SemanticConventions.AttributeMicrosoftDependencyResultCode,
69+
SemanticConventions.AttributeMicrosoftDependencyTarget,
70+
SemanticConventions.AttributeMicrosoftDependencyType,
71+
SemanticConventions.AttributeMicrosoftRequestName,
72+
SemanticConventions.AttributeMicrosoftRequestUrl,
73+
SemanticConventions.AttributeMicrosoftRequestSource,
74+
SemanticConventions.AttributeMicrosoftRequestResultCode
6375
};
6476

6577
internal static readonly HashSet<string> s_semanticsSet = new(s_semantics);
@@ -75,6 +87,8 @@ internal struct ActivityTagsProcessor
7587

7688
public string? EndUserPseudoId { get; private set; } = null;
7789

90+
public bool HasOverrideAttributes { get; private set; } = false;
91+
7892
public ActivityTagsProcessor()
7993
{
8094
MappedTags = AzMonList.Initialize();
@@ -118,6 +132,18 @@ public void CategorizeTags(Activity activity)
118132
case SemanticConventions.AttributeEnduserPseudoId:
119133
EndUserPseudoId = tag.Value.ToString();
120134
continue;
135+
case SemanticConventions.AttributeMicrosoftDependencyData:
136+
case SemanticConventions.AttributeMicrosoftDependencyName:
137+
case SemanticConventions.AttributeMicrosoftDependencyTarget:
138+
case SemanticConventions.AttributeMicrosoftDependencyType:
139+
case SemanticConventions.AttributeMicrosoftDependencyResultCode:
140+
case SemanticConventions.AttributeMicrosoftOperationName:
141+
case SemanticConventions.AttributeMicrosoftRequestName:
142+
case SemanticConventions.AttributeMicrosoftRequestUrl:
143+
case SemanticConventions.AttributeMicrosoftRequestSource:
144+
case SemanticConventions.AttributeMicrosoftRequestResultCode:
145+
HasOverrideAttributes = true;
146+
break;
121147
}
122148

123149
AzMonList.Add(ref MappedTags, tag);

sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/AzMonList.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public static void Clear(ref AzMonList list)
6363

6464
for (int i = 0; i < length; i++)
6565
{
66-
if (ReferenceEquals(list[i].Key, tagName))
66+
if (string.Equals(list[i].Key, tagName, StringComparison.Ordinal))
6767
{
6868
return list[i].Value;
6969
}

sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/SemanticConventions.cs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,5 +175,69 @@ internal static class SemanticConventions
175175
public const string AttributeDbQuerySummary = "db.query.summary";
176176
public const string AttributeDbQueryText = "db.query.text";
177177
public const string AttributeDbStoredProcedureName = "db.stored_procedure.name";
178+
179+
// Microsoft Application Insights Override Attributes
180+
// These attributes allow explicit mapping from Application Insights fields to OpenTelemetry attributes
181+
// When present, these override computed values from standard semantic conventions
182+
183+
/// <summary>
184+
/// Override attribute for dependency data field.
185+
/// When present, takes precedence over computed data from semantic conventions.
186+
/// </summary>
187+
public const string AttributeMicrosoftDependencyData = "microsoft.dependency.data";
188+
189+
/// <summary>
190+
/// Override attribute for dependency name field.
191+
/// When present, takes precedence over computed name from semantic conventions.
192+
/// </summary>
193+
public const string AttributeMicrosoftDependencyName = "microsoft.dependency.name";
194+
195+
/// <summary>
196+
/// Override attribute for operation name field.
197+
/// When present, takes precedence over computed operation name.
198+
/// </summary>
199+
public const string AttributeMicrosoftOperationName = "microsoft.operation_name";
200+
201+
/// <summary>
202+
/// Override attribute for dependency result code field.
203+
/// When present, takes precedence over computed result code from semantic conventions.
204+
/// </summary>
205+
public const string AttributeMicrosoftDependencyResultCode = "microsoft.dependency.resultCode";
206+
207+
/// <summary>
208+
/// Override attribute for dependency target field.
209+
/// When present, takes precedence over computed target from semantic conventions.
210+
/// </summary>
211+
public const string AttributeMicrosoftDependencyTarget = "microsoft.dependency.target";
212+
213+
/// <summary>
214+
/// Override attribute for dependency type field.
215+
/// When present, takes precedence over computed type from semantic conventions.
216+
/// </summary>
217+
public const string AttributeMicrosoftDependencyType = "microsoft.dependency.type";
218+
219+
/// <summary>
220+
/// Override attribute for request name field.
221+
/// When present, takes precedence over Activity.DisplayName.
222+
/// </summary>
223+
public const string AttributeMicrosoftRequestName = "microsoft.request.name";
224+
225+
/// <summary>
226+
/// Override attribute for request URL field.
227+
/// When present, takes precedence over computed URL from semantic conventions.
228+
/// </summary>
229+
public const string AttributeMicrosoftRequestUrl = "microsoft.request.url";
230+
231+
/// <summary>
232+
/// Override attribute for request source field.
233+
/// When present, takes precedence over computed source from semantic conventions.
234+
/// </summary>
235+
public const string AttributeMicrosoftRequestSource = "microsoft.request.source";
236+
237+
/// <summary>
238+
/// Override attribute for request result code field.
239+
/// When present, takes precedence over computed result code from semantic conventions.
240+
/// </summary>
241+
public const string AttributeMicrosoftRequestResultCode = "microsoft.request.resultCode";
178242
}
179243
}

sdk/monitor/Azure.Monitor.OpenTelemetry.Exporter/src/Internals/TraceHelper.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@ internal static (List<TelemetryItem> TelemetryItems, TelemetrySchemaTypeCounter
5050
{
5151
case TelemetryType.Request:
5252
var requestData = new RequestData(Version, activity, ref activityTagsProcessor);
53-
requestData.Name = telemetryItem.Tags.TryGetValue(ContextTagKeys.AiOperationName.ToString(), out var operationName) ? operationName.Truncate(SchemaConstants.RequestData_Name_MaxLength) : activity.DisplayName.Truncate(SchemaConstants.RequestData_Name_MaxLength);
53+
// Only set Name if not already set by override attribute
54+
if (string.IsNullOrEmpty(requestData.Name))
55+
{
56+
requestData.Name = telemetryItem.Tags.TryGetValue(ContextTagKeys.AiOperationName.ToString(), out var operationName) ? operationName.Truncate(SchemaConstants.RequestData_Name_MaxLength) : activity.DisplayName.Truncate(SchemaConstants.RequestData_Name_MaxLength);
57+
}
5458
telemetryItem.Data = new MonitorBase
5559
{
5660
BaseType = "RequestData",

0 commit comments

Comments
 (0)