-
-
Notifications
You must be signed in to change notification settings - Fork 222
MAUI CommunityToolkit MVVM auto instrumentation of async commands #4125
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
Conversation
src/Sentry.Maui.CommunityToolkitMvvm/Sentry.Maui.CommunityToolkitMvvm.csproj
Outdated
Show resolved
Hide resolved
src/Sentry.Maui.CommunityToolkitMvvm/CtMvvmMauiElementEventBinder.cs
Outdated
Show resolved
Hide resolved
src/Sentry.Maui.CommunityToolkitMvvm/CtMvvmMauiElementEventBinder.cs
Outdated
Show resolved
Hide resolved
src/Sentry.Maui.CommunityToolkitMvvm/CtMvvmMauiElementEventBinder.cs
Outdated
Show resolved
Hide resolved
src/Sentry.Maui.CommunityToolkitMvvm/CtMvvmMauiElementEventBinder.cs
Outdated
Show resolved
Hide resolved
src/Sentry.Maui.CommunityToolkitMvvm/MauiAppBuilderExtensions.cs
Outdated
Show resolved
Hide resolved
This is quite popular: https://nugettrends.com/packages?ids=CommunityToolkit.Mvvm&months=24 Worth adding it in ![]() |
…try/sentry-dotnet into maui_gestures_breadcrumbs
src/Sentry.Maui.CommunityToolkit.Mvvm/MauiCommunityToolkitMvvmEventsBinder.cs
Outdated
Show resolved
Hide resolved
test/Sentry.Maui.Tests/MauiEventsBinderTests.CtMvvmAsyncRelayCommands.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
worth profiling the Init code just to see if building the container has any significant bottleneck (or anything else we added there).
But outside of that,lgtm
People using MAUI tend to change the container library. It is extremely common. You want to profile all of them? |
docs on usage please? |
Better yet, how do I use this in my Avalonia app? I use CommunityToolkit MVVM too. I just can't directly consume your Sentry.Maui.CommunityToolkit package. |
Wow - I think you must have asked for this at the precise moment this new integration was released 😄 There aren't any docs yet but there's an example of how to enable the integration in Sentry.Samples.Maui: sentry-dotnet/samples/Sentry.Samples.Maui/MauiProgram.cs Lines 29 to 35 in 34d8490
Once enabled, the new integration automatically creates traces for any MVVM AsyncRelayCommands in the application. This new integration is pretty minimal at the moment but we're hoping to get feedback from SDK about it's usefulness and whether/how it might be extended or tweaked to be even better. |
@BrycensRanch this particular integration is MAUI specific. It leverages event hooks in the Microsoft.Maui.Controls.Application class to detect when an AsynReplayCommand starts or stops and start/finish a tracing span that represents that relay command. I think Avalonia would require a separate solution. |
Yes I did ask the question right after I have received notification of a new release 😄 . Just to be clear -
|
Instructions and example for changelogPlease add an entry to Example: ## Unreleased
- MAUI CommunityToolkit MVVM auto instrumentation of async commands ([#4125](https://github.com/getsentry/sentry-dotnet/pull/4125)) If none of the above apply, you can opt out of this check by adding |
Yes, indeed. That's where that extension on the SentryOptions is defined. Sorry, I should have mentioned that.
No, the comments on potential performance impact relate to code changes in the I pushed up a branch containing those benchmarks. I had to break the software a bit in order to build them, so we'll never merge that branch but it means other folks can run the benchmarks themselves to reproduce the results... so we can make decisions based on data rather than strong opinions. Data tends to argue less 😜 @Redth @aritchie @bruno-garcia on the back of the benchmarks, it looks like we could shave 10 microseconds (that's millionths of a second) off startup by creating the config options and invoking the callback ourselves. So we could do that in another PR. It would be a trivial change. |
That could be potentially dealbreaker as enterprises devices like Zebra and Honeywell (most of our target audience) tend to run on archaic chips with low memory but I dont have an way of benchmarking them at the moment to compare Apple with Apple. |
@Syed-RI I spotted a potential issue with the benchmarks... I think a lot of what needed to be measured was hidden in the GlobalSetup method of the benchmark tests. I corrected this and re-ran them. The new benchmarks actually show the existing code/solution to be the fastest option Feel free to check the benchmarks out for yourself. Again, the difference between all of the three options is less than 12 millionths of a second, which on a machine that is 1000 times slower than mine would be around 10 milliseconds (not something a human being would perceive) btw: if you're concerned about memory, this change will have little to no impact. But this PR could be a huge win for you: |
Closing this particular PR for comments as the discussion is getting quite long (and off topic). If you've got any performance concerns, please add comments on the related Benchmarking PR instead (so that we can use data to drive the discussion): |
@jamescrosswell - you're benchmarks don't account for real world situations. The "strong opinions" isn't a fair comment. We're all professionals and some of us have lots of experience in different areas. So let me summarize some additional reasoning for why I'm 1000% against doing what this PR is doing
public static MauiApp CreateMauiApp() => MauiApp
.CreateBuilder()
.UseMauiApp<App>()
.ConfigureServices(x =>
{
// this method is about 40 lines longer, so just a sample here is fine
x.AddMauiBlazorWebView();
x.AddMudServices();
x.AddSingleton<ILogEnricher, Redact>();
x.AddSingleton(TimeProvider.System);
x.AddSingleton<SqliteConnection>();
x.AddJob(typeof(Redact));
x.AddBluetoothLE();
x.AddGps<Redact>();
x.AddGeofencing<Redact>();
x.AddHttpTransfers<Redact>();
#if DEBUG
x.AddBlazorWebViewDeveloperTools();
#endif
})
.UseShiny()
.UseBarcodeScanning()
.UseMauiCommunityToolkit()
.UseMauiCommunityToolkitMediaElement()
.UseMauiCommunityToolkitMarkup()
.UseBottomSheet()
.UseMauiCameraView()
.UseBiometricAuthentication()
.UseSettingsView()
.UsePrism(
new DryIocContainerExtension(), // different DI container because prism requires mutable containers
prism => prism.CreateWindow("redacted")
)
.UseMauiMaps()
.UseCardsView()
.AddRemoteConfigurationMaui("https://redacted")
.UseSentry(options =>
{
options.Dsn = "redacted";
options.TracesSampleRate = 1.0;
})
.ConfigureFonts(fonts =>
{
//.... redact
})
.Build();
CC: @bruno-garcia |
Actually, @aritchie I think we can address all of the concerns you have above by using the second method from the benchmark fork: sentry-dotnet/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs Lines 88 to 96 in 7f64d50
That way people still setup the new integration as an extension on I've created a new issue for this here: Should be trivial to implement. |
This is a POC to try to insert auto instrumentation of async operations that occur in commands. The MVVM Community Toolkit is insanely popular in .NET MAUI. This will be a key component to making #3181 awesome.
TODO
Performance considerations
There has been a lot of back and forth before merging this PR about the performance implications of building a service provider to resolve the options during service registration itself (since there's a bit of inception going on wiring up the MauiEventBinders on startup here).
Initially I (@jamescrosswell) created some benchmarks comparing two different ways to initialise the Maui integrations. Basically I temporarily changed the implementation of the UseSentry app builder extension to take an optional bool param and do this with it:
So if
resolveOptionsWithServiceProvider == true
we use the existing code from this PR to get the options... otherwise we use the "fake options" hack/workaround.Benchmark results
So the hack/alternative looked to be about 33% slower and allocate a fraction more memory.
As such, we left the original code in the PR (that builds a service provider to resolve the options during init).
Here's the code I used for the original benchmark:
After receiving further comments from @Redth I figured I'd put together some more comprehensive benchmarks:
The new benchmarks differ in two key respects
ShortJob
, so the results weren't as reliable as they could beResults of the new benchmarks are:
Curiously, this time the performance for options 2 and 3 are reversed (in the initial benchmark it was quicker to build the service provider than to create the options ourselves)... This is probably because we were running a
ShortJob
before, so the results weren't very reliable.This time round, the winner appears to be invoking the config options callback ourselves... although it allocates slightly more memory.
Overall, the difference between all 3 different techniques is less than 10 microseconds. So we could save 10 microseconds by creating the options and invoking the callback ourselves, rather than building a service provider to do this for us.