From c8edcfab47fe548a22eb5755bd1c308387827ff4 Mon Sep 17 00:00:00 2001 From: dudik <8845578+dudikeleti@users.noreply.github.com> Date: Wed, 21 Jan 2026 12:58:19 +0100 Subject: [PATCH 1/7] Add code origin support for Net Framework MVC and web api --- .../AspNet/ActionDescriptorWithMethodInfo.cs | 25 ++++ ...rActionInvoker_InvokeAction_Integration.cs | 66 ++++++++++ ...tionDescriptor_ExecuteAsync_Integration.cs | 65 ++++++++++ .../SpanCodeOrigin/EndpointDetector.cs | 42 +++++-- .../AspNet/AspNetMvc5CodeOriginTests.cs | 114 ++++++++++++++++++ .../AspNet/AspNetWebApi2CodeOriginTests.cs | 110 +++++++++++++++++ .../Debugger/EndpointDetectorTests.cs | 56 +++++++++ 7 files changed, 471 insertions(+), 7 deletions(-) create mode 100644 tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ActionDescriptorWithMethodInfo.cs create mode 100644 tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AspNet/AspNetMvc5CodeOriginTests.cs create mode 100644 tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AspNet/AspNetWebApi2CodeOriginTests.cs diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ActionDescriptorWithMethodInfo.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ActionDescriptorWithMethodInfo.cs new file mode 100644 index 000000000000..7cfe5eac00aa --- /dev/null +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ActionDescriptorWithMethodInfo.cs @@ -0,0 +1,25 @@ +// +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. +// + +#if NETFRAMEWORK +#nullable enable + +using System.Reflection; +using Datadog.Trace.DuckTyping; + +namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet +{ + /// + /// Duck type for MVC/WebApi2 descriptors that expose a MethodInfo. + /// + [DuckCopy] + internal struct ActionDescriptorWithMethodInfo + { + [Duck] + public MethodInfo MethodInfo; + } +} +#endif + diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ControllerActionInvoker_InvokeAction_Integration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ControllerActionInvoker_InvokeAction_Integration.cs index 370917f4cd3b..353cdadf35ca 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ControllerActionInvoker_InvokeAction_Integration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ControllerActionInvoker_InvokeAction_Integration.cs @@ -8,14 +8,18 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Reflection; using System.Web; using Datadog.Trace.AppSec; using Datadog.Trace.AppSec.Coordinator; using Datadog.Trace.AspNet; using Datadog.Trace.ClrProfiler.CallTarget; using Datadog.Trace.Configuration; +using Datadog.Trace.Debugger; +using Datadog.Trace.Debugger.SpanCodeOrigin; using Datadog.Trace.DuckTyping; using Datadog.Trace.Logging; +using Datadog.Trace.Vendors.Serilog.Events; namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet { @@ -70,9 +74,71 @@ internal static CallTargetState OnMethodBegin(TActionDescriptor actionDescriptor, SpanCodeOrigin codeOrigin) + { + if (SharedItems.TryPeekScope(HttpContext.Current, AspNetMvcIntegration.HttpContextKey) is not { Root.Span: { } rootSpan }) + { + if (Log.IsEnabled(LogEventLevel.Debug)) + { + Log.Debug("Code origin is enabled for spans but MVC scope was not found in HttpContext."); + } + + return; + } + + if (actionDescriptor is null) + { + if (Log.IsEnabled(LogEventLevel.Debug)) + { + Log.Debug("Code origin is enabled for spans but MVC ActionDescriptor instance was null."); + } + + return; + } + + if (!actionDescriptor.TryDuckCast(out var reflected) || reflected.MethodInfo is not { } actionMethod) + { + if (Log.IsEnabled(LogEventLevel.Debug)) + { + Log.Debug( + "Code origin is enabled for spans but could not extract MVC action MethodInfo from ActionDescriptor type {ActionDescriptorType}.", + actionDescriptor.GetType()); + } + + return; + } + + if (actionMethod.DeclaringType is not { } actionType) + { + if (Log.IsEnabled(LogEventLevel.Debug)) + { + Log.Debug( + "Code origin is enabled for spans but extracted MVC action MethodInfo has no DeclaringType. Method: {Method}.", + actionMethod); + } + + return; + } + + codeOrigin.SetCodeOriginForEntrySpan(rootSpan, actionType, actionMethod); + } + /// /// OnMethodEnd callback /// diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ReflectedHttpActionDescriptor_ExecuteAsync_Integration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ReflectedHttpActionDescriptor_ExecuteAsync_Integration.cs index 10634ea0386f..1a02b8a5ecab 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ReflectedHttpActionDescriptor_ExecuteAsync_Integration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ReflectedHttpActionDescriptor_ExecuteAsync_Integration.cs @@ -16,8 +16,11 @@ using Datadog.Trace.AspNet; using Datadog.Trace.ClrProfiler.CallTarget; using Datadog.Trace.Configuration; +using Datadog.Trace.Debugger; +using Datadog.Trace.Debugger.SpanCodeOrigin; using Datadog.Trace.DuckTyping; using Datadog.Trace.Logging; +using Datadog.Trace.Vendors.Serilog.Events; namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet { @@ -70,9 +73,71 @@ internal static CallTargetState OnMethodBegin(TTarget inst Log.Error(ex, "Error instrumenting method {MethodName}", "System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync()"); } + try + { + var codeOrigin = DebuggerManager.Instance.CodeOrigin; + if (codeOrigin is { Settings.CodeOriginForSpansEnabled: true }) + { + AddSpanCodeOrigin(instance, codeOrigin); + } + } + catch (Exception ex) when (BlockException.GetBlockException(ex) is null) + { + Log.Error(ex, "Error adding code origin for spans in {MethodName}", "System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync()"); + } + return CallTargetState.GetDefault(); } + private static void AddSpanCodeOrigin(TTarget instance, SpanCodeOrigin codeOrigin) + { + if (SharedItems.TryPeekScope(HttpContext.Current, AspNetWebApi2Integration.HttpContextKey) is not { Root.Span: { } rootSpan }) + { + if (Log.IsEnabled(LogEventLevel.Debug)) + { + Log.Debug("Code origin is enabled for spans but WebApi2 scope was not found in HttpContext."); + } + + return; + } + + if (instance is null) + { + if (Log.IsEnabled(LogEventLevel.Debug)) + { + Log.Debug("Code origin is enabled for spans but ReflectedHttpActionDescriptor instance was null."); + } + + return; + } + + if (!instance.TryDuckCast(out var reflected) || reflected.MethodInfo is not { } actionMethod) + { + if (Log.IsEnabled(LogEventLevel.Debug)) + { + Log.Debug( + "Code origin is enabled for spans but could not extract WebApi2 action MethodInfo from ReflectedHttpActionDescriptor type {ActionDescriptorType}.", + instance.GetType()); + } + + return; + } + + if (actionMethod.DeclaringType is not { } actionType) + { + if (Log.IsEnabled(LogEventLevel.Debug)) + { + Log.Debug( + "Code origin is enabled for spans but extracted WebApi2 action MethodInfo has no DeclaringType. Method: {Method}.", + actionMethod); + } + + return; + } + + codeOrigin.SetCodeOriginForEntrySpan(rootSpan, actionType, actionMethod); + } + internal static TResponse? OnAsyncMethodEnd(TTarget instance, TResponse? response, Exception? exception, in CallTargetState state) { var security = Security.Instance; diff --git a/tracer/src/Datadog.Trace/Debugger/SpanCodeOrigin/EndpointDetector.cs b/tracer/src/Datadog.Trace/Debugger/SpanCodeOrigin/EndpointDetector.cs index 1dba428e478d..363a5741b09a 100644 --- a/tracer/src/Datadog.Trace/Debugger/SpanCodeOrigin/EndpointDetector.cs +++ b/tracer/src/Datadog.Trace/Debugger/SpanCodeOrigin/EndpointDetector.cs @@ -1,4 +1,4 @@ -// +// // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // @@ -18,20 +18,20 @@ namespace Datadog.Trace.Debugger.SpanCodeOrigin; internal static class EndpointDetector { - private static readonly HashSet ControllerAttributes = + private static readonly HashSet AspNetCoreControllerAttributes = [ "Microsoft.AspNetCore.Mvc.ApiControllerAttribute", "Microsoft.AspNetCore.Mvc.ControllerAttribute", "Microsoft.AspNetCore.Mvc.RouteAttribute" ]; - private static readonly HashSet ControllerBaseNames = + private static readonly HashSet AspNetCoreControllerBaseNames = [ "Microsoft.AspNetCore.Mvc.Controller", "Microsoft.AspNetCore.Mvc.ControllerBase" ]; - private static readonly HashSet ActionAttributes = + private static readonly HashSet AspNetCoreActionAttributes = [ "Microsoft.AspNetCore.Mvc.HttpGetAttribute", "Microsoft.AspNetCore.Mvc.HttpPostAttribute", @@ -43,6 +43,25 @@ internal static class EndpointDetector "Microsoft.AspNetCore.Mvc.Routing.HttpMethodAttribute" ]; + private static readonly HashSet NetFrameworkControllerBaseNames = + [ + // MVC 4/5 + "System.Web.Mvc.Controller", + "System.Web.Mvc.ControllerBase", + + // Web API 2 + "System.Web.Http.ApiController" + ]; + + private static readonly HashSet NetFrameworkNonActionAttributes = + [ + // MVC 4/5 + "System.Web.Mvc.NonActionAttribute", + + // Web API 2 + "System.Web.Http.NonActionAttribute" + ]; + private static readonly HashSet SignalRHubBaseNames = [ "Microsoft.AspNetCore.SignalR.Hub", @@ -74,7 +93,9 @@ internal static ImmutableHashSet GetEndpointMethodTokens(DatadogMetadataRea bool isPageModel = false; bool isSignalRHub = false; bool isCompilerGeneratedType = false; - var isController = IsInheritFromTypesOrHasAttribute(typeDef, metadataReader, ControllerAttributes, ControllerBaseNames); + var isAspNetCoreController = IsInheritFromTypesOrHasAttribute(typeDef, metadataReader, AspNetCoreControllerAttributes, AspNetCoreControllerBaseNames); + var isNetFrameworkController = !isAspNetCoreController && IsInheritFromTypes(typeDef, metadataReader, NetFrameworkControllerBaseNames); + var isController = isAspNetCoreController || isNetFrameworkController; if (!isController) { isPageModel = IsInheritFromTypes(typeDef, metadataReader, PageModelBaseNames); @@ -101,7 +122,13 @@ internal static ImmutableHashSet GetEndpointMethodTokens(DatadogMetadataRea continue; } - if (isController && HasAttributeFromSet(methodDef.GetCustomAttributes(), metadataReader, ActionAttributes)) + if (isAspNetCoreController && HasAttributeFromSet(methodDef.GetCustomAttributes(), metadataReader, AspNetCoreActionAttributes)) + { + builder.Add(metadataReader.GetToken(methodHandle)); + continue; + } + + if (isNetFrameworkController && !HasAttributeFromSet(methodDef.GetCustomAttributes(), metadataReader, NetFrameworkNonActionAttributes)) { builder.Add(metadataReader.GetToken(methodHandle)); continue; @@ -141,7 +168,8 @@ private static bool IsValidMethod(MethodDefinition methodDef) { var attributes = methodDef.Attributes; return (attributes & MethodAttributes.Public) != 0 && - (attributes & MethodAttributes.Static) == 0; + (attributes & MethodAttributes.Static) == 0 && + (attributes & MethodAttributes.SpecialName) == 0; } private static bool IsInheritFromTypesOrHasAttribute(TypeDefinition typeDef, MetadataReader reader, HashSet attributesNames, HashSet baseTypeNames) diff --git a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AspNet/AspNetMvc5CodeOriginTests.cs b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AspNet/AspNetMvc5CodeOriginTests.cs new file mode 100644 index 000000000000..59dbb681016c --- /dev/null +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AspNet/AspNetMvc5CodeOriginTests.cs @@ -0,0 +1,114 @@ +// +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. +// + +#if NETFRAMEWORK + +using System; +using System.Collections.Generic; +using System.Net; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Datadog.Trace.Configuration; +using Datadog.Trace.ExtensionMethods; +using Datadog.Trace.TestHelpers; +using VerifyTests; +using VerifyXunit; +using Xunit; +using Xunit.Abstractions; +#pragma warning disable SA1402 + +namespace Datadog.Trace.ClrProfiler.IntegrationTests +{ + [UsesVerify] + public abstract class AspNetMvc5CodeOriginTests : TracingIntegrationTest, IClassFixture, IAsyncLifetime + { + private readonly IisFixture _iisFixture; + private readonly string _testName; + private readonly bool _classicMode; + + protected AspNetMvc5CodeOriginTests(IisFixture iisFixture, ITestOutputHelper output, bool classicMode) + : base("AspNetMvc5", @"test\test-applications\aspnet", output) + { + _iisFixture = iisFixture; + _iisFixture.ShutdownPath = "/home/shutdown"; + _classicMode = classicMode; + _testName = $"{nameof(AspNetMvc5CodeOriginTests)}.{(classicMode ? "Classic" : "Integrated")}"; + + SetServiceVersion("1.0.0"); + SetEnvironmentVariable(ConfigurationKeys.Debugger.CodeOriginForSpansEnabled, "true"); + } + + public override Result ValidateIntegrationSpan(MockSpan span, string metadataSchemaVersion) => + span.Name switch + { + "aspnet.request" => span.IsAspNet(metadataSchemaVersion), + "aspnet-mvc.request" => span.IsAspNetMvc(metadataSchemaVersion), + _ => Result.DefaultSuccess, + }; + + [SkippableFact] + [Trait("Category", "EndToEnd")] + [Trait("RunOnWindows", "True")] + [Trait("LoadFromGAC", "True")] + public async Task AddsCodeOriginToAspNetRequestSpan() + { + const string path = "/Home/Index"; + const int statusCode = 200; + + var spans = await GetWebServerSpans( + path: _iisFixture.VirtualApplicationPath + path, + agent: _iisFixture.Agent, + httpPort: _iisFixture.HttpPort, + expectedHttpStatusCode: HttpStatusCode.OK, + expectedSpanCount: 2); + + var sanitisedPath = VerifyHelper.SanitisePathsForVerify(path); + var settings = VerifyHelper.GetSpanVerifierSettings(sanitisedPath, statusCode); + + AddCodeOriginScrubbers(settings); + + await Verifier.Verify(spans, settings) + .UseFileName($"{_testName}.__path={sanitisedPath}_statusCode={statusCode}"); + } + + public Task InitializeAsync() => _iisFixture.TryStartIis(this, _classicMode ? IisAppType.AspNetClassic : IisAppType.AspNetIntegrated); + + public Task DisposeAsync() => Task.CompletedTask; + + private static void AddCodeOriginScrubbers(VerifySettings settings) + { + // File paths are machine dependent, and line/column numbers are sensitive to unrelated edits. + // We scrub the values but keep the tags present in the snapshot. + settings.AddRegexScrubber( + new Regex(@"_dd\.code_origin\.frames\.(\d+)\.file: .*", VerifyHelper.RegOptions), + "_dd.code_origin.frames.$1.file: "); + + settings.AddRegexScrubber( + new Regex(@"_dd\.code_origin\.frames\.(\d+)\.(line|column): \d+", VerifyHelper.RegOptions), + "_dd.code_origin.frames.$1.$2: 0"); + } + } + + [Collection("IisTests")] + public class AspNetMvc5CodeOriginTestsClassic : AspNetMvc5CodeOriginTests + { + public AspNetMvc5CodeOriginTestsClassic(IisFixture iisFixture, ITestOutputHelper output) + : base(iisFixture, output, classicMode: true) + { + } + } + + [Collection("IisTests")] + public class AspNetMvc5CodeOriginTestsIntegrated : AspNetMvc5CodeOriginTests + { + public AspNetMvc5CodeOriginTestsIntegrated(IisFixture iisFixture, ITestOutputHelper output) + : base(iisFixture, output, classicMode: false) + { + } + } +} + +#endif + diff --git a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AspNet/AspNetWebApi2CodeOriginTests.cs b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AspNet/AspNetWebApi2CodeOriginTests.cs new file mode 100644 index 000000000000..384bbba2ce45 --- /dev/null +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AspNet/AspNetWebApi2CodeOriginTests.cs @@ -0,0 +1,110 @@ +// +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. +// + +#if NETFRAMEWORK + +using System; +using System.Collections.Generic; +using System.Net; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Datadog.Trace.Configuration; +using Datadog.Trace.ExtensionMethods; +using Datadog.Trace.TestHelpers; +using VerifyXunit; +using Xunit; +using Xunit.Abstractions; + +namespace Datadog.Trace.ClrProfiler.IntegrationTests +{ + [Collection("IisTests")] + public class AspNetWebApi2CodeOriginTestsClassic : AspNetWebApi2CodeOriginTests + { + public AspNetWebApi2CodeOriginTestsClassic(IisFixture iisFixture, ITestOutputHelper output) + : base(iisFixture, output, classicMode: true) + { + } + } + + [Collection("IisTests")] + public class AspNetWebApi2CodeOriginTestsIntegrated : AspNetWebApi2CodeOriginTests + { + public AspNetWebApi2CodeOriginTestsIntegrated(IisFixture iisFixture, ITestOutputHelper output) + : base(iisFixture, output, classicMode: false) + { + } + } + + [UsesVerify] + public abstract class AspNetWebApi2CodeOriginTests : TracingIntegrationTest, IClassFixture, IAsyncLifetime + { + private readonly IisFixture _iisFixture; + private readonly string _testName; + private readonly bool _classicMode; + + protected AspNetWebApi2CodeOriginTests(IisFixture iisFixture, ITestOutputHelper output, bool classicMode) + : base("AspNetMvc5", @"test\test-applications\aspnet", output) + { + _iisFixture = iisFixture; + _iisFixture.ShutdownPath = "/home/shutdown"; + _classicMode = classicMode; + _testName = $"{nameof(AspNetWebApi2CodeOriginTests)}.{(classicMode ? "Classic" : "Integrated")}"; + + SetServiceVersion("1.0.0"); + SetEnvironmentVariable(ConfigurationKeys.Debugger.CodeOriginForSpansEnabled, "true"); + } + + public override Result ValidateIntegrationSpan(MockSpan span, string metadataSchemaVersion) => + span.Name switch + { + "aspnet.request" => span.IsAspNet(metadataSchemaVersion), + "aspnet-webapi.request" => span.IsAspNetWebApi2(metadataSchemaVersion), + _ => Result.DefaultSuccess, + }; + + [SkippableFact] + [Trait("Category", "EndToEnd")] + [Trait("RunOnWindows", "True")] + [Trait("LoadFromGAC", "True")] + public async Task AddsCodeOriginToAspNetRequestSpan() + { + const string path = "/api/environment"; + const int statusCode = 200; + + var spans = await GetWebServerSpans( + path: _iisFixture.VirtualApplicationPath + path, + agent: _iisFixture.Agent, + httpPort: _iisFixture.HttpPort, + expectedHttpStatusCode: HttpStatusCode.OK, + expectedSpanCount: 2); + + var sanitisedPath = VerifyHelper.SanitisePathsForVerify(path); + var settings = VerifyHelper.GetSpanVerifierSettings(sanitisedPath, statusCode); + + AddCodeOriginScrubbers(settings); + + await Verifier.Verify(spans, settings) + .UseFileName($"{_testName}.__path={sanitisedPath}_statusCode={statusCode}"); + } + + public Task InitializeAsync() => _iisFixture.TryStartIis(this, _classicMode ? IisAppType.AspNetClassic : IisAppType.AspNetIntegrated); + + public Task DisposeAsync() => Task.CompletedTask; + + private static void AddCodeOriginScrubbers(VerifySettings settings) + { + settings.AddRegexScrubber( + new Regex(@"_dd\.code_origin\.frames\.(\d+)\.file: .*", VerifyHelper.RegOptions), + "_dd.code_origin.frames.$1.file: "); + + settings.AddRegexScrubber( + new Regex(@"_dd\.code_origin\.frames\.(\d+)\.(line|column): \d+", VerifyHelper.RegOptions), + "_dd.code_origin.frames.$1.$2: 0"); + } + } +} + +#endif + diff --git a/tracer/test/Datadog.Trace.Tests/Debugger/EndpointDetectorTests.cs b/tracer/test/Datadog.Trace.Tests/Debugger/EndpointDetectorTests.cs index 8a28d0ba6400..eb9122f61726 100644 --- a/tracer/test/Datadog.Trace.Tests/Debugger/EndpointDetectorTests.cs +++ b/tracer/test/Datadog.Trace.Tests/Debugger/EndpointDetectorTests.cs @@ -102,6 +102,16 @@ public void GetEndpointMethodTokens_FindsAllEndpoints() isExpectedEndpoint = true; } + // .NET Framework MVC/WebApi2 controllers (convention-based) + else if (type.Name == "NetFxMvcController" && method.Name == "Index") + { + isExpectedEndpoint = true; + } + else if (type.Name == "NetFxWebApiController" && method.Name == "Get") + { + isExpectedEndpoint = true; + } + // PageModel handlers else if (type.Name == "TestPageModel" && method.Name is "OnGet" or "OnGetAsync" or "OnPost" or "OnPostAsync" or "OnPut" or "OnPutAsync" or "OnDelete" or "OnDeleteAsync" or "OnHead" or "OnHeadAsync" or "OnPatch" or "OnPatchAsync" or "OnOptions" or "OnOptionsAsync" && @@ -191,6 +201,12 @@ public void GetEndpointMethodTokens_DoesNotDetectNonEndpoints() shouldNotBeEndpoint = true; } + // .NET Framework MVC/WebApi2 non-actions + else if (type.Name is "NetFxMvcController" or "NetFxWebApiController" && method.Name == "Helper") + { + shouldNotBeEndpoint = true; + } + // PageModel methods with NonHandler attribute else if (type.Name == "TestPageModel" && method.Name == "OnGetWithNonHandlerAttribute") { @@ -414,6 +430,24 @@ public static void MapPost(this object app, string pattern, Delegate handler) { } } +namespace System.Web.Mvc +{ + [AttributeUsage(AttributeTargets.Method)] + public class NonActionAttribute : Attribute { } + + public class ControllerBase { } + + public class Controller : ControllerBase { } +} + +namespace System.Web.Http +{ + [AttributeUsage(AttributeTargets.Method)] + public class NonActionAttribute : Attribute { } + + public class ApiController { } +} + namespace EndpointDetectorTestNamespace { // Controller with ControllerAttribute @@ -577,6 +611,28 @@ public static void UseHandlers(object app, RequestHandler handler1, RequestHandl // This empty method ensures the delegates are used } } + + // .NET Framework MVC controller + public class NetFxMvcController : System.Web.Mvc.Controller + { + public object Index() => null; + + [System.Web.Mvc.NonAction] + public object Helper() => null; + + private object PrivateMethod() => null; + + public static object StaticMethod() => null; + } + + // .NET Framework Web API 2 controller + public class NetFxWebApiController : System.Web.Http.ApiController + { + public object Get() => null; + + [System.Web.Http.NonAction] + public object Helper() => null; + } }"); } } From 247f65dd7a28ad029a2b5aa2069e29e161949c1f Mon Sep 17 00:00:00 2001 From: dudik <8845578+dudikeleti@users.noreply.github.com> Date: Tue, 27 Jan 2026 14:57:33 +0100 Subject: [PATCH 2/7] improve debug logs --- ...rActionInvoker_InvokeAction_Integration.cs | 30 +++++++---------- ...tionDescriptor_ExecuteAsync_Integration.cs | 32 +++++++------------ 2 files changed, 23 insertions(+), 39 deletions(-) diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ControllerActionInvoker_InvokeAction_Integration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ControllerActionInvoker_InvokeAction_Integration.cs index 353cdadf35ca..5c1852c0ea19 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ControllerActionInvoker_InvokeAction_Integration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ControllerActionInvoker_InvokeAction_Integration.cs @@ -92,44 +92,36 @@ internal static CallTargetState OnMethodBegin(TActionDescriptor actionDescriptor, SpanCodeOrigin codeOrigin) { - if (SharedItems.TryPeekScope(HttpContext.Current, AspNetMvcIntegration.HttpContextKey) is not { Root.Span: { } rootSpan }) - { - if (Log.IsEnabled(LogEventLevel.Debug)) - { - Log.Debug("Code origin is enabled for spans but MVC scope was not found in HttpContext."); - } - - return; - } - if (actionDescriptor is null) { - if (Log.IsEnabled(LogEventLevel.Debug)) - { - Log.Debug("Code origin is enabled for spans but MVC ActionDescriptor instance was null."); - } - return; } - if (!actionDescriptor.TryDuckCast(out var reflected) || reflected.MethodInfo is not { } actionMethod) + var httpContext = HttpContext.Current; + if (SharedItems.TryPeekScope(httpContext, AspNetMvcIntegration.HttpContextKey) is not { Root.Span: { } rootSpan }) { if (Log.IsEnabled(LogEventLevel.Debug)) { Log.Debug( - "Code origin is enabled for spans but could not extract MVC action MethodInfo from ActionDescriptor type {ActionDescriptorType}.", + "Code origin is enabled but scope was not found in HttpContext (key: {HttpContextKey}, httpContextNull: {HttpContextIsNull}, itemsCount: {HttpContextItemsCount}, actionDescriptorType: {ActionDescriptorType}).", + AspNetMvcIntegration.HttpContextKey, + httpContext is null, + httpContext?.Items?.Count ?? 0, actionDescriptor.GetType()); } return; } - if (actionMethod.DeclaringType is not { } actionType) + if (!actionDescriptor.TryDuckCast(out var reflected) + || reflected.MethodInfo is not { } actionMethod + || actionMethod.DeclaringType is not { } actionType) { if (Log.IsEnabled(LogEventLevel.Debug)) { Log.Debug( - "Code origin is enabled for spans but extracted MVC action MethodInfo has no DeclaringType. Method: {Method}.", + "Code origin is enabled but could not extract action from ActionDescriptor type {ActionDescriptorType} or action MethodInfo has no DeclaringType. Method: {Method}.", + actionDescriptor.GetType(), actionMethod); } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ReflectedHttpActionDescriptor_ExecuteAsync_Integration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ReflectedHttpActionDescriptor_ExecuteAsync_Integration.cs index 1a02b8a5ecab..321dcaad9b56 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ReflectedHttpActionDescriptor_ExecuteAsync_Integration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ReflectedHttpActionDescriptor_ExecuteAsync_Integration.cs @@ -91,44 +91,36 @@ internal static CallTargetState OnMethodBegin(TTarget inst private static void AddSpanCodeOrigin(TTarget instance, SpanCodeOrigin codeOrigin) { - if (SharedItems.TryPeekScope(HttpContext.Current, AspNetWebApi2Integration.HttpContextKey) is not { Root.Span: { } rootSpan }) + if (instance == null) { - if (Log.IsEnabled(LogEventLevel.Debug)) - { - Log.Debug("Code origin is enabled for spans but WebApi2 scope was not found in HttpContext."); - } - - return; - } - - if (instance is null) - { - if (Log.IsEnabled(LogEventLevel.Debug)) - { - Log.Debug("Code origin is enabled for spans but ReflectedHttpActionDescriptor instance was null."); - } - return; } - if (!instance.TryDuckCast(out var reflected) || reflected.MethodInfo is not { } actionMethod) + var httpContext = HttpContext.Current; + if (SharedItems.TryPeekScope(httpContext, AspNetWebApi2Integration.HttpContextKey) is not { Root.Span: { } rootSpan }) { if (Log.IsEnabled(LogEventLevel.Debug)) { Log.Debug( - "Code origin is enabled for spans but could not extract WebApi2 action MethodInfo from ReflectedHttpActionDescriptor type {ActionDescriptorType}.", + "Code origin is enabled but scope was not found in HttpContext (key: {HttpContextKey}, httpContextNull: {HttpContextIsNull}, itemsCount: {HttpContextItemsCount}, actionDescriptorType: {ActionDescriptorType}).", + AspNetWebApi2Integration.HttpContextKey, + httpContext is null, + httpContext?.Items?.Count ?? 0, instance.GetType()); } return; } - if (actionMethod.DeclaringType is not { } actionType) + if (!instance.TryDuckCast(out var reflected) + || reflected.MethodInfo is not { } actionMethod + || actionMethod.DeclaringType is not { } actionType) { if (Log.IsEnabled(LogEventLevel.Debug)) { Log.Debug( - "Code origin is enabled for spans but extracted WebApi2 action MethodInfo has no DeclaringType. Method: {Method}.", + "Code origin is enabled but could not extract action from HttpActionDescriptor type {ActionDescriptorType} or action has no DeclaringType. Method: {Method}.", + instance.GetType(), actionMethod); } From 7a97828034ca36c488296a255839ed1b5bfea913 Mon Sep 17 00:00:00 2001 From: dudik <8845578+dudikeleti@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:33:43 +0100 Subject: [PATCH 3/7] fix build --- .../ControllerActionInvoker_InvokeAction_Integration.cs | 5 ++--- ...ReflectedHttpActionDescriptor_ExecuteAsync_Integration.cs | 5 ++--- .../AspNet/AspNetWebApi2CodeOriginTests.cs | 3 +++ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ControllerActionInvoker_InvokeAction_Integration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ControllerActionInvoker_InvokeAction_Integration.cs index 5c1852c0ea19..e7d188c9ede5 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ControllerActionInvoker_InvokeAction_Integration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ControllerActionInvoker_InvokeAction_Integration.cs @@ -120,9 +120,8 @@ private static void AddSpanCodeOrigin(TActionDescriptor actio if (Log.IsEnabled(LogEventLevel.Debug)) { Log.Debug( - "Code origin is enabled but could not extract action from ActionDescriptor type {ActionDescriptorType} or action MethodInfo has no DeclaringType. Method: {Method}.", - actionDescriptor.GetType(), - actionMethod); + "Code origin is enabled but could not extract action from ActionDescriptor type {ActionDescriptorType} or action MethodInfo has no DeclaringType.", + actionDescriptor.GetType()); } return; diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ReflectedHttpActionDescriptor_ExecuteAsync_Integration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ReflectedHttpActionDescriptor_ExecuteAsync_Integration.cs index 321dcaad9b56..b753febbd026 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ReflectedHttpActionDescriptor_ExecuteAsync_Integration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ReflectedHttpActionDescriptor_ExecuteAsync_Integration.cs @@ -119,9 +119,8 @@ private static void AddSpanCodeOrigin(TTarget instance, SpanCodeOrigin if (Log.IsEnabled(LogEventLevel.Debug)) { Log.Debug( - "Code origin is enabled but could not extract action from HttpActionDescriptor type {ActionDescriptorType} or action has no DeclaringType. Method: {Method}.", - instance.GetType(), - actionMethod); + "Code origin is enabled but could not extract action from HttpActionDescriptor type {ActionDescriptorType} or action has no DeclaringType.", + instance.GetType()); } return; diff --git a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AspNet/AspNetWebApi2CodeOriginTests.cs b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AspNet/AspNetWebApi2CodeOriginTests.cs index 384bbba2ce45..e9528be983d6 100644 --- a/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AspNet/AspNetWebApi2CodeOriginTests.cs +++ b/tracer/test/Datadog.Trace.ClrProfiler.IntegrationTests/AspNet/AspNetWebApi2CodeOriginTests.cs @@ -4,6 +4,8 @@ // #if NETFRAMEWORK +#pragma warning disable SA1402 // File may only contain a single class +#pragma warning disable SA1649 // File name must match first type name using System; using System.Collections.Generic; @@ -13,6 +15,7 @@ using Datadog.Trace.Configuration; using Datadog.Trace.ExtensionMethods; using Datadog.Trace.TestHelpers; +using VerifyTests; using VerifyXunit; using Xunit; using Xunit.Abstractions; From 1dba03f0b349eb65f2403c8014c342d6689c5c5b Mon Sep 17 00:00:00 2001 From: dudik <8845578+dudikeleti@users.noreply.github.com> Date: Mon, 9 Feb 2026 23:58:44 +0100 Subject: [PATCH 4/7] Add InstrumentationCategory.Tracing --- .../AspNet/ControllerActionInvoker_InvokeAction_Integration.cs | 2 +- .../ReflectedHttpActionDescriptor_ExecuteAsync_Integration.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ControllerActionInvoker_InvokeAction_Integration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ControllerActionInvoker_InvokeAction_Integration.cs index e7d188c9ede5..e611bbdc1439 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ControllerActionInvoker_InvokeAction_Integration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ControllerActionInvoker_InvokeAction_Integration.cs @@ -35,7 +35,7 @@ namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet MinimumVersion = "4", MaximumVersion = "5", IntegrationName = IntegrationName, - InstrumentationCategory = InstrumentationCategory.AppSec | InstrumentationCategory.Iast)] + InstrumentationCategory = InstrumentationCategory.Tracing | InstrumentationCategory.AppSec | InstrumentationCategory.Iast)] // ReSharper disable once InconsistentNaming [Browsable(false)] [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ReflectedHttpActionDescriptor_ExecuteAsync_Integration.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ReflectedHttpActionDescriptor_ExecuteAsync_Integration.cs index b753febbd026..43e3d8d4ba1e 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ReflectedHttpActionDescriptor_ExecuteAsync_Integration.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/AspNet/ReflectedHttpActionDescriptor_ExecuteAsync_Integration.cs @@ -36,7 +36,7 @@ namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet MinimumVersion = "5.1", MaximumVersion = "5", IntegrationName = IntegrationName, - InstrumentationCategory = InstrumentationCategory.AppSec | InstrumentationCategory.Iast)] + InstrumentationCategory = InstrumentationCategory.Tracing | InstrumentationCategory.AppSec | InstrumentationCategory.Iast)] // ReSharper disable once InconsistentNaming [Browsable(false)] [EditorBrowsable(EditorBrowsableState.Never)] From 9ae29fbf235ac26900485df2662d18ed2588462a Mon Sep 17 00:00:00 2001 From: dudik <8845578+dudikeleti@users.noreply.github.com> Date: Mon, 9 Feb 2026 23:59:50 +0100 Subject: [PATCH 5/7] Add code origin snapshots --- ...th=_Home_Index_statusCode=200.verified.txt | 61 +++++++++++++++++++ ...th=_Home_Index_statusCode=200.verified.txt | 61 +++++++++++++++++++ ...pi_environment_statusCode=200.verified.txt | 59 ++++++++++++++++++ ...pi_environment_statusCode=200.verified.txt | 59 ++++++++++++++++++ 4 files changed, 240 insertions(+) create mode 100644 tracer/test/snapshots/AspNetMvc5CodeOriginTests.Classic.__path=_Home_Index_statusCode=200.verified.txt create mode 100644 tracer/test/snapshots/AspNetMvc5CodeOriginTests.Integrated.__path=_Home_Index_statusCode=200.verified.txt create mode 100644 tracer/test/snapshots/AspNetWebApi2CodeOriginTests.Classic.__path=_api_environment_statusCode=200.verified.txt create mode 100644 tracer/test/snapshots/AspNetWebApi2CodeOriginTests.Integrated.__path=_api_environment_statusCode=200.verified.txt diff --git a/tracer/test/snapshots/AspNetMvc5CodeOriginTests.Classic.__path=_Home_Index_statusCode=200.verified.txt b/tracer/test/snapshots/AspNetMvc5CodeOriginTests.Classic.__path=_Home_Index_statusCode=200.verified.txt new file mode 100644 index 000000000000..c8131e6839db --- /dev/null +++ b/tracer/test/snapshots/AspNetMvc5CodeOriginTests.Classic.__path=_Home_Index_statusCode=200.verified.txt @@ -0,0 +1,61 @@ +[ + { + TraceId: Id_1, + SpanId: Id_2, + Name: aspnet-mvc.request, + Resource: GET /home/index, + Service: sample, + Type: web, + ParentId: Id_3, + Tags: { + aspnet.action: index, + aspnet.controller: home, + aspnet.route: {controller}/{action}/{id}, + component: aspnet, + env: integration_tests, + http.method: GET, + http.request.headers.host: localhost:00000, + http.status_code: 200, + http.url: http://localhost:00000/Home/Index, + http.useragent: testhelper, + language: dotnet, + span.kind: server, + version: 1.0.0 + } + }, + { + TraceId: Id_1, + SpanId: Id_3, + Name: aspnet.request, + Resource: GET /home/index, + Service: sample, + Type: web, + Tags: { + component: aspnet, + env: integration_tests, + http.method: GET, + http.request.headers.host: localhost:00000, + http.route: {controller}/{action}/{id}, + http.status_code: 200, + http.url: http://localhost:00000/Home/Index, + http.useragent: testhelper, + language: dotnet, + runtime-id: Guid_1, + span.kind: server, + version: 1.0.0, + _dd.code_origin.frames.0.column: 0, + _dd.code_origin.frames.0.file: + _dd.code_origin.frames.0.index: 0, + _dd.code_origin.frames.0.line: 0, + _dd.code_origin.frames.0.method: Index, + _dd.code_origin.frames.0.type: Samples.AspNetMvc5.Controllers.HomeController, + _dd.code_origin.type: entry + }, + Metrics: { + process_id: 0, + _dd.top_level: 1.0, + _dd.tracer_kr: 1.0, + _sampling_priority_v1: 1.0 + } + } +] \ No newline at end of file diff --git a/tracer/test/snapshots/AspNetMvc5CodeOriginTests.Integrated.__path=_Home_Index_statusCode=200.verified.txt b/tracer/test/snapshots/AspNetMvc5CodeOriginTests.Integrated.__path=_Home_Index_statusCode=200.verified.txt new file mode 100644 index 000000000000..c8131e6839db --- /dev/null +++ b/tracer/test/snapshots/AspNetMvc5CodeOriginTests.Integrated.__path=_Home_Index_statusCode=200.verified.txt @@ -0,0 +1,61 @@ +[ + { + TraceId: Id_1, + SpanId: Id_2, + Name: aspnet-mvc.request, + Resource: GET /home/index, + Service: sample, + Type: web, + ParentId: Id_3, + Tags: { + aspnet.action: index, + aspnet.controller: home, + aspnet.route: {controller}/{action}/{id}, + component: aspnet, + env: integration_tests, + http.method: GET, + http.request.headers.host: localhost:00000, + http.status_code: 200, + http.url: http://localhost:00000/Home/Index, + http.useragent: testhelper, + language: dotnet, + span.kind: server, + version: 1.0.0 + } + }, + { + TraceId: Id_1, + SpanId: Id_3, + Name: aspnet.request, + Resource: GET /home/index, + Service: sample, + Type: web, + Tags: { + component: aspnet, + env: integration_tests, + http.method: GET, + http.request.headers.host: localhost:00000, + http.route: {controller}/{action}/{id}, + http.status_code: 200, + http.url: http://localhost:00000/Home/Index, + http.useragent: testhelper, + language: dotnet, + runtime-id: Guid_1, + span.kind: server, + version: 1.0.0, + _dd.code_origin.frames.0.column: 0, + _dd.code_origin.frames.0.file: + _dd.code_origin.frames.0.index: 0, + _dd.code_origin.frames.0.line: 0, + _dd.code_origin.frames.0.method: Index, + _dd.code_origin.frames.0.type: Samples.AspNetMvc5.Controllers.HomeController, + _dd.code_origin.type: entry + }, + Metrics: { + process_id: 0, + _dd.top_level: 1.0, + _dd.tracer_kr: 1.0, + _sampling_priority_v1: 1.0 + } + } +] \ No newline at end of file diff --git a/tracer/test/snapshots/AspNetWebApi2CodeOriginTests.Classic.__path=_api_environment_statusCode=200.verified.txt b/tracer/test/snapshots/AspNetWebApi2CodeOriginTests.Classic.__path=_api_environment_statusCode=200.verified.txt new file mode 100644 index 000000000000..940a619c1ad3 --- /dev/null +++ b/tracer/test/snapshots/AspNetWebApi2CodeOriginTests.Classic.__path=_api_environment_statusCode=200.verified.txt @@ -0,0 +1,59 @@ +[ + { + TraceId: Id_1, + SpanId: Id_2, + Name: aspnet-webapi.request, + Resource: GET /api/environment, + Service: sample, + Type: web, + ParentId: Id_3, + Tags: { + aspnet.route: api/environment, + component: aspnet, + env: integration_tests, + http.method: GET, + http.request.headers.host: localhost:00000, + http.status_code: 200, + http.url: http://localhost:00000/api/environment, + http.useragent: testhelper, + language: dotnet, + span.kind: server, + version: 1.0.0 + } + }, + { + TraceId: Id_1, + SpanId: Id_3, + Name: aspnet.request, + Resource: GET /api/environment, + Service: sample, + Type: web, + Tags: { + component: aspnet, + env: integration_tests, + http.method: GET, + http.request.headers.host: localhost:00000, + http.route: api/environment, + http.status_code: 200, + http.url: http://localhost:00000/api/environment, + http.useragent: testhelper, + language: dotnet, + runtime-id: Guid_1, + span.kind: server, + version: 1.0.0, + _dd.code_origin.frames.0.column: 0, + _dd.code_origin.frames.0.file: + _dd.code_origin.frames.0.index: 0, + _dd.code_origin.frames.0.line: 0, + _dd.code_origin.frames.0.method: Environment, + _dd.code_origin.frames.0.type: Samples.AspNetMvc5.Controllers.ApiController, + _dd.code_origin.type: entry + }, + Metrics: { + process_id: 0, + _dd.top_level: 1.0, + _dd.tracer_kr: 1.0, + _sampling_priority_v1: 1.0 + } + } +] \ No newline at end of file diff --git a/tracer/test/snapshots/AspNetWebApi2CodeOriginTests.Integrated.__path=_api_environment_statusCode=200.verified.txt b/tracer/test/snapshots/AspNetWebApi2CodeOriginTests.Integrated.__path=_api_environment_statusCode=200.verified.txt new file mode 100644 index 000000000000..940a619c1ad3 --- /dev/null +++ b/tracer/test/snapshots/AspNetWebApi2CodeOriginTests.Integrated.__path=_api_environment_statusCode=200.verified.txt @@ -0,0 +1,59 @@ +[ + { + TraceId: Id_1, + SpanId: Id_2, + Name: aspnet-webapi.request, + Resource: GET /api/environment, + Service: sample, + Type: web, + ParentId: Id_3, + Tags: { + aspnet.route: api/environment, + component: aspnet, + env: integration_tests, + http.method: GET, + http.request.headers.host: localhost:00000, + http.status_code: 200, + http.url: http://localhost:00000/api/environment, + http.useragent: testhelper, + language: dotnet, + span.kind: server, + version: 1.0.0 + } + }, + { + TraceId: Id_1, + SpanId: Id_3, + Name: aspnet.request, + Resource: GET /api/environment, + Service: sample, + Type: web, + Tags: { + component: aspnet, + env: integration_tests, + http.method: GET, + http.request.headers.host: localhost:00000, + http.route: api/environment, + http.status_code: 200, + http.url: http://localhost:00000/api/environment, + http.useragent: testhelper, + language: dotnet, + runtime-id: Guid_1, + span.kind: server, + version: 1.0.0, + _dd.code_origin.frames.0.column: 0, + _dd.code_origin.frames.0.file: + _dd.code_origin.frames.0.index: 0, + _dd.code_origin.frames.0.line: 0, + _dd.code_origin.frames.0.method: Environment, + _dd.code_origin.frames.0.type: Samples.AspNetMvc5.Controllers.ApiController, + _dd.code_origin.type: entry + }, + Metrics: { + process_id: 0, + _dd.top_level: 1.0, + _dd.tracer_kr: 1.0, + _sampling_priority_v1: 1.0 + } + } +] \ No newline at end of file From bd252b8ba4b5c9a2c6b8476957de471204ecd174 Mon Sep 17 00:00:00 2001 From: dudik <8845578+dudikeleti@users.noreply.github.com> Date: Tue, 10 Feb 2026 18:40:03 +0100 Subject: [PATCH 6/7] fix security integration test --- .../Datadog.Trace.Security.IntegrationTests/AspNetWebApi.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tracer/test/Datadog.Trace.Security.IntegrationTests/AspNetWebApi.cs b/tracer/test/Datadog.Trace.Security.IntegrationTests/AspNetWebApi.cs index 7aaca72b8f8a..739038c21c17 100644 --- a/tracer/test/Datadog.Trace.Security.IntegrationTests/AspNetWebApi.cs +++ b/tracer/test/Datadog.Trace.Security.IntegrationTests/AspNetWebApi.cs @@ -105,7 +105,10 @@ public async Task TestBlockedRequest(string test) var settings = VerifyHelper.GetSpanVerifierSettings(test); FilterConnectionHeader(settings); - await TestAppSecRequestWithVerifyAsync(_iisFixture.Agent, url, null, 5, 1, settings, userAgent: "Hello/V"); + // When AppSec is enabled, the request is blocked early and only the ASP.NET root span is created. + // When AppSec is disabled, the request completes normally and we also get the Web API span. + var spansPerRequest = SecurityEnabled ? 1 : 2; + await TestAppSecRequestWithVerifyAsync(_iisFixture.Agent, url, null, 5, spansPerRequest, settings, userAgent: "Hello/V"); } [Trait("Category", "EndToEnd")] From 22e8dbd040b332c0cae976e0db36241a0bfc9191 Mon Sep 17 00:00:00 2001 From: dudik <8845578+dudikeleti@users.noreply.github.com> Date: Wed, 11 Feb 2026 12:42:20 +0100 Subject: [PATCH 7/7] update generated source --- tracer/build/supported_calltargets.g.json | 4 ++-- .../Generated/generated_calltargets.g.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tracer/build/supported_calltargets.g.json b/tracer/build/supported_calltargets.g.json index e88f055d8947..cde0f5b56113 100644 --- a/tracer/build/supported_calltargets.g.json +++ b/tracer/build/supported_calltargets.g.json @@ -1569,7 +1569,7 @@ "InstrumentationTypeName": "Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet.ControllerActionInvoker_InvokeAction_Integration", "IntegrationKind": 0, "IsAdoNetIntegration": false, - "InstrumentationCategory": 6 + "InstrumentationCategory": 7 }, { "IntegrationName": "AspNetWebApi2", @@ -1620,7 +1620,7 @@ "InstrumentationTypeName": "Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet.ReflectedHttpActionDescriptor_ExecuteAsync_Integration", "IntegrationKind": 0, "IsAdoNetIntegration": false, - "InstrumentationCategory": 6 + "InstrumentationCategory": 7 }, { "IntegrationName": "AspNetWebApi2", diff --git a/tracer/src/Datadog.Tracer.Native/Generated/generated_calltargets.g.cpp b/tracer/src/Datadog.Tracer.Native/Generated/generated_calltargets.g.cpp index dff661caa48c..6cbb5e6e48fb 100644 --- a/tracer/src/Datadog.Tracer.Native/Generated/generated_calltargets.g.cpp +++ b/tracer/src/Datadog.Tracer.Native/Generated/generated_calltargets.g.cpp @@ -483,9 +483,9 @@ std::vector callTargets = #if _WIN32 {(WCHAR*)WStr("System.Web.Mvc"),(WCHAR*)WStr("System.Web.Mvc.Async.AsyncControllerActionInvoker"),(WCHAR*)WStr("BeginInvokeAction"),sig145,5,4,0,0,5,65535,65535,assemblyName,(WCHAR*)WStr("Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet.AsyncControllerActionInvoker_BeginInvokeAction_Integration"),CallTargetKind::Default,1,1}, {(WCHAR*)WStr("System.Web.Mvc"),(WCHAR*)WStr("System.Web.Mvc.Async.AsyncControllerActionInvoker"),(WCHAR*)WStr("EndInvokeAction"),sig124,2,4,0,0,5,65535,65535,assemblyName,(WCHAR*)WStr("Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet.AsyncControllerActionInvoker_EndInvokeAction_Integration"),CallTargetKind::Default,1,1}, -{(WCHAR*)WStr("System.Web.Mvc"),(WCHAR*)WStr("System.Web.Mvc.ControllerActionInvoker"),(WCHAR*)WStr("InvokeActionMethod"),sig394,4,4,0,0,5,65535,65535,assemblyName,(WCHAR*)WStr("Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet.ControllerActionInvoker_InvokeAction_Integration"),CallTargetKind::Default,6,1}, +{(WCHAR*)WStr("System.Web.Mvc"),(WCHAR*)WStr("System.Web.Mvc.ControllerActionInvoker"),(WCHAR*)WStr("InvokeActionMethod"),sig394,4,4,0,0,5,65535,65535,assemblyName,(WCHAR*)WStr("Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet.ControllerActionInvoker_InvokeAction_Integration"),CallTargetKind::Default,7,1}, {(WCHAR*)WStr("System.Web.Http"),(WCHAR*)WStr("System.Web.Http.ApiController"),(WCHAR*)WStr("ExecuteAsync"),sig299,3,5,1,0,5,65535,65535,assemblyName,(WCHAR*)WStr("Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet.ApiController_ExecuteAsync_Integration"),CallTargetKind::Default,1,1}, -{(WCHAR*)WStr("System.Web.Http"),(WCHAR*)WStr("System.Web.Http.Controllers.ReflectedHttpActionDescriptor"),(WCHAR*)WStr("ExecuteAsync"),sig298,4,5,1,0,5,65535,65535,assemblyName,(WCHAR*)WStr("Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet.ReflectedHttpActionDescriptor_ExecuteAsync_Integration"),CallTargetKind::Default,6,1}, +{(WCHAR*)WStr("System.Web.Http"),(WCHAR*)WStr("System.Web.Http.Controllers.ReflectedHttpActionDescriptor"),(WCHAR*)WStr("ExecuteAsync"),sig298,4,5,1,0,5,65535,65535,assemblyName,(WCHAR*)WStr("Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet.ReflectedHttpActionDescriptor_ExecuteAsync_Integration"),CallTargetKind::Default,7,1}, {(WCHAR*)WStr("System.Web.Http"),(WCHAR*)WStr("System.Web.Http.ExceptionHandling.ExceptionHandlerExtensions"),(WCHAR*)WStr("HandleAsync"),sig300,4,5,1,0,5,65535,65535,assemblyName,(WCHAR*)WStr("Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet.ExceptionHandlerExtensions_HandleAsync_Integration"),CallTargetKind::Default,1,1}, #endif {(WCHAR*)WStr("AWSSDK.DynamoDBv2"),(WCHAR*)WStr("Amazon.DynamoDBv2.AmazonDynamoDBClient"),(WCHAR*)WStr("BatchGetItem"),sig008,2,3,0,0,4,65535,65535,assemblyName,(WCHAR*)WStr("Datadog.Trace.ClrProfiler.AutoInstrumentation.AWS.DynamoDb.BatchGetItemIntegration"),CallTargetKind::Default,1,15},