Skip to content

Commit

Permalink
Add manual+automatic instrumentation tests + fix SetUser bug (#4938)
Browse files Browse the repository at this point in the history
* Add integration tests + sample for manual instrumentation

* Add fix for calling SetUser outside of an ASP.NET Core context

* Add workaround for the flake

* Fix manual snapshots
  • Loading branch information
andrewlock authored Dec 1, 2023
1 parent 924dc94 commit a1a1a81
Show file tree
Hide file tree
Showing 9 changed files with 1,689 additions and 11 deletions.
7 changes: 7 additions & 0 deletions Datadog.Trace.sln
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Samples.Amazon.Lambda.Runti
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Samples.Remoting", "tracer\test\test-applications\integrations\Samples.Remoting\Samples.Remoting.csproj", "{23EA38E3-0BF1-40DF-A52D-C34EA2FB3F26}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Samples.ManualInstrumentation", "tracer\test\test-applications\integrations\Samples.ManualInstrumentation\Samples.ManualInstrumentation.csproj", "{F24EB026-65FB-4247-ABF8-942B11ADAB64}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -1329,6 +1331,10 @@ Global
{23EA38E3-0BF1-40DF-A52D-C34EA2FB3F26}.Debug|Any CPU.Build.0 = Debug|Any CPU
{23EA38E3-0BF1-40DF-A52D-C34EA2FB3F26}.Release|Any CPU.ActiveCfg = Release|Any CPU
{23EA38E3-0BF1-40DF-A52D-C34EA2FB3F26}.Release|Any CPU.Build.0 = Release|Any CPU
{F24EB026-65FB-4247-ABF8-942B11ADAB64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F24EB026-65FB-4247-ABF8-942B11ADAB64}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F24EB026-65FB-4247-ABF8-942B11ADAB64}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F24EB026-65FB-4247-ABF8-942B11ADAB64}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1545,6 +1551,7 @@ Global
{4E9535E2-C918-4C39-9F1B-F182C185DE64} = {8CEC2042-F11C-49F5-A674-2355793B600A}
{18A6904A-5AFD-4816-AC3F-9F5E433720B5} = {BAF8F246-3645-42AD-B1D0-0F7EAFBAB34A}
{23EA38E3-0BF1-40DF-A52D-C34EA2FB3F26} = {BAF8F246-3645-42AD-B1D0-0F7EAFBAB34A}
{F24EB026-65FB-4247-ABF8-942B11ADAB64} = {BAF8F246-3645-42AD-B1D0-0F7EAFBAB34A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {160A1D00-1F5B-40F8-A155-621B4459D78F}
Expand Down
41 changes: 41 additions & 0 deletions tracer/src/Datadog.Trace/AspNetCoreAvailabilityChecker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// <copyright file="AspNetCoreAvailabilityChecker.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;

namespace Datadog.Trace;

internal static class AspNetCoreAvailabilityChecker
{
private static readonly Lazy<bool> IsAvailable = new(CheckForRequiredTypes);

private static bool CheckForRequiredTypes()
{
try
{
// Try to load the HttpContext type
// Assumes that this is only called when we would expect the type to
// already be loaded, for example inside an ASP.NET Core request.
// As this is only checked _once_, it assumes the customer doesn't call
// this API both _before_ ASP.NET Core types are loaded _and_ afterwards.
var httpContextType = Type.GetType("Microsoft.AspNetCore.Http.HttpContext, Microsoft.AspNetCore.Http.Abstractions", throwOnError: false);
return httpContextType is not null;
}
catch
{
// Problem loading the type, so assume we're not in aspnetcore
return false;
}
}

/// <summary>
/// Should be called when ASP.NET Core is expected to be loaded,
/// and checks if ASP.NET Core types are available
/// </summary>
public static bool IsAspNetCoreAvailable() => IsAvailable.Value;
}
#endif
24 changes: 13 additions & 11 deletions tracer/src/Datadog.Trace/SpanExtensions.Core.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,9 @@
// </copyright>
#if !NETFRAMEWORK

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
using Datadog.Trace.AppSec;
using Datadog.Trace.AppSec.Coordinator;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Template;

namespace Datadog.Trace
{
Expand All @@ -27,9 +19,19 @@ private static void RunBlockingCheck(Span span, string userId)
{
var security = Security.Instance;

if (security.Enabled && CoreHttpContextStore.Instance.Get() is { } httpContext)
if (security.Enabled && AspNetCoreAvailabilityChecker.IsAspNetCoreAvailable())
{
security.CheckUser(httpContext, span, userId);
RunBlockingCheckUnsafe(security, span, userId);
}

// Don't inline this, so we don't load the aspnetcore types if they're not available
[MethodImpl(MethodImplOptions.NoInlining)]
static void RunBlockingCheckUnsafe(Security security, Span span, string userId)
{
if (CoreHttpContextStore.Instance.Get() is { } httpContext)
{
security.CheckUser(httpContext, span, userId);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// <copyright file="ManualInstrumentationTests.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>

using System.Threading.Tasks;
using Datadog.Trace.TestHelpers;
using FluentAssertions;
using FluentAssertions.Execution;
using VerifyXunit;
using Xunit;
using Xunit.Abstractions;

namespace Datadog.Trace.ClrProfiler.IntegrationTests;

[UsesVerify]
public class ManualInstrumentationTests : TestHelper
{
public ManualInstrumentationTests(ITestOutputHelper output)
: base("ManualInstrumentation", output)
{
}

[SkippableFact]
[Trait("RunOnWindows", "True")]
public async Task ManualAndAutomatic()
{
const int expectedSpans = 36;
using var telemetry = this.ConfigureTelemetry();
using var agent = EnvironmentHelper.GetMockAgent();
using var assert = new AssertionScope();
using var process = RunSampleAndWaitForExit(agent);

var spans = agent.WaitForSpans(expectedSpans);
spans.Should().HaveCount(expectedSpans);

var settings = VerifyHelper.GetSpanVerifierSettings();

await VerifyHelper.VerifySpans(spans, settings);
}

[SkippableFact]
[Trait("RunOnWindows", "True")]
public async Task ManualOnly()
{
EnvironmentHelper.SetAutomaticInstrumentation(false);
const int expectedSpans = 29;
using var telemetry = this.ConfigureTelemetry();
using var agent = EnvironmentHelper.GetMockAgent();
using var assert = new AssertionScope();
using var process = RunSampleAndWaitForExit(agent);

var spans = agent.WaitForSpans(expectedSpans);
spans.Should().HaveCount(expectedSpans);

var settings = VerifyHelper.GetSpanVerifierSettings();

await VerifyHelper.VerifySpans(spans, settings);
}
}
Loading

0 comments on commit a1a1a81

Please sign in to comment.