Using Shared Logging Levels with .NET Aspire
Overview
I recently was consolidating quite a few pieces of a microservices application into a single Aspire solution (which I highly recommend, by the way, and if you need guidance for sure check out the official docs but I also have a Pluralsight course that can help).
While doing the setup, I came up with a technique to define some shared logging levels that can be applied to your ASP.NET Core projects that I found pretty helpful and wanted to share it here in this short post.
A sample of code that shows the approach in this article is in the GitHub repo tied to my Pluralsight course -- it uses the Serilog-based approach for environment variables.
Logging Levels in ASP.NET
For ASP.NET applications, you often configure log levels for applications with values
in an appsettings.json
file with content that might look like this:
1"Logging": {
2 "LogLevel": {
3 "Default": "Information",
4 "System": "Information",
5 "Microsoft": "Warning",
6 "Microsoft.Hosting": "Information",
7 "Duende": "Warning"
8 }
9}
In Serilog, the same settings are possible but the appsettings.json
syntax is a little
different:
1"Serilog": {
2 "MinimumLevel": {
3 "Default": "Information",
4 "Override": {
5 "System": "Warning",
6 "Microsoft": "Warning",
7 "Microsoft.Hosting": "Information",
8 "Duende": "Warning"
9 }
10 }
11}
This configuration exists for each of your ASP.NET applications - and if you have a microservices-based one like I did, this might be 10 or more different applications.
If you need to use different log levels, like more Debug
levels, you would end
up needing to change content in many different appsettings.json
files.
AppHost Modification in .NET Aspire
Two .NET features come to our assistance here:
- the chained / override feature where environment variables override appsettings files
- the ability to easily set environment variables for ASP.NET projects in the Aspire
AppHost
You can set an environment variable on a Project in the AppHost with code such as this:
1var api = builder.AddProject<Projects.CatalogApi>("catalog")
2 .WithEnvironment("SomeEnvVar", "some value");
The environment-variable based name for the logging level setup from above for the Default
log level would
be:
Logging__LogLevel__Default
Note the double-underscores that separate the levels of the json tree.
The Code - An Extension Method and Single-Line AppHost Updates Per Project
Based on the above, we can define an extension method that looks like this - and I've got this
defined in the AppHost
project:
1internal static class LoggingHelper
2{
3 internal static IResourceBuilder<T> WithSharedLoggingLevels<T>(this IResourceBuilder<T> builder)
4 where T : IResourceWithEnvironment
5 {
6 var dict = new Dictionary<string, string>
7 {
8 { "Default", "Information" },
9 { "System", "Warning" },
10 { "Microsoft", "Warning" },
11 { "Microsoft.Hosting", "Information" },
12 { "Duende": "Warning" }
13 };
14
15 foreach (var item in dict.Keys)
16 {
17 builder = builder.WithEnvironment($"Logging__LogLevel__{item}", dict[item]);
18 }
19 return builder;
20 }
21}
And then on any project we add to the AppHost
, we can simply invoke the method:
1var api = builder.AddProject<Projects.CatalogApi>("catalog")
2 .WithSharedLoggingLevels();
EF Core -- See Queries in Logs
If you add the following settings, you can see the queries that you're executing in the log entries:
1{ "Microsoft.EntityFrameworkCore.Database.Command", "Information" },
2{ "Microsoft.EntityFrameworkCore.Query", "Information" },
3{ "Microsoft.EntityFrameworkCore.Update", "Information" },
These will enable you to see the queries that EF Core executes the database even if the trace information does not include it.
Implications & Limitations
Implemented Approach: Only for Local Development
Because I implemented the extension method in the AppHost
project, it only applies to
local development. If you wanted this kind of behavior more generally in any deployed environment,
you could add a similar (but not the same) method in the ServiceDefaults
project and reference
shared configuration in some other way -- shared file, hard-coded values, or whatever (even a database!).
Overriding an Individual ASP.NET Project
The implemented approach here will use the shared logging level in the AppHost project
instead of any configured values in the local appsettings.json
file for the projects due
to the fact that environment variables take a higher priority in the default configuration
hierarchy.
The easiest way to set a custom level of logging for an individual ASP.NET project in your
Aspire solution is to simply comment out the WithSharedLoggingLevels()
call for that
project, and then update the appsettings.json
file for the project with whatever settings
you need.