Логування: Serilog та Middleware
Логування: 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) вам потрібно додати:
Serilog.AspNetCore(основний пакет ядра)Serilog.Sinks.Console(щоб писати в консоль своїми, красивішими кольорами)Serilog.Sinks.File(щоб писати логи в файл на диску)
Крок 2: Ініціалізація та конфігурація
У Minimal APIs Serilog радить налаштовувати логер перед тим, як створювати WebApplicationBuilder, щоб у разі падіння на старті, ми встигли це залогувати.
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+).
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).
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% комерційних проєктів.
Практичні завдання
logs/). Відкрийте його та подивіться на вміст.app.UseHttpLogging(), але налаштуйте його так (через AddHttpLogging), щоб він логував АБСОЛЮТНО все (HttpLoggingFields.All).
Зробіть POST запит через Postman або Swagger, передавши якийсь JSON у тіло запиту (Body).
Подивіться в консоль логів. Знайдіть свій JSON там.LoggerConfiguration так, щоб він використовував .Enrich.WithMachineName() та .Enrich.WithThreadId(). (Вам доведеться завантажити відповідні NuGet пакети Serilog.Enrichers).
Розкажіть, який додатковий контекст додався до ваших записів і чому він корисний у мікросервісній архітектурі (де у вас працює кілька копій додатку на різних або одному сервері).Логування в ASP.NET Core: Основи
Уявіть, що ви ведете щоденник. Коли все добре, ви записуєте туди дрібні радощі. Коли виникає проблема — ви описуєте її детально, щоб потім проаналізувати. Логування (Logging) у програмуванні — це ведення такого "щоденника" вашим додатком.
Управління станом: HttpContext.Items та Cookies
Веб працює поверх протоколу HTTP, який за своєю природою не має стану (Stateless). Це означає, що сервер не пам'ятає вас між першим і другим запитом. Для сервера кожен запит — ніби від абсолютно нового користувача.