Conversation
|
Azure Static Web Apps: Your stage site is ready! Visit it here: https://delightful-moss-0c5b8040f-3011.eastus2.azurestaticapps.net |
jonpryor
added a commit
to unoplatform/uno.templates
that referenced
this pull request
Jan 13, 2026
Context: unoplatform/uno.extensions#3008 Context: unoplatform/uno.extensions#3011 `dotnet new unoapp` templates have two patterns for `ILogger` configuration: 1. The *recommended* pattern, which uses Dependency Injection (DI) via [`.UseLogging()`][0]: dotnet new unoapp -preset "recommended" 2. The "No Dependency Injection" (NoDI) pattern, which *doesn't* use Dependency Injection, and instead has an [`App.InitializeLogging()`][1] method called from [`Main()`][2]: dotnet new unoapp -di False The wonderful thing is, those log *different*, non-overlapping, things. The Dependency Injection pattern provides an `ILogger` instance to constructors of types registered with e.g. [`services.AddSingleton()`][3]: services.AddSingleton<IMyService, MyService>() // … interface IMyService; class MyService : IMyService { public MyService(ILogger<MyService> logger) => …; } The "No Dependency Injection" pattern provides `ILogger` instances from the [`Log<T>(T)` extension method][4], which is used extensively within Uno itself, e.g. // https://github.com/unoplatform/uno/blob/77c6ca0533a63be48c6bbdd830c8653e17a9b0fa/src/Uno.UI.Dispatching/Native/NativeDispatcher.cs#L146 dispatcher.Log().Error("NativeDispatcher unhandled exception", exception); Using only DI logging initialization will miss NoDI messages. Using only NoDI logging initialization will miss DI messages. In order to capture *all* `ILogger`-originated messages, *both* DI and NoDI log initialization patterns must be used, which was *not* done by *any* `dotnet new unoapp` templates before this change. Additionally, DI-based logging is *optional* -- it can be disabled via `dotnet new unoapp -di False …` -- and thus its presence cannot necessarily be relied upon. unoplatform/uno.extensions#3011 updates `HostBuilder.Build()` so that *if* `App.InitializeLogging()` registers an `ILoggerFactory`, that instance will be used *by default* by DI-based logging, *even if* `.UseLogging()` is not used. Update uno.templates to finish the harmonization: 1. *Always* emit an `App.InitializeLogging()` method, and call it from `Main()`.` 2. *Refactor* `App.InitializeLogging()` and the `.UseLogging()` callback so that both call a new `App.ConfigureLogging()` method. `ConfigureLogging()` is responsible for configuringn the `ILoggingBuilder` instance, and this setup allows both `App.InitializeLogging()` and `.UseLogging()` to share the same code, centralizing `ILogger` configuration. This should work even without unoplatform/uno.extensions#3011, ensuring that *all* `ILogger`-originated messages are logged, *so long as* DI-based logging is used. (This adds NoDI logging to the DI template.) That said, it is preferred that unoplatform/uno.extensions#3011 exist *first*, so that even if `.UseLogging()` isn't used, DI-based logging will still generate log messages. [0]: https://github.com/unoplatform/uno.templates/blob/8988cac5efc444de0afeb400da1da24620b5c083/src/Uno.Templates/content/unoapp/MyExtensionsApp.1/App.recommended.cs#L81-L112 [1]: https://github.com/unoplatform/uno.templates/blob/8988cac5efc444de0afeb400da1da24620b5c083/src/Uno.Templates/content/unoapp/MyExtensionsApp.1/App.blank.cs#L115-L182 [2]: https://github.com/unoplatform/uno.templates/blob/8988cac5efc444de0afeb400da1da24620b5c083/src/Uno.Templates/content/unoapp/MyExtensionsApp.1/Platforms/Desktop/Program.cs#L10-L14 [3]: https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.servicecollectionserviceextensions.addsingleton?view=net-10.0-pp [4]: https://platform.uno/docs/articles/logging.html#uno-logging-extensions
jonpryor
added a commit
to unoplatform/uno.templates
that referenced
this pull request
Jan 13, 2026
Context: unoplatform/uno.extensions#3008 Context: unoplatform/uno.extensions#3011 `dotnet new unoapp` templates have two patterns for `ILogger` configuration: 1. The *recommended* pattern, which uses Dependency Injection (DI) via [`.UseLogging()`][0]: dotnet new unoapp -preset "recommended" 2. The "No Dependency Injection" (NoDI) pattern, which *doesn't* use Dependency Injection, and instead has an [`App.InitializeLogging()`][1] method called from [`Main()`][2]: dotnet new unoapp -di False The wonderful thing is, those log *different*, non-overlapping, things. The Dependency Injection pattern provides an `ILogger` instance to constructors of types registered with e.g. [`services.AddSingleton()`][3]: services.AddSingleton<IMyService, MyService>() // … interface IMyService; class MyService : IMyService { public MyService(ILogger<MyService> logger) => …; } The "No Dependency Injection" pattern provides `ILogger` instances from the [`Log<T>(T)` extension method][4], which is used extensively within Uno itself, e.g. // https://github.com/unoplatform/uno/blob/77c6ca0533a63be48c6bbdd830c8653e17a9b0fa/src/Uno.UI.Dispatching/Native/NativeDispatcher.cs#L146 dispatcher.Log().Error("NativeDispatcher unhandled exception", exception); Using only DI logging initialization will miss NoDI messages. Using only NoDI logging initialization will miss DI messages. In order to capture *all* `ILogger`-originated messages, *both* DI and NoDI log initialization patterns must be used, which was *not* done by *any* `dotnet new unoapp` templates before this change. Additionally, DI-based logging is *optional* -- it can be disabled via `dotnet new unoapp -di False …` -- and thus its presence cannot necessarily be relied upon. unoplatform/uno.extensions#3011 updates `HostBuilder.Build()` so that *if* `App.InitializeLogging()` registers an `ILoggerFactory`, that instance will be used *by default* by DI-based logging, *even if* `.UseLogging()` is not used. Update uno.templates to finish the harmonization: 1. *Always* emit an `App.InitializeLogging()` method, and call it from `Main()`.` 2. *Refactor* `App.InitializeLogging()` and the `.UseLogging()` callback so that both call a new `App.ConfigureLogging()` method. `ConfigureLogging()` is responsible for configuringn the `ILoggingBuilder` instance, and this setup allows both `App.InitializeLogging()` and `.UseLogging()` to share the same code, centralizing `ILogger` configuration. This should work even without unoplatform/uno.extensions#3011, ensuring that *all* `ILogger`-originated messages are logged, *so long as* DI-based logging is used. (This adds NoDI logging to the DI template.) That said, it is preferred that unoplatform/uno.extensions#3011 exist *first*, so that even if `.UseLogging()` isn't used, DI-based logging will still generate log messages. [0]: https://github.com/unoplatform/uno.templates/blob/8988cac5efc444de0afeb400da1da24620b5c083/src/Uno.Templates/content/unoapp/MyExtensionsApp.1/App.recommended.cs#L81-L112 [1]: https://github.com/unoplatform/uno.templates/blob/8988cac5efc444de0afeb400da1da24620b5c083/src/Uno.Templates/content/unoapp/MyExtensionsApp.1/App.blank.cs#L115-L182 [2]: https://github.com/unoplatform/uno.templates/blob/8988cac5efc444de0afeb400da1da24620b5c083/src/Uno.Templates/content/unoapp/MyExtensionsApp.1/Platforms/Desktop/Program.cs#L10-L14 [3]: https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.servicecollectionserviceextensions.addsingleton?view=net-10.0-pp [4]: https://platform.uno/docs/articles/logging.html#uno-logging-extensions
1 task
e890863 to
abb3fc9
Compare
|
Azure Static Web Apps: Your stage site is ready! Visit it here: https://delightful-moss-0c5b8040f-3011.eastus2.azurestaticapps.net |
jonpryor
added a commit
to unoplatform/uno.templates
that referenced
this pull request
Jan 13, 2026
Context: unoplatform/uno.extensions#3008 Context: unoplatform/uno.extensions#3011 `dotnet new unoapp` templates have two patterns for `ILogger` configuration: 1. The *recommended* pattern, which uses Dependency Injection (DI) via [`.UseLogging()`][0]: dotnet new unoapp -preset "recommended" 2. The "No Dependency Injection" (NoDI) pattern, which *doesn't* use Dependency Injection, and instead has an [`App.InitializeLogging()`][1] method called from [`Main()`][2]: dotnet new unoapp -di False The wonderful thing is, those log *different*, non-overlapping, things. The Dependency Injection pattern provides an `ILogger` instance to constructors of types registered with e.g. [`services.AddSingleton()`][3]: services.AddSingleton<IMyService, MyService>() // … interface IMyService; class MyService : IMyService { public MyService(ILogger<MyService> logger) => …; } The "No Dependency Injection" pattern provides `ILogger` instances from the [`Log<T>(T)` extension method][4], which is used extensively within Uno itself, e.g. // https://github.com/unoplatform/uno/blob/77c6ca0533a63be48c6bbdd830c8653e17a9b0fa/src/Uno.UI.Dispatching/Native/NativeDispatcher.cs#L146 dispatcher.Log().Error("NativeDispatcher unhandled exception", exception); Using only DI logging initialization will miss NoDI messages. Using only NoDI logging initialization will miss DI messages. In order to capture *all* `ILogger`-originated messages, *both* DI and NoDI log initialization patterns must be used, which was *not* done by *any* `dotnet new unoapp` templates before this change. Additionally, DI-based logging is *optional* -- it can be disabled via `dotnet new unoapp -di False …` -- and thus its presence cannot necessarily be relied upon. unoplatform/uno.extensions#3011 updates `HostBuilder.Build()` so that *if* `App.InitializeLogging()` registers an `ILoggerFactory`, that instance will be used *by default* by DI-based logging, *even if* `.UseLogging()` is not used. Update uno.templates to finish the harmonization: 1. *Always* emit an `App.InitializeLogging()` method, and call it from `Main()`.` 2. *Refactor* `App.InitializeLogging()` and the `.UseLogging()` callback so that both call a new `App.ConfigureLogging()` method. `ConfigureLogging()` is responsible for configuring the `ILoggingBuilder` instance, and this setup allows both `App.InitializeLogging()` and `.UseLogging()` to share the same code, centralizing `ILogger` configuration. This should work even without unoplatform/uno.extensions#3011, ensuring that *all* `ILogger`-originated messages are logged, *so long as* DI-based logging is used. (This adds NoDI logging to the DI template.) That said, it is preferred that unoplatform/uno.extensions#3011 exist *first*, so that even if `.UseLogging()` isn't used, DI-based logging will still generate log messages. [0]: https://github.com/unoplatform/uno.templates/blob/8988cac5efc444de0afeb400da1da24620b5c083/src/Uno.Templates/content/unoapp/MyExtensionsApp.1/App.recommended.cs#L81-L112 [1]: https://github.com/unoplatform/uno.templates/blob/8988cac5efc444de0afeb400da1da24620b5c083/src/Uno.Templates/content/unoapp/MyExtensionsApp.1/App.blank.cs#L115-L182 [2]: https://github.com/unoplatform/uno.templates/blob/8988cac5efc444de0afeb400da1da24620b5c083/src/Uno.Templates/content/unoapp/MyExtensionsApp.1/Platforms/Desktop/Program.cs#L10-L14 [3]: https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.servicecollectionserviceextensions.addsingleton?view=net-10.0-pp [4]: https://platform.uno/docs/articles/logging.html#uno-logging-extensions
jonpryor
added a commit
that referenced
this pull request
Jan 15, 2026
Context: #3008 Context: #3011 Context: unoplatform/uno.templates#1921 Issue #3008 details that there are two `ILogger` "environments" in a Uno app: * The `App.InitializeLogging()` environment ("NoDI"), and * The environment configured by Dependency Injection methods such as `.UseLogging()` and `.UseSerilog()` ("DI"). The problem is that "DI" logging *cannot* capture log messages produced before the DI environment is built, frequently via `builder.Build()` or `builder.NavigateAsync<TShell>()`. *A lot* of code can execute before those methods complete. A further problem is that no default template actually configures both environments: if you use `dotnet new unoapp -di False` you get the NoDI `App.InitializeLogging()` environment, which *will not* log messages from the Dependency Injection enviornment. If you use `dotnet new unoapp -preset "recommended"` you get *only* the DI-based logging, thus missing all messages from the NoDI environment. Thus, the idea for "`ILogger` configuration unification": What If instead of having two separate places for logging, we just had one: the NoDI `App.InitializeLogging()` location. This can be made to work via #3011, which updates `HostBuilder.Build()` so that *if* `LogExtensionPoint.AmbientLoggerFactory` is set, it *replaces* the `ILoggerFactory` that `HostBuilder.Build()` normally configures. This allows `App.InitializeLogging()` to also be used in the DI environment. So far, so useful. But what about [Serilog][0]? Serilog is a widely recommended logger library, and the current `.UseSerilog()` extension method *requires* a full Dependency Injection environment: it cannot be invoked within `App.InitializeLogging()`. Try to square this circle by adding a new `ILoggingBuilder.AddSerilog()` extension method. This would allow Serilog to be configured within `App.InitializeLogging()`, helping to support a unified logging setup experience. [0]: https://platform.uno/docs/articles/external/uno.extensions/doc/Overview/Logging/LoggingOverview.html#serilog
7 tasks
Fixes: #3008 Context: unoplatform/uno.templates#1921 `dotnet new unoapp` templates have two patterns for `ILogger` configuration: 1. The *recommended* pattern, which uses Dependency Injection (DI) via [`.UseLogging()`][0]: dotnet new unoapp -preset "recommended" 2. The "No Dependency Injection" (NoDI) pattern, which *doesn't* use Dependency Injection, and instead has an [`App.InitializeLogging()`][1] method called from [`Main()`][2]: dotnet new unoapp -di False The wonderful thing is, those log *different*, non-overlapping, things. The Dependency Injection pattern provides an `ILogger` instance to constructors of types registered with e.g. [`services.AddSingleton()`][3]: services.AddSingleton<IMyService, MyService>() // … interface IMyService; class MyService : IMyService { public MyService(ILogger<MyService> logger) => …; } The "No Dependency Injection" pattern provides `ILogger` instances from the [`Log<T>(this T)` extension method][4], which is used extensively within Uno itself, e.g. // https://github.com/unoplatform/uno/blob/77c6ca0533a63be48c6bbdd830c8653e17a9b0fa/src/Uno.UI.Dispatching/Native/NativeDispatcher.cs#L146 dispatcher.Log().Error("NativeDispatcher unhandled exception", exception); Using only DI logging initialization will miss NoDI messages. Using only NoDI logging initialization will miss DI messages. In order to capture *all* `ILogger`-originated messages, *both* DI and NoDI log initialization patterns must be used, which is *not* done by *any* `dotnet new unoapp` templates. Additionally, DI-based logging is *optional* -- it can be disabled via `dotnet new unoapp -di False …` -- and thus its presence cannot necessarily be relied upon. Attempt to begin improving this scenario by updating `HostBuilder.CreateServiceProvider()` (via `HostBuilder.Build()`) to [Replace][5] the `ILoggerFactory` registration added by `.AddLogging()` with the `ILoggerFactory` instance stored by `Uno.Extensions.LogExtensionPoint.AmbientLoggerFactory`. This means that *if* NoDI init is performed *before* `HostBuilder.Build()` is invoked, then the `ILoggerFactory` instance created as part of NoDI init is also registered with `IHost.Services`. This in turn means that if NoDI init is performed *and not* DI init, registered services will obtain an `ILogger` instance which follows the NoDI init configuration. Meaning that *only* NoDI init will be required to see *both* NoDI *and* DI-originating messages. Additionally, if both NoDI and DI init is performed, DI init will log a warning message indicating that the `AmbientLoggerFactory` is used. [0]: https://github.com/unoplatform/uno.templates/blob/8988cac5efc444de0afeb400da1da24620b5c083/src/Uno.Templates/content/unoapp/MyExtensionsApp.1/App.recommended.cs#L81-L112 [1]: https://github.com/unoplatform/uno.templates/blob/8988cac5efc444de0afeb400da1da24620b5c083/src/Uno.Templates/content/unoapp/MyExtensionsApp.1/App.blank.cs#L115-L182 [2]: https://github.com/unoplatform/uno.templates/blob/8988cac5efc444de0afeb400da1da24620b5c083/src/Uno.Templates/content/unoapp/MyExtensionsApp.1/Platforms/Desktop/Program.cs#L10-L14 [3]: https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.servicecollectionserviceextensions.addsingleton?view=net-10.0-pp [4]: https://platform.uno/docs/articles/logging.html#uno-logging-extensions [5]: https://learn.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.extensions.servicecollectiondescriptorextensions.replace?view=net-10.0-pp#microsoft-extensions-dependencyinjection-extensions-servicecollectiondescriptorextensions-replace(microsoft-extensions-dependencyinjection-iservicecollection-microsoft-extensions-dependencyinjection-servicedescriptor)
abb3fc9 to
d159348
Compare
Contributor
Author
|
I'm increasingly convinced that this approach isn't viable, and will require too many changes to the ecosystem to be viable. See also: #3008 (comment) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes: #3008
Context: unoplatform/uno.templates#1921
dotnet new unoapptemplates have two patterns forILoggerconfiguration:The recommended pattern, which uses Dependency Injection (DI) via
.UseLogging():The "No Dependency Injection" (NoDI) pattern, which doesn't use Dependency Injection, and instead has an
App.InitializeLogging()method called fromMain():The wonderful thing is, those log different, non-overlapping, things.
The Dependency Injection pattern provides an
ILoggerinstance to constructors of types registered with e.g.services.AddSingleton():The "No Dependency Injection" pattern provides
ILoggerinstances from theLog<T>(T)extension method, which is used extensively within Uno itself, e.g.Using only DI logging initialization will miss NoDI messages.
Using only NoDI logging initialization will miss DI messages.
In order to capture all
ILogger-originated messages, both DI and NoDI log initialization patterns must be used, which is not done by anydotnet new unoapptemplates.Additionally, DI-based logging is optional -- it can be disabled via
dotnet new unoapp -di False …-- and thus its presence cannot necessarily be relied upon.Attempt to begin improving this scenario by updating
HostBuilder.CreateServiceProvider()(viaHostBuilder.Build()) to Replace theILoggerFactoryregistration added by.AddLogging()with theILoggerFactoryinstance stored byUno.Extensions.LogExtensionPoint.AmbientLoggerFactory.This means that if NoDI init is performed before
HostBuilder.Build()is invoked, then theILoggerFactoryinstance created as part of NoDI init is also registered withIHost.Services. This in turn means that if NoDI init is performed and not DI init, registered services will obtain anILoggerinstance which follows the NoDI init configuration.Meaning that only NoDI init will be required to see both NoDI and DI-originating messages.
GitHub Issue (If applicable): closes #
PR Type
What kind of change does this PR introduce?
What is the current behavior?
What is the new behavior?
PR Checklist
Please check if your PR fulfills the following requirements:
Screenshots Compare Test Runresults.Other information
Internal Issue (If applicable):