diff --git a/src/Qyl.OpenTelemetry.AutoInstrumentation/Internal/QylHttpActivityPolicy.cs b/src/Qyl.OpenTelemetry.AutoInstrumentation/Internal/QylHttpActivityPolicy.cs
index e4f31de..80ca71b 100644
--- a/src/Qyl.OpenTelemetry.AutoInstrumentation/Internal/QylHttpActivityPolicy.cs
+++ b/src/Qyl.OpenTelemetry.AutoInstrumentation/Internal/QylHttpActivityPolicy.cs
@@ -7,6 +7,7 @@ internal static class QylHttpActivityPolicy
public static Activity? StartClientActivity(
string instrumentationDomain,
string method,
+ string? methodOriginal,
Uri? requestUri,
string? rawRequestUri)
{
@@ -18,7 +19,7 @@ internal static class QylHttpActivityPolicy
if (activity is null)
return null;
- SetRequestMethod(activity, method);
+ SetRequestMethod(activity, method, methodOriginal);
if (requestUri is not null)
SetClientUrl(activity, requestUri, rawRequestUri);
@@ -27,9 +28,11 @@ internal static class QylHttpActivityPolicy
public static Activity? StartServerActivity(
string method,
+ string? methodOriginal,
string? route,
string? path,
- string? query)
+ string? query,
+ string? scheme)
{
var activity = QylActivityFactory.StartTraceActivity(
QylAutoInstrumentationIds.AspNetCore,
@@ -39,7 +42,9 @@ internal static class QylHttpActivityPolicy
if (activity is null)
return null;
- SetRequestMethod(activity, method);
+ SetRequestMethod(activity, method, methodOriginal);
+ if (!string.IsNullOrEmpty(scheme))
+ activity.SetTag(QylSemanticAttributes.UrlScheme, scheme);
if (path is not null)
activity.SetTag(QylSemanticAttributes.UrlPath, path);
if (!string.IsNullOrEmpty(query))
@@ -73,8 +78,12 @@ public static void SetResponseStatus(Activity activity, int statusCode, int erro
public static void SetResponseStatus(Activity activity, int statusCode)
=> activity.SetTag(QylSemanticAttributes.HttpResponseStatusCode, statusCode);
- private static void SetRequestMethod(Activity activity, string method)
- => activity.SetTag(QylSemanticAttributes.HttpRequestMethod, method);
+ private static void SetRequestMethod(Activity activity, string method, string? methodOriginal)
+ {
+ activity.SetTag(QylSemanticAttributes.HttpRequestMethod, method);
+ if (!string.IsNullOrEmpty(methodOriginal))
+ activity.SetTag(QylSemanticAttributes.HttpRequestMethodOriginal, methodOriginal);
+ }
private static void SetClientUrl(Activity activity, Uri requestUri, string? rawRequestUri)
{
diff --git a/src/Qyl.OpenTelemetry.AutoInstrumentation/PublicAPI.Unshipped.txt b/src/Qyl.OpenTelemetry.AutoInstrumentation/PublicAPI.Unshipped.txt
index e0ce27c..1bf8e86 100644
--- a/src/Qyl.OpenTelemetry.AutoInstrumentation/PublicAPI.Unshipped.txt
+++ b/src/Qyl.OpenTelemetry.AutoInstrumentation/PublicAPI.Unshipped.txt
@@ -1,4 +1,6 @@
#nullable enable
+const Qyl.OpenTelemetry.AutoInstrumentation.QylSemanticAttributes.CodeFunctionName = "code.function.name" -> string!
+const Qyl.OpenTelemetry.AutoInstrumentation.QylSemanticAttributes.UrlScheme = "url.scheme" -> string!
const Qyl.OpenTelemetry.AutoInstrumentation.QylMetricMeters.AspNetCoreAuthenticationMeterName = "Microsoft.AspNetCore.Authentication" -> string!
const Qyl.OpenTelemetry.AutoInstrumentation.QylMetricMeters.AspNetCoreAuthorizationMeterName = "Microsoft.AspNetCore.Authorization" -> string!
const Qyl.OpenTelemetry.AutoInstrumentation.QylMetricMeters.AspNetCoreDiagnosticsMeterName = "Microsoft.AspNetCore.Diagnostics" -> string!
diff --git a/src/Qyl.OpenTelemetry.AutoInstrumentation/QylHttpMethod.cs b/src/Qyl.OpenTelemetry.AutoInstrumentation/QylHttpMethod.cs
index cb4b9b6..3c662e1 100644
--- a/src/Qyl.OpenTelemetry.AutoInstrumentation/QylHttpMethod.cs
+++ b/src/Qyl.OpenTelemetry.AutoInstrumentation/QylHttpMethod.cs
@@ -36,4 +36,19 @@ public static string Normalize(string? method)
return QylSemanticAttributes.HttpRequestMethodOther;
}
+
+ ///
+ /// Normalizes and reports the raw value as when it
+ /// is non-standard. Per OTel, http.request.method_original MUST be set whenever http.request.method is
+ /// _OTHER; is null for the standard methods.
+ ///
+ public static string Normalize(string? method, out string? original)
+ {
+ var normalized = Normalize(method);
+ original = !string.IsNullOrEmpty(method)
+ && string.Equals(normalized, QylSemanticAttributes.HttpRequestMethodOther, StringComparison.Ordinal)
+ ? method
+ : null;
+ return normalized;
+ }
}
diff --git a/src/Qyl.OpenTelemetry.AutoInstrumentation/QylInterceptedAspNetCore.cs b/src/Qyl.OpenTelemetry.AutoInstrumentation/QylInterceptedAspNetCore.cs
index cd7316f..65324b5 100644
--- a/src/Qyl.OpenTelemetry.AutoInstrumentation/QylInterceptedAspNetCore.cs
+++ b/src/Qyl.OpenTelemetry.AutoInstrumentation/QylInterceptedAspNetCore.cs
@@ -63,13 +63,15 @@ public static Task InvokeAsync(RequestDelegate requestDelegate, HttpContext cont
if (!options.IsInstrumentationEnabled(QylAutoInstrumentationSignal.Traces, QylAutoInstrumentationIds.AspNetCore))
return null;
- var method = QylHttpMethod.Normalize(context.Request.Method);
+ var method = QylHttpMethod.Normalize(context.Request.Method, out var methodOriginal);
var route = GetRoute(context);
var activity = QylHttpActivityPolicy.StartServerActivity(
method,
+ methodOriginal,
route,
context.Request.Path.Value,
- context.Request.QueryString.HasValue ? context.Request.QueryString.Value![1..] : null);
+ context.Request.QueryString.HasValue ? context.Request.QueryString.Value![1..] : null,
+ context.Request.Scheme);
if (activity is null)
return null;
diff --git a/src/Qyl.OpenTelemetry.AutoInstrumentation/QylInterceptedAzure.cs b/src/Qyl.OpenTelemetry.AutoInstrumentation/QylInterceptedAzure.cs
index 12a4aae..4d68a19 100644
--- a/src/Qyl.OpenTelemetry.AutoInstrumentation/QylInterceptedAzure.cs
+++ b/src/Qyl.OpenTelemetry.AutoInstrumentation/QylInterceptedAzure.cs
@@ -12,11 +12,17 @@ public static class QylInterceptedAzure
/// Runs the Start Activity runtime helper used by source-generated qyl interceptors.
public static Activity? StartActivity(string methodName)
{
- return QylActivityFactory.StartTraceActivity(
+ var activity = QylActivityFactory.StartTraceActivity(
QylAutoInstrumentationIds.Azure,
QylActivityNames.AzureSdk,
ActivityKind.Client,
QylInstrumentationDomains.AzureSdk);
+ // Record the intercepted operation so Azure spans are distinguishable. The span name stays the
+ // stable low-cardinality "Azure SDK"; the specific method goes on code.function.name (CODE RED #9:
+ // the methodName parameter was previously received and dropped).
+ if (activity is not null && !string.IsNullOrEmpty(methodName))
+ activity.SetTag(QylSemanticAttributes.CodeFunctionName, methodName);
+ return activity;
}
/// Runs the Record Exception runtime helper used by source-generated qyl interceptors.
diff --git a/src/Qyl.OpenTelemetry.AutoInstrumentation/QylInterceptedHttpClient.cs b/src/Qyl.OpenTelemetry.AutoInstrumentation/QylInterceptedHttpClient.cs
index 53914ae..abe692c 100644
--- a/src/Qyl.OpenTelemetry.AutoInstrumentation/QylInterceptedHttpClient.cs
+++ b/src/Qyl.OpenTelemetry.AutoInstrumentation/QylInterceptedHttpClient.cs
@@ -584,7 +584,7 @@ private static HttpClientObservation StartHttpClientObservation(
Uri? requestUri,
string? rawRequestUri)
{
- method = QylHttpMethod.Normalize(method);
+ method = QylHttpMethod.Normalize(method, out var methodOriginal);
var startTimeUtc = QylDurationMetrics.GetHttpClientStartTimeUtc(metricsEnabled);
Activity? activity = null;
@@ -594,6 +594,7 @@ private static HttpClientObservation StartHttpClientObservation(
activity = QylHttpActivityPolicy.StartClientActivity(
QylInstrumentationDomains.HttpClient,
method,
+ methodOriginal,
requestUri,
rawRequestUri);
}
diff --git a/src/Qyl.OpenTelemetry.AutoInstrumentation/QylInterceptedHttpWebRequest.cs b/src/Qyl.OpenTelemetry.AutoInstrumentation/QylInterceptedHttpWebRequest.cs
index 8ac1a64..685b2fa 100644
--- a/src/Qyl.OpenTelemetry.AutoInstrumentation/QylInterceptedHttpWebRequest.cs
+++ b/src/Qyl.OpenTelemetry.AutoInstrumentation/QylInterceptedHttpWebRequest.cs
@@ -21,10 +21,11 @@ public static DateTime GetStartTimeUtc()
if (!options.IsInstrumentationEnabled(QylAutoInstrumentationSignal.Traces, QylAutoInstrumentationIds.HttpClient))
return null;
- var method = QylHttpMethod.Normalize(request.Method);
+ var method = QylHttpMethod.Normalize(request.Method, out var methodOriginal);
var activity = QylHttpActivityPolicy.StartClientActivity(
QylInstrumentationDomains.HttpWebRequest,
method,
+ methodOriginal,
request.RequestUri,
request.RequestUri?.ToString());
if (activity is null)
diff --git a/src/Qyl.OpenTelemetry.AutoInstrumentation/QylSemanticAttributes.cs b/src/Qyl.OpenTelemetry.AutoInstrumentation/QylSemanticAttributes.cs
index 6873b76..a199519 100644
--- a/src/Qyl.OpenTelemetry.AutoInstrumentation/QylSemanticAttributes.cs
+++ b/src/Qyl.OpenTelemetry.AutoInstrumentation/QylSemanticAttributes.cs
@@ -62,6 +62,10 @@ public static class QylSemanticAttributes
public const string UrlQuery = UrlAttributes.Query;
/// Well-known Url Full value used by qyl auto-instrumentation.
public const string UrlFull = UrlAttributes.Full;
+ /// Well-known Url Scheme value used by qyl auto-instrumentation.
+ public const string UrlScheme = "url.scheme";
+ /// Well-known Code Function Name value used by qyl auto-instrumentation.
+ public const string CodeFunctionName = "code.function.name";
/// Well-known Dotnet Gc Heap Generation value used by qyl auto-instrumentation.
public const string DotnetGcHeapGeneration = DotnetAttributes.GcHeapGeneration;
diff --git a/tools/Qyl.OpenTelemetry.AutoInstrumentation.WebApiAotDemo/Program.cs b/tools/Qyl.OpenTelemetry.AutoInstrumentation.WebApiAotDemo/Program.cs
index 3da041e..2b6455a 100644
--- a/tools/Qyl.OpenTelemetry.AutoInstrumentation.WebApiAotDemo/Program.cs
+++ b/tools/Qyl.OpenTelemetry.AutoInstrumentation.WebApiAotDemo/Program.cs
@@ -218,6 +218,7 @@ private static IReadOnlyDictionary Canonicalize(IReadOnlyDiction
"qyl.instrumentation.domain",
"http.request.method",
"http.route",
+ "url.scheme",
"http.response.status_code",
"server.address",
"server.port",
diff --git a/tools/Qyl.OpenTelemetry.AutoInstrumentation.WebApiAotDemo/verified/report.json b/tools/Qyl.OpenTelemetry.AutoInstrumentation.WebApiAotDemo/verified/report.json
index 51b2342..f71175e 100644
--- a/tools/Qyl.OpenTelemetry.AutoInstrumentation.WebApiAotDemo/verified/report.json
+++ b/tools/Qyl.OpenTelemetry.AutoInstrumentation.WebApiAotDemo/verified/report.json
@@ -71,7 +71,8 @@
"http.request.method": "GET",
"http.response.status_code": "204",
"http.route": "/probe/{id:int}",
- "qyl.instrumentation.domain": "aspnetcore.server"
+ "qyl.instrumentation.domain": "aspnetcore.server",
+ "url.scheme": "http"
}
},
{
@@ -101,7 +102,8 @@
"http.request.method": "GET",
"http.response.status_code": "204",
"http.route": "/probe/{id:int}",
- "qyl.instrumentation.domain": "aspnetcore.server"
+ "qyl.instrumentation.domain": "aspnetcore.server",
+ "url.scheme": "http"
}
},
{