Minimal API

Логування: Serilog та Middleware

Вбудована консоль ASP.NET Core відмінно підходить для розробки. Проте в Production вам потрібен надійний спосіб зберігати логи: записувати їх у текстові файли з ротацією (новий файл щодня), відправляти в базу даних або надсилати у хмарні сервіси на кшталт DataDog, SEQ чи Application Insights.

Логування: Serilog та Middleware

Вбудована консоль ASP.NET Core відмінно підходить для розробки. Проте в Production вам потрібен надійний спосіб зберігати логи: записувати їх у текстові файли з ротацією (новий файл щодня), відправляти в базу даних або надсилати у хмарні сервіси на кшталт DataDog, SEQ чи Application Insights.

Хоча стандартний ILogger на це здатен за допомогою додаткових провайдерів, індустріальним стандартом (де-факто) у світі .NET є бібліотека Serilog.

Чому саме Serilog?

Serilog створений з нуля для підтримки Структурованого логування. Він надзвичайно швидкий, налаштовується дуже просто і має сотні "раковин" (Sinks) — плагінів для запису логів куди завгодно: від файлів до Slack.

Давайте замінимо стандартний логер ASP.NET Core на Serilog!

Крок 1: Встановлення пакетів

Через NuGet (або в терміналі dotnet add package) вам потрібно додати:

  1. Serilog.AspNetCore (основний пакет ядра)
  2. Serilog.Sinks.Console (щоб писати в консоль своїми, красивішими кольорами)
  3. Serilog.Sinks.File (щоб писати логи в файл на диску)

Крок 2: Ініціалізація та конфігурація

У Minimal APIs Serilog радить налаштовувати логер перед тим, як створювати WebApplicationBuilder, щоб у разі падіння на старті, ми встигли це залогувати.

Program.cs
using Serilog;

// 1. Попереднє налаштування (до builder'а)
Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Information()
    .WriteTo.Console()
    .WriteTo.File("logs/myapp-.txt", rollingInterval: RollingInterval.Day)
    .CreateLogger();

try
{
    Log.Information("Старт додатку...");
    var builder = WebApplication.CreateBuilder(args);

    // 2. Вказуємо ASP.NET використовувати Serilog замість стандартної системи
    builder.Host.UseSerilog();

    var app = builder.Build();

    app.MapGet("/", (ILogger<Program> logger) =>
    {
        // Цей виклик тепер автоматично пройде через Serilog!
        logger.LogInformation("Отримано запит до головної сторінки.");
        return "Serilog is working!";
    });

    app.Run();
}
catch (Exception ex)
{
    Log.Fatal(ex, "Додаток впав під час запуску");
}
finally
{
    // 3. Закриваємо потоки файлів
    Log.CloseAndFlush();
}

Анатомія коду:

  • Ми налаштували RollingInterval.Day. Це означає, що Serilog самостійно створюватиме новий текстовий файл щодня (myapp-20241014.txt, myapp-20241015.txt) і писати туди журнали подій.
  • Використовуючи builder.Host.UseSerilog(), ми замінили всю внутрішню систему ASP.NET Core. Будь-які компоненти фреймворку (чи ваші власні роути), які проситимуть ILogger<T>, автоматично отримуватимуть екземпляр Serilog, самі цього не знаючи!
  • Блок try-catch гарантує, що якщо додаток впаде через неправильні налаштування DI (наприклад), ми зможемо знайти причину в наших файлах.

Автоматичне логування HTTP запитів

Часто вам потрібно знати відповіді на питання:

  • Хто і з якого IP відвідував /api/users?
  • Які заголовки були в запиті?
  • Який розмір Body?

Навіщо писати цей код вручну у кожному маршруті? Ми можемо використати вбудований HttpLoggingMiddleware (починаючи з ASP.NET Core 6+).

Program.cs
using Microsoft.AspNetCore.HttpLogging;

var builder = WebApplication.CreateBuilder(args);

// 1. Налаштовуємо деталізацію (що саме логувати)
builder.Services.AddHttpLogging(logging =>
{
    logging.LoggingFields = HttpLoggingFields.RequestPath |         // Шлях URL
                            HttpLoggingFields.RequestMethod |       // Метод GET/POST
                            HttpLoggingFields.RequestHeaders |      // Заголовки (напр. User-Agent)
                            HttpLoggingFields.ResponseStatusCode;   // Статус відповіді (200, 404)
});

var app = builder.Build();

// 2. Вмикаємо Middleware
app.UseHttpLogging();

app.MapGet("/hello", () => "Welcome to HTTP Logging");

app.Run();

Після підключення app.UseHttpLogging(), фреймворк автоматично перехоплюватиме кожен запит і створюватиме гарні записи рівня Information із вибраними полями (LoggingFields).

Будьте дуже обережні з конфігурацією! Ніколи не логуйте поля паролів, токенів авторизації чи номерів кредитних карток з RequestBody на Production.

Створення власного Logging Middleware

Якщо вбудованого UseHttpLogging вам недостатньо або він здається надто важким, створити власний кастомний Middleware для логування надзвичайно просто (ми робили щось подібне у розділі про Middleware).

Program.cs
var app = WebApplication.CreateBuilder(args).Build();

// Кастомний Inline Middleware для хронометража
app.Use(async (context, next) =>
{
    var logger = context.RequestServices.GetRequiredService<ILogger<Program>>();

    // Запам'ятовуємо час початку і шлях
    var stopwatch = System.Diagnostics.Stopwatch.StartNew();
    var path = context.Request.Path;

    try
    {
        // Відпускаємо запит далі по конвеєру
        await next();

        // Запит повернувся назад (усе ок)
        stopwatch.Stop();
        logger.LogInformation("Запит {Path} завершено за {ms} мс (Статус: {StatusCode})",
            path, stopwatch.ElapsedMilliseconds, context.Response.StatusCode);
    }
    catch (Exception ex)
    {
        // Якщо хтось "впав" далі у конвеєрі
        stopwatch.Stop();
        logger.LogError(ex, "Запит {Path} впав з помилкою після {ms} мс",
            path, stopwatch.ElapsedMilliseconds);

        // Прокидаємо помилку далі (важливо!)
        throw;
    }
});

app.MapGet("/fast", () => "It was fast!");
app.MapGet("/slow", async () => { await Task.Delay(1000); return "It was slow..."; });

app.Run();

Анатомія коду:

  • Ми інжектували ILogger прямо з поточного контексту (context.RequestServices).
  • Використали Stopwatch для точнішого вимірювання часу запиту.
  • Огорнули next() у try-catch. Завдяки цьому ми ловимо абсолютно всі помилки, які стаються в будь-якому з наших роутів, і красиво їх логуємо, перш ніж додаток поверне 500 помилку клієнту. Це паттерн "Global Exception Handling & Logging", який використовується у 99% комерційних проєктів.

Практичні завдання

Свій перший лог-файл Додайте пакети Serilog до вашого проєкту (як показано в теорії). Налаштуйте запис лише у файл (вимкніть вивід у консоль). Зробіть декілька запитів у браузері. Знайдіть файл на жорсткому диску (папка logs/). Відкрийте його та подивіться на вміст.
Copyright © 2026