Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 0 additions & 12 deletions tracer/src/Datadog.Trace/AspNet/SharedConstants.cs

This file was deleted.

59 changes: 59 additions & 0 deletions tracer/src/Datadog.Trace/AspNet/SharedItems.cs
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";
Copy link
Copy Markdown
Member

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.

Copy link
Copy Markdown
Contributor Author

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.


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
8 changes: 2 additions & 6 deletions tracer/src/Datadog.Trace/AspNet/TracingHttpModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,11 @@
using System.Linq;
using System.Web;
using Datadog.Trace.AppSec;
using Datadog.Trace.AppSec.Transport.Http;
using Datadog.Trace.Configuration;
using Datadog.Trace.ExtensionMethods;
using Datadog.Trace.Headers;
using Datadog.Trace.Logging;
using Datadog.Trace.Tagging;
using Datadog.Trace.Util;
using Datadog.Trace.Util.Http;

namespace Datadog.Trace.AspNet
{
Expand Down Expand Up @@ -77,7 +74,6 @@ public void Dispose()
private void OnBeginRequest(object sender, EventArgs eventArgs)
{
Scope scope = null;

try
{
var tracer = Tracer.Instance;
Expand Down Expand Up @@ -175,8 +171,8 @@ private void OnEndRequest(object sender, EventArgs eventArgs)

scope.Span.SetHttpStatusCode(app.Context.Response.StatusCode, isServer: true, Tracer.Instance.Settings);

if (app.Context.Items[SharedConstants.HttpContextPropagatedResourceNameKey] is string resourceName
&& !string.IsNullOrEmpty(resourceName))
if (app.Context.Items[SharedItems.HttpContextPropagatedResourceNameKey] is string resourceName
&& !string.IsNullOrEmpty(resourceName))
{
scope.Span.ResourceName = resourceName;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does not appear to be used.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, question is whether:

  • It should be removed as unused
  • We should use a different operation name for child actions
    I'm pretty sure we discussed it, but can't remember the conclusion 😬

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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.
Fixed in 9c0cc8d


private const string RouteCollectionRouteTypeName = "System.Web.Mvc.Routing.RouteCollectionRoute";

Expand All @@ -43,7 +44,6 @@ internal static Scope CreateScope(ControllerContextStruct controllerContext)
try
{
var httpContext = controllerContext.HttpContext;

if (httpContext == null)
{
return null;
Expand All @@ -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)
{
Expand Down Expand Up @@ -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(
Expand All @@ -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;
}
}
}
Expand All @@ -189,5 +197,4 @@ internal static Scope CreateScope(ControllerContextStruct controllerContext)
}
}
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to check if the resource name has already been set?

}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.ComponentModel;
using System.Web;
using Datadog.Trace.AppSec;
using Datadog.Trace.AspNet;
using Datadog.Trace.ClrProfiler.CallTarget;
using Datadog.Trace.Configuration;
using Datadog.Trace.DuckTyping;
Expand Down Expand Up @@ -60,7 +61,7 @@ internal static CallTargetState OnMethodBegin<TTarget, TContext>(TTarget instanc
{
var duckedControllerContext = controllerContext.DuckCast<ControllerContextStruct>();
scope = AspNetMvcIntegration.CreateScope(duckedControllerContext);
HttpContext.Current.Items[AspNetMvcIntegration.HttpContextKey] = scope;
SharedItems.PushScope(HttpContext.Current, AspNetMvcIntegration.HttpContextKey, scope);

var security = Security.Instance;
if (security.Settings.Enabled)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System;
using System.ComponentModel;
using System.Web;
using Datadog.Trace.AspNet;
using Datadog.Trace.ClrProfiler.CallTarget;
using Datadog.Trace.Configuration;
using Datadog.Trace.ExtensionMethods;
Expand Down Expand Up @@ -54,7 +55,7 @@ internal static CallTargetReturn<TResult> OnMethodEnd<TTarget, TResult>(TTarget

try
{
scope = httpContext?.Items[AspNetMvcIntegration.HttpContextKey] as Scope;
scope = SharedItems.TryPopScope(httpContext, AspNetMvcIntegration.HttpContextKey);
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ internal struct ControllerContextStruct
/// Gets the RouteData
/// </summary>
public RouteData RouteData;

/// <summary>
/// Gets the ParentActionViewContext
/// </summary>
public ViewContextStruct ParentActionViewContext;
}
}
#endif
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
Expand Up @@ -74,21 +74,21 @@ public AspNetMvc4Tests(IisFixture iisFixture, ITestOutputHelper output, bool cla

public static TheoryData<string, int> Data() => new()
{
{ "/Admin", 200 },
{ "/Admin/Home", 200 },
{ "/Admin/Home/Index", 200 },
{ "/", 200 },
{ "/Home", 200 },
{ "/Home/Index", 200 },
{ "/Home/BadRequest", 500 },
{ "/Home/identifier", 500 },
{ "/Home/identifier/123", 200 },
{ "/Home/identifier/BadValue", 500 },
{ "/Home/OptionalIdentifier", 200 },
{ "/Home/OptionalIdentifier/123", 200 },
{ "/Home/OptionalIdentifier/BadValue", 200 },
{ "/Home/StatusCode?value=201", 201 },
{ "/Home/StatusCode?value=503", 503 },
{ "/Admin", 200 }, // Contains child actions
{ "/Admin/Home", 200 }, // Contains child actions
{ "/Admin/Home/Index", 200 }, // Contains child actions
{ "/", 200 },
{ "/Home", 200 },
{ "/Home/Index", 200 },
{ "/Home/BadRequest", 500 },
{ "/Home/identifier", 500 },
{ "/Home/identifier/123", 200 },
{ "/Home/identifier/BadValue", 500 },
{ "/Home/OptionalIdentifier", 200 },
{ "/Home/OptionalIdentifier/123", 200 },
{ "/Home/OptionalIdentifier/BadValue", 200 },
{ "/Home/StatusCode?value=201", 201 },
{ "/Home/StatusCode?value=503", 503 },
};

[SkippableTheory]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@ public AspNetMvc5Tests(IisFixture iisFixture, ITestOutputHelper output, bool cla

public static TheoryData<string, int> Data() => new()
{
{ "/DataDog", 200 },
{ "/DataDog/DogHouse", 200 },
{ "/DataDog/DogHouse/Woof", 200 },
{ "/DataDog", 200 }, // Contains child actions
{ "/DataDog/DogHouse", 200 }, // Contains child actions
{ "/DataDog/DogHouse/Woof", 200 }, // Contains child actions
{ "/", 200 },
{ "/Home", 200 },
{ "/Home/Index", 200 },
Expand Down
68 changes: 68 additions & 0 deletions tracer/test/Datadog.Trace.Tests/AspNet/SharedItemsTests.cs
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
Loading