Section 4 of 6

Structured Logging

🎯 What You'll Learn

  • What is structured logging
  • Message templates
  • Named parameters
  • Benefits of structured logging
  • Querying structured logs

What is Structured Logging?

Structured logging stores log data as structured data (key-value pairs) instead of plain text, making logs easier to search and analyze.

String Interpolation vs Message Templates

❌ Bad: String Interpolation

Unstructured (Don't do this) C#
_logger.LogInformation($"Product {productId} created by {userId}");

Output: "Product 123 created by user456" (plain text)

✅ Good: Message Templates

Structured (Do this) C#
_logger.LogInformation("Product {ProductId} created by {UserId}", productId, userId);

Output: Structured data with properties ProductId=123, UserId="user456"

Named Parameters

Use PascalCase names in curly braces for parameters.

Named Parameters C#
_logger.LogInformation(
    "Order {OrderId} placed by {CustomerName} for {TotalAmount:C}",
    orderId,
    customerName,
    totalAmount
);

Format Specifiers

Formatting Values C#
// Currency
_logger.LogInformation("Price: {Price:C}", 19.99); // $19.99

// Decimal places
_logger.LogInformation("Percentage: {Percent:P2}", 0.1234); // 12.34%

// Date
_logger.LogInformation("Date: {Date:yyyy-MM-dd}", DateTime.Now);

Benefits of Structured Logging

  • Searchable: Query by specific properties
  • Analyzable: Aggregate and analyze data
  • Filterable: Filter by property values
  • Machine-readable: Easy to parse programmatically

InvenTrack Example

Structured Logging in Action C#
public async Task<IActionResult> CreateOrder(Order order)
{
    _logger.LogInformation(
        "Creating order for customer {CustomerId} with {ItemCount} items, total {TotalAmount:C}",
        order.CustomerId,
        order.Items.Count,
        order.TotalAmount
    );

    try
    {
        await _context.Orders.AddAsync(order);
        await _context.SaveChangesAsync();

        _logger.LogInformation(
            "Order {OrderId} created successfully",
            order.Id
        );

        return Ok(order);
    }
    catch (Exception ex)
    {
        _logger.LogError(
            ex,
            "Failed to create order for customer {CustomerId}",
            order.CustomerId
        );
        return BadRequest();
    }
}

Key Takeaways

  • Structured logging: Key-value pairs, not plain text
  • Message templates: Use {PropertyName} placeholders
  • Don't use string interpolation: Use parameters instead
  • PascalCase: Name parameters in PascalCase
  • Format specifiers: {Price:C}, {Percent:P2}, etc.
  • Benefits: Searchable, analyzable, filterable