Episode 03 of an Apizr walkthrough building a MAUI app requesting a backend API.
Apizr is a Refit based web api client manager, but resilient (retry, connectivity, cache, auth, log, priority, etc…).
This episode is about configuring logging.
APIZR
If you’re new to Apizr, you shoud know that it’s based on Refit and aims to provide many more features on top of it, like retry handling, connectivity check, cache management, authentication handling, priority management, and so on… It’s here to help me to don’t repeat myself into each and every projects while dealing with api request calls and resilience needs.
Get the big picture by reading the documentation:
Feel free to browse code, tests and samples and maybe to submit PRs or ideas:
Don’t forget the YouTube Channel Playlist about Apizr:
The starcellar series
The StarCellar walkthrough series is built thanks to multiple episodes, each with a dedicated source code branch, blog post and playlist video.
Feel free to fork the project and start from the main branch, so that you could follow this tutorial coding by yourself. If you do so, don’t forget to run the API through a Dev Tunnel and to update the MAUI app’s base address.
the starcellar episode 03
The current episode is covered by this video:
Configuring logging Without Apizr
First, the Without.Apizr project as usual!
Start by referencing NuGet packages:
-
HttpTracer
Now open the MauiProgram class and scroll to the Refit registration part.
There, add the following RefitSettings parameter to both RefitClient registrations:
new RefitSettings { HttpMessageHandlerFactory = () => new HttpTracerHandler { Verbosity = HttpMessageParts.All } }
Here we provide the HttpTracerHandler to Refit by setting its HttpMessageHandler.
HttpTracerHandler will trace http traces with a verbosity set to All.
You can run the app and see that there’s now some http traces right into the VS output.
You should see something like that:
Configuring logging With Apizr
Now the With.Apizr project!
Nothing more to install, just decorate APIs by the Log attribute provided by Apizr like:
[BaseAddress("/wines"), Log] public interface ICellarApi { [Get("")] Task<IEnumerable<Wine>> GetWinesAsync(); [Get("/{id}")] Task<Wine> GetWineDetailsAsync(Guid id); [Post("")] Task<Wine> CreateWineAsync(Wine item); [Put("/{id}")] Task UpdateWineAsync(Guid id, Wine item); [Delete("/{id}")] Task DeleteWineAsync(Guid id); }
Here we tell Apizr to log all traces by default thanks to attribute design.
You can run the app and see that there’s now some http traces right into the VS output.
Adjusting logging options With Apizr
When you don’t say anything but just Log like in previous API design, Apizr will use following default logging options:
HttpTracerMode
:Everything
(will log everything anytime)TrafficVerbosity
:All
(will log all http traces)LogLevels
: [Low]Trace
, [Medium]
and [High]Information
Critical
(log levels to use while writing logs)
You definitly can ajust logging options instead of using default ones.
Here is the same API with more custom options:
[assembly:Log] namespace StarCellar.With.Apizr.Services.Apis.Cellar { [BaseAddress("/wines"), Log(HttpMessageParts.RequestAll, HttpTracerMode.ErrorsAndExceptionsOnly, LogLevel.Information)] public interface ICellarApi { [Get(""), Log(HttpMessageParts.RequestBody, HttpTracerMode.ExceptionsOnly, LogLevel.Warning)] Task<IEnumerable<Wine>> GetWinesAsync(); [Get("/{id}")] Task<Wine> GetWineDetailsAsync(Guid id); [Post("")] Task<Wine> CreateWineAsync(Wine item); [Put("/{id}")] Task UpdateWineAsync(Guid id, Wine item); [Delete("/{id}")] Task DeleteWineAsync(Guid id); } }
There, you can see that we decided to apply the default logging configuration to all assembly APIs, but a custom one to ICellarApi
specific API and all its requests, but a custom one to GetWinesAsync
specific request.
You can run the app and see that everything’s logging as asked to.
Understanding Apizr logging configuration pipeline
Previously, we configured logging options by design, decorating attribute at different levels:
- APIs Assembly: will be shared by all APIs in the assembly
- API interface: will be set to decorated API only, overriding parent logging options
- Request method: will be set to decorated request only, overriding parent logging options
But we can do the same fluently at registration and request time (with ApizrRequestOptions parameter) like:
// Registering builder.Services.AddApizr( registry => registry .AddManagerFor<ICellarApi>(options => options .WithLogging(HttpMessageParts.RequestAll, HttpTracerMode.ErrorsAndExceptionsOnly, LogLevel.Information)) .AddManagerFor<IFileApi>(), options => options .WithLogging()); ... // Requesting var wines = await _cellarApiManager.ExecuteAsync((api, opt) => api.GetWinesAsync(opt), options => options.WithLogging(HttpMessageParts.RequestBody, HttpTracerMode.ExceptionsOnly, LogLevel.Warning));
Actually, we even can mix it all together, I mean Design, Register and Request time configurations.
Just keep in mind that fluent configuration always wins over attribute one, and the closest configuration to the request always wins over all others.
Redacting logged headers With Apizr
While logging http traces, you may have to respect some privacy or security policies, like hiding sensitive headers.
Apizr lets you do that by design using a star * symbol:
[BaseAddress("/wines"), Log] public interface ICellarApi { [Get(""), Headers("MyHeaderKey: *MyHeaderValue*")] Task<IEnumerable<Wine>> GetWinesAsync(); ... }
Or fluently thanks to the following options:
// direct header configuration options => options.WithHeaders(["MyHeaderKey: *MyHeaderValue*"]) // OR direct redacting configuration options => options.WithLoggedHeadersRedactionNames(new[]{ "MyHeaderKey" }) // OR factory redacting configuration options => options.WithLoggedHeadersRedactionRule(header => header == "MyHeaderKey")
From there, you should see your logs with headers redacted like so:
==================== HTTP REQUEST: [GET] ==================== GET https://mycustomurl.com/path Request Headers: MyHeaderKey: *
Running the app
Nothing’s changed on the user experience point of view. The same app, but with some options adjusted and shared thanks to Apizr:
get more of it
Feel free to ask me anything on twitter, or opening a discussion, issue or PR on GitHub.
Again, it’s basically all built for my own use and motivated by my needs. But it’s live on Nuget and opened to all so feel free to contribute.
Apizr brings many more features so you should head to the next episode to continue your walkthrough path.