Skip to content

Latest commit

 

History

History
196 lines (131 loc) · 6.7 KB

File metadata and controls

196 lines (131 loc) · 6.7 KB

Logging

The cloops.microservices SDK uses Serilog for structured logging, integrated with Microsoft.Extensions.Logging. This provides a powerful, flexible logging system that works seamlessly with log collectors in production environments.

Table of Contents

  1. Getting a Logger Through Dependency Injection
  2. Using the Logger
  3. Customizing Log Levels
  4. Production Logging Setup

Getting a Logger Through Dependency Injection

The SDK automatically configures Serilog and integrates it with Microsoft.Extensions.Logging. To use logging in your services, inject ILogger<T> through constructor dependency injection:

using Microsoft.Extensions.Logging;

namespace YourApp.Services;

public class MyService
{
    private readonly ILogger<MyService> _logger;

    public MyService(ILogger<MyService> logger)
    {
        _logger = logger;
    }
}

Important: Always use the generic ILogger<T> where T is your service class. This provides better log filtering and categorization.

Using the Logger

The ILogger<T> interface provides several methods for logging at different levels:

Log Levels

  • LogTrace - Very detailed logs, typically only useful during development
  • LogDebug - Detailed information for debugging
  • LogInformation - General informational messages about application flow
  • LogWarning - Warning messages for unexpected but recoverable situations
  • LogError - Error messages for failures that don't stop the application
  • LogCritical - Critical failures that may cause the application to abort

Examples

public class OrderService
{
    private readonly ILogger<OrderService> _logger;

    public OrderService(ILogger<OrderService> logger)
    {
        _logger = logger;
    }

    public async Task ProcessOrder(Order order)
    {
        _logger.LogInformation("Processing order {OrderId} for customer {CustomerId}",
            order.Id, order.CustomerId);

        try
        {
            // Process order logic
            _logger.LogDebug("Order {OrderId} validated successfully", order.Id);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to process order {OrderId}", order.Id);
            throw;
        }
    }
}

Structured Logging

Serilog supports structured logging using placeholders. Instead of string interpolation, use placeholders:

// ✅ Good - Structured logging
_logger.LogInformation("User {UserId} logged in from {IPAddress}", userId, ipAddress);

// ❌ Avoid - String interpolation loses structure
_logger.LogInformation($"User {userId} logged in from {ipAddress}");

Structured logging allows log aggregators to parse and query logs by specific fields, making debugging and monitoring much more powerful.

Customizing Log Levels

Log levels can be customized through environment variables via AppSettings.

Default Configuration

By default, the SDK configures:

  • Minimum Level: Information
  • Framework Overrides: System.Net.Http, Microsoft, and System namespaces are set to Warning to reduce noise

Enabling Debug Logging

To enable debug-level logging, set the DEBUG environment variable to true:

export DEBUG=true

This will:

  • Set the minimum log level to Debug
  • Allow LogDebug and LogTrace messages to be emitted

Environment-Based Configuration

The logging configuration is automatically adjusted based on the environment:

  • Production: Logs are output in compact JSON format (structured logging) for easy parsing by log collectors
  • Non-Production: Logs are output in a human-readable, colorized format for easier development

The environment is determined by the DOTNET_ENVIRONMENT environment variable and defaults to Production. Set it to Development, or any non-Production value, to render logs in a human-friendly console format.

Production Logging Setup

In production environments (especially Kubernetes), logs are typically collected by log aggregation systems. The SDK is configured to work seamlessly with these systems.

How It Works

  1. Structured JSON Output: In production, the SDK outputs logs in compact JSON format using Serilog's CompactJsonFormatter. This format is easily parseable by log collectors.

  2. Log Collection: Log collectors (like Alloy, Fluentd, or Promtail) read logs from stdout/stderr of your containers.

  3. Log Aggregation: Collected logs are forwarded to centralized log aggregation platforms for storage, search, and analysis.

Log Collectors for Kubernetes

The SDK's structured JSON logging works with any log collector that can parse JSON. Here are some popular options:

Alloy (Grafana Alloy) [Recommended]

Fluentd

  • Documentation: https://docs.fluentd.org/
  • Popular open-source log collector
  • Large ecosystem of plugins
  • Works with many log backends (Elasticsearch, Splunk, etc.)

Vector

  • Documentation: https://vector.dev/docs/
  • High-performance observability data pipeline
  • Supports many sources and sinks
  • Good performance characteristics

Log Aggregation Platforms

Once logs are collected, they're typically sent to aggregation platforms:

Example Kubernetes Setup

In a typical Kubernetes setup with Alloy:

  1. Application logs are written to stdout/stderr in JSON format
  2. Alloy DaemonSet collects logs from all pods
  3. Alloy forwards logs to your chosen backend (Loki, Elasticsearch, etc.)
  4. Dashboards in Grafana/Kibana visualize and query the logs

The SDK requires no additional configuration - it automatically outputs structured JSON in production environments, making it compatible with any log collector.

Log Enrichment

The SDK automatically enriches all logs with:

  • Application name (from AssemblyName)
  • Thread ID and Thread Name
  • Log context (from LogContext)
  • Timestamp and Log Level

These enrichments help with filtering and correlation in log aggregation systems.


Back to documentation index