Marcin Jahn | Dev Notebook
  • Home
  • Programming
  • Technologies
  • Projects
  • About
  • Home
  • Programming
  • Technologies
  • Projects
  • About
  • An icon of the Core section Core
    • Programs Execution
    • Stack and Heap
    • Asynchronous Programming
      • Overview
      • Event Queues
      • Fibers
      • Stackless Coroutines
  • An icon of the .NET section .NET
    • HTTPClient
    • Async
      • How Async Works
      • TAP Tips
    • Equality
    • Comparisons
    • Enumerables
    • Unit Tests
    • Generic Host
    • Logging
    • Configuration
    • Records
    • Nullability
    • Garbage Collector
    • IL and Allocations
    • gRPC
    • Source Generators
    • Platform Invoke
    • ASP.NET Core
      • Overview
      • Middleware
      • Razor Pages
      • Routing in Razor Pages
      • Web APIs
      • Filters
      • Identity
      • Validation
      • Tips
    • Entity Framework Core
      • Overview
      • Testing
      • Tips
  • An icon of the Angular section Angular
    • Overview
    • Components
    • Directives
    • Services and DI
    • Routing
    • Observables (RxJS)
    • Forms
    • Pipes
    • HTTP
    • Modules
    • NgRx
    • Angular Universal
    • Tips
    • Standalone Components
  • An icon of the JavaScript section JavaScript
    • OOP
    • JavaScript - The Weird Parts
    • JS Functions
    • ES Modules
    • Node.js
    • Axios Tips
    • TypeScript
      • TypeScript Environment Setup
      • TypeScript Tips
    • React
      • React Routing
      • MobX
    • Advanced Vue.js Features
  • An icon of the Rust section Rust
    • Overview
    • Cargo
    • Basics
    • Ownership
    • Structures
    • Enums
    • Organization
    • Collections
    • Error Handling
    • Generics
    • Traits
    • Lifetimes
    • Closures
    • Raw Pointers
    • Smart Pointers
    • Concurrency
    • Testing
    • Tips
  • An icon of the C/C++ section C/C++
    • Compilation
    • Structures
    • OOP in C
    • Pointers
    • Strings
    • Dynamic Memory
    • argc and argv Visualization
  • An icon of the GTK section GTK
    • Overview
    • GObject
    • GJS
  • An icon of the CSS section CSS
    • Responsive Design
    • CSS Tips
    • CSS Pixel
  • An icon of the Unity section Unity
    • Unity
  • An icon of the Functional Programming section Functional Programming
    • Fundamentals of Functional Programming
    • .NET Functional Features
    • Signatures
    • Function Composition
    • Error Handling
    • Partial Application
    • Modularity
    • Category Theory
      • Overview
      • Monoid
      • Other Magmas
      • Functors
  • An icon of the Algorithms section Algorithms
    • Big O Notation
    • Array
    • Linked List
    • Queue
    • Hash Table and Set
    • Tree
    • Sorting
    • Searching
  • An icon of the Architecture section Architecture
    • What is architecture?
    • Domain-Driven Design
    • ASP.NET Core Projects
  • An icon of the Core section Core
    • Programs Execution
    • Stack and Heap
    • Asynchronous Programming
      • Overview
      • Event Queues
      • Fibers
      • Stackless Coroutines
  • An icon of the .NET section .NET
    • HTTPClient
    • Async
      • How Async Works
      • TAP Tips
    • Equality
    • Comparisons
    • Enumerables
    • Unit Tests
    • Generic Host
    • Logging
    • Configuration
    • Records
    • Nullability
    • Garbage Collector
    • IL and Allocations
    • gRPC
    • Source Generators
    • Platform Invoke
    • ASP.NET Core
      • Overview
      • Middleware
      • Razor Pages
      • Routing in Razor Pages
      • Web APIs
      • Filters
      • Identity
      • Validation
      • Tips
    • Entity Framework Core
      • Overview
      • Testing
      • Tips
  • An icon of the Angular section Angular
    • Overview
    • Components
    • Directives
    • Services and DI
    • Routing
    • Observables (RxJS)
    • Forms
    • Pipes
    • HTTP
    • Modules
    • NgRx
    • Angular Universal
    • Tips
    • Standalone Components
  • An icon of the JavaScript section JavaScript
    • OOP
    • JavaScript - The Weird Parts
    • JS Functions
    • ES Modules
    • Node.js
    • Axios Tips
    • TypeScript
      • TypeScript Environment Setup
      • TypeScript Tips
    • React
      • React Routing
      • MobX
    • Advanced Vue.js Features
  • An icon of the Rust section Rust
    • Overview
    • Cargo
    • Basics
    • Ownership
    • Structures
    • Enums
    • Organization
    • Collections
    • Error Handling
    • Generics
    • Traits
    • Lifetimes
    • Closures
    • Raw Pointers
    • Smart Pointers
    • Concurrency
    • Testing
    • Tips
  • An icon of the C/C++ section C/C++
    • Compilation
    • Structures
    • OOP in C
    • Pointers
    • Strings
    • Dynamic Memory
    • argc and argv Visualization
  • An icon of the GTK section GTK
    • Overview
    • GObject
    • GJS
  • An icon of the CSS section CSS
    • Responsive Design
    • CSS Tips
    • CSS Pixel
  • An icon of the Unity section Unity
    • Unity
  • An icon of the Functional Programming section Functional Programming
    • Fundamentals of Functional Programming
    • .NET Functional Features
    • Signatures
    • Function Composition
    • Error Handling
    • Partial Application
    • Modularity
    • Category Theory
      • Overview
      • Monoid
      • Other Magmas
      • Functors
  • An icon of the Algorithms section Algorithms
    • Big O Notation
    • Array
    • Linked List
    • Queue
    • Hash Table and Set
    • Tree
    • Sorting
    • Searching
  • An icon of the Architecture section Architecture
    • What is architecture?
    • Domain-Driven Design
    • ASP.NET Core Projects

Logging in .NET

Structured Logging

When logging and using a provider that supports structured/semantic logging, we should provide parameters the following way:

// GOOD
_logger.LogInformation("User {userId} logged in", userId);
// BAD
_logger.logInformation($"User {userId} logged in");

The first way stores both the value of the parameter (the actual user identifier) and the name of this parameter. In the second approach, the name is lost, so we are not able to filter based on the named parameter (userId).

JSON

The structured logging information is stored as JSON. For example:

{
"eventLevel": "Information",
"category": "Device.IoTHubService",
"eventId": "1543",
"messageTemplate": "Device {deviceId} has connected to IoT Hub",
"message": "Device 123 has connected to IoT Hub",
"deviceId": "123"
}

Log Storage

Structured logs can be stored in systems like:

  • Elasticsearch
  • Seq

Serilog

Using Serilog:

  1. Install NuGet packages:

    Terminal window
    dotnet add package Serilog.Sinks.Console # Console sink
    dotnet add package Serilog.Settings.Configuration # allows reading settings from a file
    dotnet add package Serilog.Extensions.Logging # DI
  2. Use Serilog with DI

    var host = Host.CreateDefaultBuilder(args)
    .ConfigureServices((host, services) =>
    {
    services.AddLogging(builder =>
    {
    var logger = new LoggerConfiguration()
    .ReadFrom.Configuration(host.Configuration) // Serilog.Settings.Configuration
    .CreateLogger();
    builder.ClearProviders(); // removes the default logger
    builder.AddSerilog(logger);
    });
    // Other services...
    })
    .Build();
  3. Configure

    In appsettings.json:

    {
    "Serilog": {
    "Using": [ "Serilog.Sinks.Console" ],
    "MinimumLevel": "Debug",
    "WriteTo": [ "Console" ]
    }
    }

    More information can be found here.

Rider

Sometimes Rider may display logs in black font color on the dark background. To change that, got to the “Preferences -> Editor -> Color Scheme -> Console Colors” and change “Bright White” color to #FFFFFF.

Usage without Dependency Injection

Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateLogger();
try
{
// Run the app...
}
catch(Exception e)
{
Log.Fatal(e, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}

Logging to files with Serilog

public static void AddLogging(this IServiceCollection services, AppConfiguration appConfiguration)
{
services.AddLogging(services, builder => {
builder.SetMinimumLevel(appConfiguration.LogLevel);
builder.AddSerilog(builder, GetSerilogLogger(appConfiguration), true);
});
}
private static Serilog.Core.Logger GetSerilogLogger(AppConfiguration appConfiguration)
{
var configuration = Serilog.ConsoleLoggerConfigurationExtensions.Console(
new Serilog.LoggerConfiguration()
.MinimumLevel.Verbose()
.WriteTo);
if(appConfiguration.LogToFile)
{
Serilog.FileLoggerConfigurationExtensions.File(configuration.WriteTo, appConfiguration.LogPath);
}
return configuration.CreateLogger();
}
←  Generic Host
Configuration  →
© 2023 Marcin Jahn | Dev Notebook | All Rights Reserved. | Built with Astro.