-
Notifications
You must be signed in to change notification settings - Fork 159
Support child actions in aspnet #2139
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
Changes from all commits
9bdf0c4
e9584e5
94c729b
6230480
59b7d25
b29e0ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| // <copyright file="SharedItems.cs" company="Datadog"> | ||
| // 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. | ||
| // </copyright> | ||
|
|
||
| #if NETFRAMEWORK | ||
|
|
||
| using System.Collections.Generic; | ||
| using System.Web; | ||
|
|
||
| namespace Datadog.Trace.AspNet | ||
| { | ||
| internal static class SharedItems | ||
| { | ||
| public const string HttpContextPropagatedResourceNameKey = "__Datadog.Trace.ClrProfiler.Managed.AspNetMvcIntegration-aspnet.resourcename"; | ||
|
|
||
| internal static void PushScope(HttpContext context, string key, Scope item) | ||
| { | ||
| if (context == null) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| // Storing only the scope by default to avoid allocating a stack if no inner calls are done | ||
| var existingItem = context.Items[key]; | ||
| if (existingItem is null) | ||
| { | ||
| context.Items[key] = item; | ||
| } | ||
| else if (existingItem is Stack<Scope> stack) | ||
| { | ||
| stack.Push(item); | ||
| } | ||
| else if (existingItem is Scope previousScope) | ||
| { | ||
| var newStack = new Stack<Scope>(); | ||
| newStack.Push(previousScope); | ||
| newStack.Push(item); | ||
| context.Items[key] = newStack; | ||
| } | ||
| } | ||
|
|
||
| internal static Scope TryPopScope(HttpContext context, string key) | ||
| { | ||
| var item = context?.Items[key]; | ||
| if (item is Scope storedScope) | ||
| { | ||
| return storedScope; | ||
| } | ||
| else if (item is Stack<Scope> stack && stack.Count > 0) | ||
| { | ||
| return stack.Pop(); | ||
| } | ||
|
|
||
| return default(Scope); | ||
| } | ||
| } | ||
| } | ||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,6 +25,7 @@ internal static class AspNetMvcIntegration | |
| internal const string HttpContextKey = "__Datadog.Trace.ClrProfiler.Integrations.AspNetMvcIntegration"; | ||
|
|
||
| private const string OperationName = "aspnet-mvc.request"; | ||
| private const string ChildActionOperationName = "aspnet-mvc.request.child-action"; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does not appear to be used.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch, question is whether:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for catching that Robert. And indeed, we had dedided to use it Andrew. |
||
|
|
||
| private const string RouteCollectionRouteTypeName = "System.Web.Mvc.Routing.RouteCollectionRoute"; | ||
|
|
||
|
|
@@ -43,7 +44,6 @@ internal static Scope CreateScope(ControllerContextStruct controllerContext) | |
| try | ||
| { | ||
| var httpContext = controllerContext.HttpContext; | ||
|
|
||
| if (httpContext == null) | ||
| { | ||
| return null; | ||
|
|
@@ -63,6 +63,14 @@ internal static Scope CreateScope(ControllerContextStruct controllerContext) | |
| Route route = routeData?.Route as Route; | ||
| RouteValueDictionary routeValues = routeData?.Values; | ||
| bool wasAttributeRouted = false; | ||
| bool isChildAction = controllerContext.ParentActionViewContext.RouteData?.Values["controller"] is not null; | ||
|
|
||
| if (isChildAction && newResourceNamesEnabled) | ||
| { | ||
| // For child actions, we want to stick to what was requested in the http request. | ||
| // And the child action being a child, then we have already computed the resourcename. | ||
| resourceName = httpContext.Items[SharedItems.HttpContextPropagatedResourceNameKey] as string; | ||
| } | ||
|
|
||
| if (route == null && routeData?.Route.GetType().FullName == RouteCollectionRouteTypeName) | ||
| { | ||
|
|
@@ -155,7 +163,7 @@ internal static Scope CreateScope(ControllerContextStruct controllerContext) | |
| } | ||
|
|
||
| var tags = new AspNetTags(); | ||
| scope = Tracer.Instance.StartActiveInternal(OperationName, propagatedContext, tags: tags); | ||
| scope = Tracer.Instance.StartActiveInternal(isChildAction ? ChildActionOperationName : OperationName, propagatedContext, tags: tags); | ||
| span = scope.Span; | ||
|
|
||
| span.DecorateWebServerSpan( | ||
|
|
@@ -173,10 +181,10 @@ internal static Scope CreateScope(ControllerContextStruct controllerContext) | |
|
|
||
| tags.SetAnalyticsSampleRate(IntegrationId, tracer.Settings, enabledWithGlobalSetting: true); | ||
|
|
||
| if (newResourceNamesEnabled) | ||
| if (newResourceNamesEnabled && string.IsNullOrEmpty(httpContext.Items[SharedItems.HttpContextPropagatedResourceNameKey] as string)) | ||
| { | ||
| // set the resource name in the HttpContext so TracingHttpModule can update root span | ||
| httpContext.Items[SharedConstants.HttpContextPropagatedResourceNameKey] = resourceName; | ||
| httpContext.Items[SharedItems.HttpContextPropagatedResourceNameKey] = resourceName; | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -189,5 +197,4 @@ internal static Scope CreateScope(ControllerContextStruct controllerContext) | |
| } | ||
| } | ||
| } | ||
|
|
||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -157,7 +157,7 @@ internal static void UpdateSpan(IHttpControllerContext controllerContext, Span s | |
| var httpContext = System.Web.HttpContext.Current; | ||
| if (httpContext is not null) | ||
| { | ||
| httpContext.Items[SharedConstants.HttpContextPropagatedResourceNameKey] = resourceName; | ||
| httpContext.Items[SharedItems.HttpContextPropagatedResourceNameKey] = resourceName; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to check if the resource name has already been set? |
||
| } | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| // <copyright file="ViewContextStruct.cs" company="Datadog"> | ||
| // 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. | ||
| // </copyright> | ||
|
|
||
| #if NETFRAMEWORK | ||
| using System.ComponentModel; | ||
| using System.Web.Routing; | ||
| using Datadog.Trace.DuckTyping; | ||
|
|
||
| namespace Datadog.Trace.ClrProfiler.AutoInstrumentation.AspNet | ||
| { | ||
| /// <summary> | ||
| /// ControllerContext struct copy target for ducktyping | ||
| /// </summary> | ||
| [DuckCopy] | ||
| [Browsable(false)] | ||
| [EditorBrowsable(EditorBrowsableState.Never)] | ||
| public struct ViewContextStruct | ||
| { | ||
| /// <summary> | ||
| /// Gets the RouteData | ||
| /// </summary> | ||
| public RouteData RouteData; | ||
| } | ||
| } | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| // <copyright file="SharedItemsTests.cs" company="Datadog"> | ||
| // 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. | ||
| // </copyright> | ||
|
|
||
| #if NETFRAMEWORK | ||
| using System.IO; | ||
| using System.Web; | ||
| using Datadog.Trace.AspNet; | ||
| using Xunit; | ||
|
|
||
| namespace Datadog.Trace.ClrProfiler.Managed.Tests.AutoInstrumentation.AspNet | ||
| { | ||
| public class SharedItemsTests | ||
| { | ||
| [Fact] | ||
| public void TestEmpty() | ||
| { | ||
| // Arrange | ||
| const string key = "key"; | ||
| var context = CreateContext(); | ||
|
|
||
| // Assert | ||
| Assert.Null(SharedItems.TryPopScope(context, key)); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void TestOneItem() | ||
| { | ||
| // Arrange | ||
| const string key = "key"; | ||
| var scope = new Scope(null, null, null, false); | ||
| var context = CreateContext(); | ||
|
|
||
| // Act | ||
| SharedItems.PushScope(context, key, scope); | ||
|
|
||
| // Assert | ||
| Assert.Equal(scope, SharedItems.TryPopScope(context, key)); | ||
| } | ||
|
|
||
| [Fact] | ||
| public void TestStackingItems() | ||
| { | ||
| // Arrange | ||
| const string key = "key"; | ||
| var scope1 = new Scope(null, null, null, false); | ||
| var scope2 = new Scope(scope1, null, null, false); | ||
| var scope3 = new Scope(scope2, null, null, false); | ||
| var context = CreateContext(); | ||
| // Act | ||
| SharedItems.PushScope(context, key, scope1); | ||
| SharedItems.PushScope(context, key, scope2); | ||
| SharedItems.PushScope(context, key, scope3); | ||
|
|
||
| // Assert | ||
| Assert.Equal(scope3, SharedItems.TryPopScope(context, key)); | ||
| Assert.Equal(scope2, SharedItems.TryPopScope(context, key)); | ||
| Assert.Equal(scope1, SharedItems.TryPopScope(context, key)); | ||
| } | ||
|
|
||
| private HttpContext CreateContext() | ||
| { | ||
| return new HttpContext(new HttpRequest(string.Empty, "http://datadog.com", string.Empty), new HttpResponse(new StringWriter())); | ||
| } | ||
| } | ||
| } | ||
| #endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really understand why this constant is stored here, when it's never used with the PushItem / PopItem mechanism.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was there and alone in this class in the first place. I decided to use this class as this constant is a key used on the Items dictionary as well.