Table of Contents

Watching

Please find this dedicated tutorial video on YouTube about how to configure logging with Apizr:

Configuring logging

You can adjust logging configuration with:

  • httpTracerMode (default: Everything) Http traffic tracing mode:
    • ExceptionsOnly logs only when an exception occurs
    • ErrorsAndExceptionsOnly logs only when an exception or any error occurs
    • Everything logs all, anytime
  • trafficVerbosity (default: All) Http traffic tracing verbosity (see HttpTracer), with:
    • None
    • RequestBody
    • RequestHeaders
    • RequestCookies
    • RequestAllButBody = RequestHeaders | RequestCookies,
    • RequestAll = RequestBody | RequestHeaders | RequestCookies
    • ResponseBody
    • ResponseHeaders
    • ResponseAll = ResponseBody | ResponseHeaders
    • HeadersOnly = ResponseHeaders | RequestHeaders,
    • AllButRequestBody = RequestAllButBody | ResponseAll,
    • AllButResponseBody = RequestAll | ResponseHeaders,
    • AllButBodies = ResponseAll | RequestAllButBody,
    • All = ResponseAll | RequestAll
  • logLevels (default: [Low] Trace, [Medium] Information and [High] Critical) Log levels to apply while writing logs (see Microsoft.Enxtension.Logging), with:
    • Trace
    • Debug
    • Information
    • Warning
    • Error
    • Critical
    • None

Note that parameter logLevels is an array. It lets you provide from 0 to 3 different levels, as Apizr needs to get corresponding log level to each internal severity:

  • Low: logs any internal and normal execution step
  • Medium: logs all missconfigured things, like asking for cache without providing any cache provider
  • High: logs errors and exceptions

Obviously, providing more than 3 log levels would be pointless.

It means that:

  • if you don't provide any log level at all, default levels will be applied ([Low] Trace, [Medium] Information and [High] Critical)
  • if you provide only 1 log level like Information, it will be applied to all log entries ([Low] Information, [Medium] Information and [High] Information). Up to you to catch exceptions and to log it at any level of your choice.
  • if you provide only 2 log levels like Debug and Error, the lowest will be applied to both Low and Medium ([Low] Debug, [Medium] Debug and [High] Error)
  • if you provide 3 log levels like Debug, Warning and Critical, it will be applied like you said ([Low] Debug, [Medium] Warning and [High] Critical)
  • if you provide more than 3 log levels, the lowest goes to Low, the highest to High and it will take the middle one for Medium
  • if you provide a None at some point, it will disable logging for corresponding severity

You can configure logging at:

  • Design time by attribute decoration
  • Register time by fluent configuration
  • Request time by fluent configuration.

You can set logging configuration at design time, decorating with the provided Log attribute. Configuring logging with attribute allows you to use assembly scanning auto registration feature.

The Log attribute could decorate:

  • Assembly: to set logging configuration to all assembly api interfaces/entities
  • Interface/Class: to set logging configuration to all request methods of the decorated api interface/entity
  • Method: to set logging configuration to a specific request method of an api interface or entity (with dedicated attribtes)

You also can mix decoration levels to set a common logging configuration to all assembly api interfaces, and/or a specific to all api interface methods, and/or a specific to an api interface method.

[assembly:Log]
namespace Apizr.Sample
{
    [BaseAddress("https://reqres.in/"), Log(HttpMessageParts.RequestAll, 
        HttpTracerMode.ErrorsAndExceptionsOnly, 
        LogLevel.Information)]
    public interface IReqResService
    {
        [Get("/api/users"), Log(HttpMessageParts.RequestBody, 
            HttpTracerMode.ExceptionsOnly, 
            LogLevel.Warning)]
        Task<UserList> GetUsersAsync();

        [Get("/api/users/{userId}")]
        Task<UserDetails> GetUserAsync([CacheKey] int userId);

        [Post("/api/users")]
        Task<User> CreateUser(User user);
    }
}

In this classic api example, we decided to apply the default logging configuration ([Low] Trace, [Medium] Information and [High] Critical) to all assembly api interfaces/entities. Then some custom log settings about this specific api.

You’ll find some more log attributes but dedicated to CRUD apis (the ones ending with Read, ReadAll, Create, Update or Delete suffix), so you could define log settings at any level for CRUD apis too.

Here is CRUD api an example:

[assembly:Log]
namespace Apizr.Sample.Models
{
    [BaseAddress("https://reqres.in/api/users")]
    [LogReadAll(HttpMessageParts.RequestAll, 
        HttpTracerMode.ErrorsAndExceptionsOnly, 
        LogLevel.Information)]
    [LogRead(HttpMessageParts.AllButBodies, 
        HttpTracerMode.ExceptionsOnly, 
        LogLevel.Debug)]
    public record User
    {
        [JsonPropertyName("id")]
        public int Id { get; init; }

        [JsonPropertyName("first_name")]
        public string FirstName { get; init; }

        [JsonPropertyName("last_name")]
        public string LastName { get; init; }

        [JsonPropertyName("avatar")]
        public string Avatar { get; init; }

        [JsonPropertyName("email")]
        public string Email { get; init; }
    }
}

Again, in this CRUD api example, we decided to apply the default logging configuration ([Low] Trace, [Medium] Information and [High] Critical) to all assembly api interfaces/entities. Then some custom log settings about this specific api.

Note that you can mix design, register and request time logging configurations. In case of mixed configurations, the internal duplicate strategy will be to take the closest one to the request.

Logging configuration duplicate strategy order:

  • take fluent request configuration first if defined (request options)
  • otherwise the request attribute decoration one (method)
  • otherwise the fluent proper resgistration one (api proper options)
  • otherwise the api attribute decoration one (interface)
  • otherwise the fluent common resgistration one (registry common options)
  • otherwise the global attribute decoration one (assembly)

Redacting

You may want to hide some header sensitive data from logs.

You can set which header values should be redacted before logging with this option:

// direct configuration
options => options.WithLoggedHeadersRedactionNames(new[]{ "MyHeaderKey" })

// factory configuration
options => options.WithLoggedHeadersRedactionRule(header => header == "MyHeaderKey")

From there, you should see your logs redacted like so:

 ==================== HTTP REQUEST: [GET] ==================== 
GET https://reqres.in/api/users
Request Headers:
MyHeaderKey: *

Note that you can mix register and request time redaction configurations. Also, the ApizrDuplicateStrategy optional parameter let you tell Apizr whether to override or not any parent redaction rules.