Skip to content
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

Migrate to ActivitySource #30089

Merged
7 commits merged into from
Feb 25, 2021
Merged

Migrate to ActivitySource #30089

7 commits merged into from
Feb 25, 2021

Conversation

shirhatti
Copy link
Contributor

@shirhatti shirhatti commented Feb 11, 2021

Current behavior:

  1. if (loggingEnabled || diagnosticsListenerEnabled), new up Activity
  2. activity.SetParentId(headers["traceparent"] or headers["request-id"])
  3. activity Add Baggage from headers["baggage"] or headers["Correlation-Context"]
  4. _diagnosticsListener.OnActivityImport(activity, httpContext)
  5. activity.Start()
  6. _diagnosticsListener.Write(ActivityStartKey, httpContext)

New behavior:

  1. if (loggingEnabled || diagnosticsListenerEnabled), new up a dummyListener
  2. Defer creation of Activity to ActivitySource

if (activity is not null)
{

  1. Add baggage
  2. _diagnosticsListener.OnActivityImport(activity, httpContext)
  3. _diagnosticListener.Write(ActivityStartKey, httpContext);

}

Questions:

  1. Baggage isn't present on Activity before starting
  2. Is it okay if we don't fire diagnosticsListener.OnActivityImport? Is ordering important? Can I do it after Activity.Start()?

@ghost ghost added the area-servers label Feb 11, 2021
@shirhatti shirhatti marked this pull request as draft February 11, 2021 07:48
@tarekgh
Copy link
Member

tarekgh commented Feb 12, 2021

CC @noahfalk

@shirhatti
Copy link
Contributor Author

FYI @pakrym


var headers = httpContext.Request.Headers;
if (!headers.TryGetValue(HeaderNames.TraceParent, out var requestId))
{
headers.TryGetValue(HeaderNames.RequestId, out requestId);
}

if (!StringValues.IsNullOrEmpty(requestId))
var activity = _activitySource.StartActivity(ActivityName, ActivityKind.Server, requestId);
Copy link
Contributor

Choose a reason for hiding this comment

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

@tarekgh/@noahfalk, what's the expected format of the activity name when used with the ActivitySource?

I thought that considering the ActivitySource name is Microsoft.AspNetCore.Hosting the activity name should be HttpRequestIn and not Microsoft.AspNetCore.Hosting.HttpRequestIn

Copy link
Member

Choose a reason for hiding this comment

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

You are correct @pakrym, the Activity names should be only the instrumented operation name (i.e. HttpRequestIn for this specific code). And ActivitySource name should reflect the company name, component name, and the whole instrumented area (i.e. Microsoft.AspNetCore.Hosting).

As this code existed for a while, do you think there will be any issue changing the Activity name?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think that's another area where reusing the same activity doesn't work well. We can't change existing activity names and ActivitySource-activity should have a different one.

Copy link
Contributor Author

@shirhatti shirhatti Feb 23, 2021

Choose a reason for hiding this comment

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

We spoke about this earlier today, the Activity name would have to be Microsoft.AspNetCore.Hosting.HttpRequestIn to preserve compat. Instrumentation libraries can set Activity.DisplayName if they would prefer something more concise.

Copy link
Contributor

Choose a reason for hiding this comment

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

Should we say there is no guidance around Activity.Name then? If none of the Microsoft libraries would support it.

Copy link
Member

Choose a reason for hiding this comment

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

The guidelines for Activity.Name still hold we only relax it for the sake of the compatibility. Any new written code with no compatibility concern should apply the guidelines.

Copy link
Contributor

Choose a reason for hiding this comment

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

But if all the consuming code (OT) would forever have to check for legacy behavior. What's the point of the guideline?

Copy link
Member

Choose a reason for hiding this comment

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

I am hoping the OTel will get rid of listening to the legacy behavior. My understanding is after this change, they are listening to the legacy for the payload and not the Activity itself. This may be the next step to look at that and try help OTel to get rid of this legacy listening.

Copy link
Contributor

@pakrym pakrym Feb 23, 2021

Choose a reason for hiding this comment

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

I am hoping the OTel will get rid of listening to the legacy behavior

After this change, all ASP.NET Core ActivitySource activities would forever have legacy Activity.Name.
Same for Azure SDKs if we try to reuse the same activity for both.

Copy link
Member

@tarekgh tarekgh Feb 23, 2021

Choose a reason for hiding this comment

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

As I mentioned we relaxed the guidelines for the Activity.Name for the sake of compatibility. If anyone writing any new code with no compatibility concern, would better to follow the guidelines. For aspnet case, I suggest setting the display name too.

@shirhatti
Copy link
Contributor Author

Waiting on dotnet/runtime#48374 for the next update

@shirhatti shirhatti marked this pull request as ready for review February 23, 2021 22:12
@shirhatti
Copy link
Contributor Author

shirhatti commented Feb 23, 2021

Before: crank.exe --config https://github.com/raw/aspnet/Benchmarks/master/scenarios/json.benchmarks.yml --scenario json --profile aspnet-perf-lin --application.environmentVariables ASPNETCORE_LogLevel=Error --application.framework net6.0 --application.options.displayOutput true

| application           |                           |
| --------------------- | ------------------------- |
| CPU Usage (%)         | 97                        |
| Cores usage (%)       | 1,164                     |
| Working Set (MB)      | 180                       |
| Build Time (ms)       | 4,726                     |
| Start Time (ms)       | 207                       |
| Published Size (KB)   | 280,235                   |
| .NET Core SDK Version | 6.0.100-preview.3.21123.4 |


| load                   |           |
| ---------------------- | --------- |
| CPU Usage (%)          | 50        |
| Cores usage (%)        | 594       |
| Working Set (MB)       | 38        |
| Start Time (ms)        | 0         |
| First Request (ms)     | 127       |
| Requests/sec           | 380,244   |
| Requests               | 5,741,726 |
| Mean latency (ms)      | 0.84      |
| Max latency (ms)       | 52.46     |
| Bad responses          | 0         |
| Socket errors          | 0         |
| Read throughput (MB/s) | 55.12     |
| Latency 50th (ms)      | 0.65      |
| Latency 75th (ms)      | 0.73      |
| Latency 90th (ms)      | 0.82      |
| Latency 99th (ms)      | 8.60      |

After: crank.exe --config https://github.com/raw/aspnet/Benchmarks/master/scenarios/json.benchmarks.yml --scenario json --profile aspnet-perf-lin --application.environmentVariables ASPNETCORE_LogLevel=Error --application.options.outputFiles C:\Users\shirh\source\repos\aspnetcore\artifacts\bin\Microsoft.AspNetCore.Hosting\Release\net6.0\Microsoft.AspNetCore.Hosting.dll --application.framework net6.0 --application.options.displayOutput true

| application           |                           |
| --------------------- | ------------------------- |
| CPU Usage (%)         | 97                        |
| Cores usage (%)       | 1,164                     |
| Working Set (MB)      | 179                       |
| Build Time (ms)       | 12,768                    |
| Start Time (ms)       | 217                       |
| Published Size (KB)   | 280,235                   |
| .NET Core SDK Version | 6.0.100-preview.3.21123.4 |


| load                   |           |
| ---------------------- | --------- |
| CPU Usage (%)          | 51        |
| Cores usage (%)        | 611       |
| Working Set (MB)       | 48        |
| Build Time (ms)        | 7,868     |
| Start Time (ms)        | 0         |
| Published Size (KB)    | 76,407    |
| .NET Core SDK Version  | 3.1.406   |
| First Request (ms)     | 126       |
| Requests/sec           | 397,795   |
| Requests               | 6,006,528 |
| Mean latency (ms)      | 0.78      |
| Max latency (ms)       | 40.68     |
| Bad responses          | 0         |
| Socket errors          | 0         |
| Read throughput (MB/s) | 57.66     |
| Latency 50th (ms)      | 0.62      |
| Latency 75th (ms)      | 0.70      |
| Latency 90th (ms)      | 0.79      |
| Latency 99th (ms)      | 7.72      |

@Tratcher @sebastienros Is there a better benchmark? I remember y'all mentioning there is a YARP benchmark for which Activity creation in hosting was on the hot path?

@sebastienros
Copy link
Member

In the scenarios page, look for yarp. Beware that the sample command is for httpclient though.

@shirhatti
Copy link
Contributor Author

Before: crank.exe --config https://github.com/raw/aspnet/Benchmarks/master/scenarios/proxy.benchmarks.yml --scenario proxy-httpclient --profile aspnet-perf-lin --application.framework net6.0 --application.options.displayOutput true

| downstream            |         |
| --------------------- | ------- |
| CPU Usage (%)         | 87      |
| Cores usage (%)       | 1,043   |
| Working Set (MB)      | 149     |
| Build Time (ms)       | 4,180   |
| Start Time (ms)       | 287     |
| Published Size (KB)   | 95,075  |
| .NET Core SDK Version | 3.1.406 |


| application           |                           |
| --------------------- | ------------------------- |
| CPU Usage (%)         | 95                        |
| Cores usage (%)       | 1,136                     |
| Working Set (MB)      | 189                       |
| Build Time (ms)       | 4,142                     |
| Start Time (ms)       | 160                       |
| Published Size (KB)   | 263,567                   |
| .NET Core SDK Version | 6.0.100-preview.3.21123.4 |


| load                   |           |
| ---------------------- | --------- |
| CPU Usage (%)          | 40        |
| Cores usage (%)        | 475       |
| Working Set (MB)       | 42        |
| Start Time (ms)        | 111       |
| First Request (ms)     | 213       |
| Requests               | 1,878,924 |
| Bad responses          | 0         |
| Mean latency (us)      | 2,038     |
| Max latency (us)       | 51,858    |
| Requests/sec           | 125,322   |
| Requests/sec (max)     | 136,375   |
| Read throughput (MB/s) | 14.45     |

After: crank.exe --config https://github.com/raw/aspnet/Benchmarks/master/scenarios/proxy.benchmarks.yml --scenario proxy-httpclient --profile aspnet-perf-lin --application.options.outputFiles C:\Users\shirh\source\repos\aspnetcore\artifacts\bin\Microsoft.AspNetCore.Hosting\Release\net6.0\Microsoft.AspNetCore.Hosting.dll --application.framework net6.0 --application.options.displayOutput true

| downstream            |         |
| --------------------- | ------- |
| CPU Usage (%)         | 86      |
| Cores usage (%)       | 1,038   |
| Working Set (MB)      | 148     |
| Build Time (ms)       | 5,190   |
| Start Time (ms)       | 288     |
| Published Size (KB)   | 95,075  |
| .NET Core SDK Version | 3.1.406 |


| application           |                           |
| --------------------- | ------------------------- |
| CPU Usage (%)         | 95                        |
| Cores usage (%)       | 1,137                     |
| Working Set (MB)      | 186                       |
| Build Time (ms)       | 5,000                     |
| Start Time (ms)       | 170                       |
| Published Size (KB)   | 263,567                   |
| .NET Core SDK Version | 6.0.100-preview.3.21123.4 |


| load                   |           |
| ---------------------- | --------- |
| CPU Usage (%)          | 40        |
| Cores usage (%)        | 476       |
| Working Set (MB)       | 52        |
| Build Time (ms)        | 5,820     |
| Start Time (ms)        | 111       |
| Published Size (KB)    | 77,083    |
| .NET Core SDK Version  | 3.1.406   |
| First Request (ms)     | 207       |
| Requests               | 1,857,181 |
| Bad responses          | 0         |
| Mean latency (us)      | 2,062     |
| Max latency (us)       | 50,195    |
| Requests/sec           | 124,134   |
| Requests/sec (max)     | 138,900   |
| Read throughput (MB/s) | 14.29     |

@sebastienros
Copy link
Member

--scenario proxy-httpclient this is not yarp, this is a generic httpclient proxy without logging. Use --scenario proxy-yarp intead

@sebastienros
Copy link
Member

Another tip. You can save the results with --output old.json, and --output new.json then do crank compare old.json new.json and get a nice comparison table to share

@shirhatti
Copy link
Contributor Author

Old:

crank.exe --config https://github.com/raw/aspnet/Benchmarks/master/scenarios/proxy.benchmarks.yml --scenario proxy-yarp --profile aspnet-perf-lin --application.framework net6.0 --output old.json

New:

crank.exe --config https://github.com/raw/aspnet/Benchmarks/master/scenarios/proxy.benchmarks.yml --scenario proxy-yarp --profile aspnet-perf-lin --application.options.outputFiles C:\Users\shirh\source\repos\aspnetcore\artifacts\bin\Microsoft.AspNetCore.Hosting\Release\net6.0\Microsoft.AspNetCore.Hosting.dll --application.framework net6.0
| downstream            | old     | new     |         |
| --------------------- | ------- | ------- | ------- |
| CPU Usage (%)         |      85 |      82 |  -3.53% |
| Cores usage (%)       |   1,014 |     984 |  -2.96% |
| Working Set (MB)      |     149 |     149 |   0.00% |
| Build Time (ms)       |   5,219 |   4,243 | -18.70% |
| Start Time (ms)       |     295 |     293 |  -0.68% |
| Published Size (KB)   |  95,075 |  95,075 |   0.00% |
| .NET Core SDK Version | 3.1.406 | 3.1.406 |         |


| application           | old                       | new                       |         |
| --------------------- | ------------------------- | ------------------------- | ------- |
| CPU Usage (%)         |                        96 |                        97 |  +1.04% |
| Cores usage (%)       |                     1,154 |                     1,160 |  +0.52% |
| Working Set (MB)      |                       212 |                       212 |   0.00% |
| Build Time (ms)       |                     7,003 |                     5,355 | -23.53% |
| Start Time (ms)       |                       367 |                       352 |  -4.09% |
| Published Size (KB)   |                   263,844 |                   263,844 |   0.00% |
| .NET Core SDK Version | 6.0.100-preview.3.21123.4 | 6.0.100-preview.3.21123.4 |         |


| load                   | old       | new       |         |
| ---------------------- | --------- | --------- | ------- |
| CPU Usage (%)          |        35 |        35 |   0.00% |
| Cores usage (%)        |       417 |       417 |   0.00% |
| Working Set (MB)       |        42 |        42 |   0.00% |
| Start Time (ms)        |       111 |       110 |  -0.90% |
| First Request (ms)     |       246 |       239 |  -2.85% |
| Requests               | 1,443,811 | 1,426,715 |  -1.18% |
| Bad responses          |         0 |         0 |         |
| Mean latency (us)      |     2,653 |     2,683 |  +1.13% |
| Max latency (us)       |    53,336 |    72,823 | +36.54% |
| Requests/sec           |    96,441 |    95,419 |  -1.06% |
| Requests/sec (max)     |   105,120 |   104,774 |  -0.33% |
| Read throughput (MB/s) |     11.11 |     10.98 |  -1.18% |

{
var activity = new Activity(ActivityName);
var activity = _activitySource.CreateActivity(ActivityName, ActivityKind.Server);
Copy link
Member

Choose a reason for hiding this comment

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

var [](start = 12, length = 3)

does it make sense to check _activitySource.HasListeners() before calling _activitySource.CreateActivity if you want save some cycles when there is no listeners?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think it'll make a difference. You already do the check in ActivitySource.CreateActivity- https://github.com/dotnet/runtime/blob/master/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/ActivitySource.cs#L172

@davidfowl
Copy link
Member

allocation profile plz 😄

@shirhatti
Copy link
Contributor Author

shirhatti commented Feb 24, 2021

image
(new allocation profile on the left, old one of the right)

No difference from eyeballing it.

The only thing mildly noteworthy is that average Activity size went up from 176 to 192 bytes

@ghost
Copy link

ghost commented Feb 25, 2021

Hello @shirhatti!

Because this pull request has the auto-merge label, I will be glad to assist with helping to merge this pull request once all check-in policies pass.

p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (@msftbot) and give me an instruction to get started! Learn more here.

@ghost ghost merged commit 8df6490 into main Feb 25, 2021
@ghost ghost deleted the shirhatti/activitysource branch February 25, 2021 02:11
@amcasey amcasey added area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions and removed area-runtime labels Aug 24, 2023
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Migrate to use ActivitySource API to create Activities in AspNetCore Hosting
8 participants