Skip to content

Activity Compatibility Layer #2446

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

Merged
merged 107 commits into from
Apr 22, 2022
Merged

Conversation

tonyredondo
Copy link
Member

@tonyredondo tonyredondo commented Feb 16, 2022

This PR enables support for System.Diagnostics.Activity class behind DD_TRACE_ACTIVITY_LISTENER_ENABLED experimental feature flag (here).

The implementation creates a Datadog Span object for each activity, and handles interoperation between activities and span objects keeping the ActiveScope updated. (See Tests)

Diagram

image

By using W3C propagators, we can interoperate with OpenTelemetry applications and keep the same distributed trace.

Support

The listener supports System.Diagnostics.DiagnosticSource assembly from dll version 4.0.4 (Nuget version 4.6.0) and tested up to version 6.

Note: we are using DuckTyping to interact with the library, no vendoring or strong reference is required.

Handlers

The PR contains a standard way to create new handlers and change the behavior of the library depending of the DiagnosticSource Name. Currently there are two implementations:

  • IgnoreActivityHandler: a Handler to ignore activities created from the following sources:
    • Couchbase.DotnetSdk.RequestTracer
    • HttpHandlerDiagnosticListener
    • MassTransit
    • Microsoft.AspNetCore
    • Microsoft.EntityFrameworkCore
    • MySqlConnector
    • Npgsql
    • System.Net.Http.Desktop
    • SqlClientDiagnosticListener
      The reason we are ignoring these sources is because we already have a working integration that handles those spans.
  • DefaultActivityHandler: Algorithm explained in the diagram for any other source.

Example

image

image

image

@tonyredondo tonyredondo self-assigned this Feb 16, 2022
@andrewlock

This comment has been minimized.

@andrewlock

This comment has been minimized.

@andrewlock

This comment has been minimized.

@andrewlock

This comment has been minimized.

@andrewlock
Copy link
Member

In principle, I think this looks really nice 😍

I'd like to see us testing more approaches to creating activities (I know this is just a POC, just not sure if it will reveal any issues) e.g.:

var activity = new Activity("Logging Activity");

try
{
    activity.Start();
    activity.AddTag("cart.operation.id", 123);
}
finally
{
    activity.Stop();
}

Though I think we may need to think about the "ignore list". 🤔

For example, in an ASP.NET Core app, the framework creates the activity for the request, and you typically add high cardinality tags to that "root" activity. Users will likely want those to be applied to our root ASP.NET Core span, which is, difficult 😕

It means we would need to map some of the activities to our existing spans (if they're in the trace), to make sure we are adding the tags to the right span (This recent post by Jimmy Bogard describes what I'm talking about)

Unless, of course, we completely replace our ASP.NET Core diagnostic source implementation with your Activity approach (where possible) 🤔 That may not be feasible, I'm not sure.

Also, it looks like there's a risk of "leaking" spans, if the customer doesn't stop the activity? Obviously that's an issue in their code, but it's something we probably want/need to handle somehow. I haven't got a clue how we do that and ensure we maintain the hierarchy correctly though, so maybe we can't resolve that one😟

@andrewlock

This comment has been minimized.

Copy link
Contributor

@zacharycmontoya zacharycmontoya left a comment

Choose a reason for hiding this comment

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

LGTM, amazing work as always 🎉

One part of the design that I think is worth mentioning in the PR description is how we handle a new "root" Activity object when there iss already an active Datadog Span: In this case, activity.Parent == null (there's no in-process parent Activity object) but we set the ParentId property to the parent Span's SpanId

@andrewlock

This comment has been minimized.

@andrewlock

This comment has been minimized.

@tonyredondo
Copy link
Member Author

Just realised, we don't have any full integration tests for using Activities. We should add them. I'd be inclined to add Activities to an existing sample (to avoid adding more samples to build, e.g. Samples.Telemetry), toggle the feature flag, and use snapshots to assert everything looks as we'd expect 🙂

We should also add the state of the flag to Telemetry (I don't believe we're doing that yet)?

You're right!, done in 20b8e28 and d7b990a

Comment on lines +87 to +88
Assert.Contains(spans, span => span.Name == expectedOperationName);
Assert.Contains(spans, span => span.Name == spanFromActivityOperationName);
Copy link
Member

Choose a reason for hiding this comment

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

I'd definitely prefer we use snapshots here, to make sure all the parents etc line up.

Copy link
Member Author

@tonyredondo tonyredondo Apr 20, 2022

Choose a reason for hiding this comment

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

well both spans doesn't share the same traceId.... They are not part of the same execution context, and we are not testing the context propagation. (Also, we don't have an API to set the context of an activity yet), only W3C is supported automatically. That's the reason I thought this was enough.

Copy link
Member

Choose a reason for hiding this comment

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

They are not part of the same execution context, and we are not testing the context propagation. (Also, we don't have an API to set the context of an activity yet), only W3C is supported automatically

Hmmm, I feel like we should have integration tests for that 🤔 Definitely happy for that to be a new PR though!

@andrewlock

This comment has been minimized.

@andrewlock

This comment has been minimized.

@andrewlock

This comment has been minimized.

@andrewlock

This comment has been minimized.

@andrewlock

This comment has been minimized.

@andrewlock
Copy link
Member

Benchmarks Report 🐌

Benchmarks for #2446 compared to master:

  • All benchmarks have the same speed
  • All benchmarks have the same allocations

The following thresholds were used for comparing the benchmark speeds:

  • Mann–Whitney U test with statistical test for significance of 5%
  • Only results indicating a difference greater than 10% and 0.3 ns are considered.

Allocation changes below 0.5% are ignored.

Benchmark details

Benchmarks.Trace.AgentWriterBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master WriteAndFlushEnrichedTraces net472 924μs 2.65μs 9.91μs 0 0 0 3.16 KB
master WriteAndFlushEnrichedTraces netcoreapp3.1 713μs 3.54μs 22.6μs 0 0 0 2.57 KB
#2446 WriteAndFlushEnrichedTraces net472 958μs 5.44μs 38.1μs 0 0 0 3.16 KB
#2446 WriteAndFlushEnrichedTraces netcoreapp3.1 763μs 4μs 20μs 0 0 0 2.57 KB
Benchmarks.Trace.AppSecBodyBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master AllCycleSimpleBody net472 325ns 1.83ns 11.6ns 0.066 0 0 417 B
master AllCycleSimpleBody netcoreapp3.1 405ns 1.19ns 4.46ns 0.00581 0 0 416 B
master AllCycleMoreComplexBody net472 315ns 1.73ns 9.47ns 0.0622 0 0 393 B
master AllCycleMoreComplexBody netcoreapp3.1 411ns 1.98ns 8.4ns 0.00545 0 0 392 B
master BodyExtractorSimpleBody net472 438ns 2.53ns 19.3ns 0.0569 0 0 361 B
master BodyExtractorSimpleBody netcoreapp3.1 445ns 2.03ns 7.59ns 0.00371 0 0 272 B
master BodyExtractorMoreComplexBody net472 23.9μs 104ns 391ns 1.19 0.0117 0 7.62 KB
master BodyExtractorMoreComplexBody netcoreapp3.1 20.9μs 102ns 455ns 0.0906 0 0 6.75 KB
#2446 AllCycleSimpleBody net472 321ns 1.37ns 5.3ns 0.066 0 0 417 B
#2446 AllCycleSimpleBody netcoreapp3.1 424ns 2.31ns 12.7ns 0.0058 0 0 416 B
#2446 AllCycleMoreComplexBody net472 328ns 1.82ns 10.4ns 0.0621 0 0 393 B
#2446 AllCycleMoreComplexBody netcoreapp3.1 420ns 1.99ns 7.72ns 0.00549 0 0 392 B
#2446 BodyExtractorSimpleBody net472 453ns 2.39ns 15.1ns 0.057 0 0 361 B
#2446 BodyExtractorSimpleBody netcoreapp3.1 452ns 2.47ns 13.7ns 0.00372 0 0 272 B
#2446 BodyExtractorMoreComplexBody net472 24.6μs 127ns 607ns 1.19 0.0121 0 7.62 KB
#2446 BodyExtractorMoreComplexBody netcoreapp3.1 21.2μs 103ns 458ns 0.0878 0 0 6.75 KB
Benchmarks.Trace.AspNetCoreBenchmark - Unknown 🤷 Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master SendRequest net472 0ns 0ns 0ns 0 0 0 0 b
master SendRequest netcoreapp3.1 292μs 1.27μs 4.92μs 0.149 0 0 19.74 KB
#2446 SendRequest net472 0ns 0ns 0ns 0 0 0 0 b
#2446 SendRequest netcoreapp3.1 298μs 1.42μs 5.5μs 0.149 0 0 19.74 KB
Benchmarks.Trace.DbCommandBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master ExecuteNonQuery net472 1.82μs 10.2ns 63.6ns 0.0943 0 0 594 B
master ExecuteNonQuery netcoreapp3.1 1.64μs 8.37ns 37.4ns 0.00906 0 0 632 B
#2446 ExecuteNonQuery net472 2.03μs 11.3ns 74.8ns 0.0939 0.000988 0 594 B
#2446 ExecuteNonQuery netcoreapp3.1 1.72μs 10.1ns 92.4ns 0.00903 0 0 632 B
Benchmarks.Trace.ElasticsearchBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master CallElasticsearch net472 2.77μs 14.2ns 63.3ns 0.127 0 0 803 B
master CallElasticsearch netcoreapp3.1 1.7μs 7.43ns 27.8ns 0.0105 0 0 792 B
master CallElasticsearchAsync net472 2.94μs 11.6ns 50.5ns 0.148 0.00146 0 939 B
master CallElasticsearchAsync netcoreapp3.1 1.83μs 9.08ns 38.5ns 0.0127 0 0 912 B
#2446 CallElasticsearch net472 2.65μs 14.2ns 81.8ns 0.127 0 0 802 B
#2446 CallElasticsearch netcoreapp3.1 1.71μs 8.71ns 38ns 0.0113 0 0 792 B
#2446 CallElasticsearchAsync net472 2.98μs 16.8ns 118ns 0.146 0 0 939 B
#2446 CallElasticsearchAsync netcoreapp3.1 2.07μs 11.6ns 81.4ns 0.0125 0 0 912 B
Benchmarks.Trace.GraphQLBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master ExecuteAsync net472 3.51μs 16.1ns 62.3ns 0.167 0.00169 0 1.06 KB
master ExecuteAsync netcoreapp3.1 2.11μs 11.8ns 75.7ns 0.0148 0 0 1.03 KB
#2446 ExecuteAsync net472 3.15μs 17.2ns 97.2ns 0.168 0 0 1.06 KB
#2446 ExecuteAsync netcoreapp3.1 2.08μs 8.82ns 33ns 0.0146 0 0 1.03 KB
Benchmarks.Trace.HttpClientBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master SendAsync net472 7.38μs 39.4ns 204ns 0.36 0 0 2.28 KB
master SendAsync netcoreapp3.1 4.92μs 24.2ns 99.6ns 0.0307 0 0 2.21 KB
#2446 SendAsync net472 7.34μs 32.8ns 127ns 0.362 0 0 2.28 KB
#2446 SendAsync netcoreapp3.1 5.16μs 25.4ns 111ns 0.0304 0 0 2.21 KB
Benchmarks.Trace.ILoggerBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master EnrichedLog net472 3.53μs 18.6ns 116ns 0.227 0 0 1.45 KB
master EnrichedLog netcoreapp3.1 3.24μs 18.4ns 141ns 0.0207 0 0 1.53 KB
#2446 EnrichedLog net472 3.66μs 21.3ns 183ns 0.227 0 0 1.45 KB
#2446 EnrichedLog netcoreapp3.1 3.28μs 17.5ns 92.8ns 0.0214 0 0 1.53 KB
Benchmarks.Trace.Log4netBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master EnrichedLog net472 283μs 1.31μs 5.07μs 0.436 0.145 0 4.33 KB
master EnrichedLog netcoreapp3.1 227μs 1.15μs 6.17μs 0 0 0 4.21 KB
#2446 EnrichedLog net472 290μs 964ns 3.73μs 0.441 0.147 0 4.33 KB
#2446 EnrichedLog netcoreapp3.1 241μs 1.19μs 7.6μs 0 0 0 4.21 KB
Benchmarks.Trace.NLogBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master EnrichedLog net472 8.72μs 46.3ns 236ns 0.505 0 0 3.23 KB
master EnrichedLog netcoreapp3.1 6.76μs 30.2ns 128ns 0.0501 0 0 3.6 KB
#2446 EnrichedLog net472 7.83μs 43.9ns 284ns 0.506 0 0 3.23 KB
#2446 EnrichedLog netcoreapp3.1 6.59μs 35.9ns 206ns 0.0484 0 0 3.6 KB
Benchmarks.Trace.RedisBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master SendReceive net472 2.53μs 13.2ns 67.6ns 0.16 0.00144 0 1.01 KB
master SendReceive netcoreapp3.1 2.06μs 8.34ns 31.2ns 0.0144 0 0 1.01 KB
#2446 SendReceive net472 2.72μs 13.7ns 64.4ns 0.16 0.00138 0 1.01 KB
#2446 SendReceive netcoreapp3.1 2.22μs 12.3ns 73.7ns 0.0138 0 0 1.01 KB
Benchmarks.Trace.SerilogBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master EnrichedLog net472 6.26μs 32.2ns 164ns 0.293 0 0 1.87 KB
master EnrichedLog netcoreapp3.1 5.21μs 24.8ns 99.2ns 0.0206 0 0 1.49 KB
#2446 EnrichedLog net472 6.85μs 35.8ns 189ns 0.292 0 0 1.87 KB
#2446 EnrichedLog netcoreapp3.1 5.4μs 30.2ns 210ns 0.0193 0 0 1.49 KB
Benchmarks.Trace.SpanBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master StartFinishSpan net472 923ns 4.5ns 18ns 0.0718 0 0 457 B
master StartFinishSpan netcoreapp3.1 891ns 4.92ns 30.7ns 0.00618 0 0 456 B
master StartFinishScope net472 1.13μs 5.82ns 28.5ns 0.0844 0 0 538 B
master StartFinishScope netcoreapp3.1 1.06μs 3.29ns 12.7ns 0.00823 0 0 576 B
#2446 StartFinishSpan net472 1.03μs 5.54ns 31.8ns 0.0715 0 0 457 B
#2446 StartFinishSpan netcoreapp3.1 950ns 4.38ns 20.5ns 0.00643 0 0 456 B
#2446 StartFinishScope net472 1.18μs 6.46ns 36ns 0.0843 0 0 538 B
#2446 StartFinishScope netcoreapp3.1 1.12μs 5.9ns 32.3ns 0.00773 0 0 576 B
Benchmarks.Trace.TraceAnnotationsBenchmark - Same speed ✔️ Same allocations ✔️

Raw results

Branch Method Toolchain Mean StdError StdDev Gen 0 Gen 1 Gen 2 Allocated
master RunOnMethodBegin net472 1.25μs 5.92ns 22.9ns 0.0843 0 0 538 B
master RunOnMethodBegin netcoreapp3.1 1.17μs 4.89ns 17.6ns 0.00812 0 0 576 B
#2446 RunOnMethodBegin net472 1.36μs 7.7ns 57.1ns 0.084 0 0 538 B
#2446 RunOnMethodBegin netcoreapp3.1 1.27μs 6.23ns 24.9ns 0.00779 0 0 576 B

@andrewlock
Copy link
Member

Code Coverage Report 📊

⚠️ Merging #2446 into master will will decrease line coverage by 1%
⚠️ Merging #2446 into master will will decrease branch coverage by 2%
⛔ Merging #2446 into master will will increase complexity by 543

master #2446 Change
Lines 13300 / 18702 13736 / 19469
Lines % 71% 71% -1% ⚠️
Branches 7649 / 11167 7813 / 11678
Branches % 68% 67% -2% ⚠️
Complexity 12456 12999 543

View the full report for further details:

Datadog.Trace Breakdown ⚠️

master #2446 Change
Lines % 71% 71% -1% ⚠️
Branches % 68% 67% -2% ⚠️
Complexity 12456 12999 543

The following classes have significant coverage changes.

File Line coverage change Branch coverage change Complexity change
Datadog.Trace.Agent.Transports.ApiWebRequestFactory -28% -38% 12
Datadog.Trace.Telemetry.JsonWebRequestTelemetryTransport -27% -57% 0 ✔️
Datadog.Trace.Telemetry.JsonHttpClientTelemetryTransport -26% -38% 0 ✔️
Datadog.Trace.SpanContext -25% -34% 74
Datadog.Trace.Logging.DirectSubmission.Sink.LogsApi -25% -29% 33
Datadog.Trace.Configuration.ImmutableTracerSettings -17% -32% 21
Datadog.Trace.TracerManager -17% -30% 66
Datadog.Trace.Configuration.TracerSettings -11% -7% 39
Datadog.Trace.Telemetry.ConfigurationTelemetryCollector -10% -19% 4
Datadog.Trace.Tracer -10% -12% 18
...And 5 more

The following classes were added in #2446:

File Line coverage Branch coverage Complexity
Datadog.Trace.Activity.ActivityListener 79% 52% 48
Datadog.Trace.Activity.ActivityListenerDelegatesBuilder 100% 75% 11
Datadog.Trace.Activity.ActivityListenerHandler 72% 68% 27
Datadog.Trace.Activity.DiagnosticObserverListener 75% 67% 7
Datadog.Trace.Activity.DiagnosticSourceEventListener 70% 88% 27
...And 3 more

View the full reports for further details:

@tonyredondo tonyredondo merged commit f9c842c into master Apr 22, 2022
@tonyredondo tonyredondo deleted the tony/activity-compatibility-layer branch April 22, 2022 18:40
@github-actions github-actions bot added this to the vNext milestone Apr 22, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:tracer The core tracer library (Datadog.Trace, does not include OpenTracing, native code, or integrations) type:enhancement Improvement to an existing feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants