OpenTelemetry and Method Boundaries
Probably a dumb question, but does ActivitySource (within the context of OpenTelemetry-Dotnet and Application Insights) track context across a method boundary or an async method boundary? From what I've read, it internally tracks Activity.Current and should track context across method boundaries further down in the application, but I guess I don't understand entirely how that works internally.
From what I've seen elsewhere, you have to set the parent context when you have multiple nexted Activities within the same scope, for setting an Outer activity and then small Inner activities if you wanted to track things within the same method. Outside of that, I know you need to pass parent context across thread boundaries if you using producer / consumer patterns and pushing things onto a queue (from what I understand)
I guess my confusion lies where I don't understand how the Span and TraceId propagate across method boundaries into child method calls and how those subsequent child methods are instrumented and measured. In application insights, I see a Gantt chart with processing times for methods and I'm just wondering how that chart is created with the top level method calls propagating through all of the child method calls, and whether I have to pass the context down through those methods with each of their own activities for each method to be measured. Or if ActivitySources / OpenTelemetry-Dotnet does that for you
Or do I need to explicitly pass parent context down into each layer of the application
15 Replies
Most applications will only have one ActivitySource. It can and should be statically referenced.
Activities are the dotnet name; in the OTel world they are traces. A trace has a single root, and then every trace underneath the root is a nested/child trace. This is the chart you're seeing in app insights.
Activities can and should be created at the point of ingress for an application.
- For an API, that is your endpoints/controllers
- For a background processing job, it is the timer that starts that job
- For a user interface, it is the event handler that reacts to user input (clicking a button, drawing on the screen, etc)
It might be good to see if the UI framework you're using has OTel libraries or something baked in; ideally this is handled transparently for you
I don't believe Avalonia does out of the box, but I could check
It's actually hard to find answers because results for avalonia and "Activity" result in android answers
GPT says you can do this with
InputManager.Instance.PreProcessInput += OnPreProcessInput;
And then examining the routed events.
Regardless - the point is that you want some very central way to start an activity. You do not want to think about it, and the conventions for the arguments and tags associated with that activity should be highly consistent and predictable.
For ASP.NET, when a request comes in, I always know the path of the rest, the HTTP method, and a couple other things.
TYPICALLY you don't need to be concerned with starting all kinds of internal sub-activities.They have a couple of activities of their own for raising events, etc

Sounds like RaisingRoutingEvent is what you want
Basically - by the time your code is hit, you want an activity already started.
Gotcha
So, where it gets messy
I actually bitched about this a bit ago

OTel easily allows for static access to its concepts; they're thread-safe, and easy to consume anywhere
The most common pieces you'll use are likely
ActivitySource
and Meter
In most apps, you want just one of each, and to store it in a readily accessible area
JUST LIKE how you might have seen HttpContext.Current
before as a way to access the current HTTP request used anywhere in the business logic of ASP.NET apps...
We can do the same with Activity.Current
It's "not bad" because observability is a cross-cutting concept upon which you don't build business logic, therefore should be trivial to access
The "current" activity flows across your application because it's backed by an underlying AsyncLocal<Activity>
So when Avalonia starts the RaisingRoutedEvent activity for you, always capable of referencing that activity.
If they're nice, they'll have added some tags (which show up as custom dimension in app insights) indicating the name of the clicked object and perhaps the name of the invoked handler method
When you call StartActivity
, it automagically knows to associate it with the parent activity as-needed.Gotcha, that's what I was wondering about
It's a tough sell because we've been trained for years to inject and pass parameters
And now we're being told that observability "just works" like magic.
I'm a fan of it because of the simplicity, but it's going to be hard to disambiguate good vs poor usages of static access for my team.
Generally speaking, as a first pass for observability, less is more.
Ensure your activities are starting and automatically wired up for you on event handlers
Ensure when you log something, the logs are associated to a trace ID, and accessible via the app insights transcation ID search
Start with that.
Then as you get a feel for it, start tagging activites with extra bits of data as you load them.
Perhaps augment the global event handler in Avalonia with a tag that indicates who the currently authenticated user is
Unknown User•4w ago
Message Not Public
Sign In & Join Server To View
Even though I see that Avalonia is supposed to call StartActivity internally when a Button is clicked / when it's internal routed event is triggered, I don't see any traces being created when this happens. So not really sure what's going on with that. I have called AddSource("Avalonia.Diagnostic.Source") when building the tracer provider, but it doesn't seem to register.
Actually: I think I found what the issue is after more digging
AppContext needs to be set to true for this to be enabled in their static constructor
AppContext.TryGetSwitch("Avalonia.Diagnostics.Diagnostic.IsEnabled", out var isEnabled) && isEnabled;
Ok, after setting RuntimeConfig Avalonia.Diagnostics.Diagnostic.IsEnabled
, that enabled it


Unknown User•4w ago
Message Not Public
Sign In & Join Server To View
Yeah, I just saw it after reading through the Diagnostic class here, then I found their old pull request https://github.com/AvaloniaUI/Avalonia/pull/18314 where they talked about how they implemented it with a RuntimeConfig check
<ItemGroup>
<RuntimeHostConfigurationOption Include="Avalonia.Diagnostics.Diagnostic.IsEnabled" Value="true" />
</ItemGroup>
GitHub
Add diagnostic metrics/activities by maxkatz6 · Pull Request #1831...
What does the pull request do?
Histogram metrics
avalonia.comp.render.time
avalonia.comp.update.time
avalonia.ui.measure.time
avalonia.ui.arrange.time
avalonia.ui.render.time
avalonia.ui.input.tim...
