Конфигурация и логирование

Конфигурация и логирование: IConfiguration, IOptions и встроенный логгер.

Суть: конфигурация (строки подключения, настройки) и логирование тоже доступны через DI. IConfiguration и типизированный IOptions<T> дают доступ к настройкам, а ILogger<T> — к структурированному логированию.

Хардкодить настройки в коде нельзя: разные окружения требуют разных значений, секреты нельзя коммитить. ASP.NET Core даёт единую систему конфигурации и логирования, доступную через те же механизмы DI, что и ваши сервисы.

Типизированная конфигурация

{
  "Jwt": {
    "Issuer": "myapp",
    "ExpiryMinutes": 60
  }
}
public class JwtOptions
{
    public string Issuer { get; set; } = "";
    public int ExpiryMinutes { get; set; }
}

// регистрация привязки секции к классу
builder.Services.Configure<JwtOptions>(
    builder.Configuration.GetSection("Jwt"));

// использование через DI
public class TokenService
{
    private readonly JwtOptions _opts;
    public TokenService(IOptions<JwtOptions> opts) => _opts = opts.Value;
}

Вместо «магических строк» вроде обращения по ключу Jwt:Issuer вы получаете типизированный объект — с автодополнением и проверкой типов.

Логирование

public class UserService
{
    private readonly ILogger<UserService> _log;
    public UserService(ILogger<UserService> log) => _log = log;

    public void Process(int id)
    {
        _log.LogInformation("Обработка пользователя {UserId}", id);
    }
}

Обратите внимание на {UserId} — это структурированное логирование: значение пишется не просто в текст, а как именованное поле, по которому потом можно искать в системах логов.

Как работает под капотом

Конфигурация собирается из провайдеров (JSON-файлы, переменные окружения, аргументы) в единый IConfiguration, где более поздние источники переопределяют ранние. Configure<T> связывает секцию с классом и регистрирует IOptions<T> в контейнере. Логирование устроено как фабрика: ILogger<T> создаётся под конкретный класс T (имя категории равно имени класса), а сообщения уходят во все подключённые провайдеры (консоль, файлы, внешние системы).

Частые ошибки

  • Доступ к настройкам строками везде. Обращение по строковым ключам по всему коду хрупко — типизируйте через IOptions.
  • Логировать секреты и персональные данные. Пароли, токены, ПДн в логах — утечка.
  • Строковая интерполяция в логах. Интерполяция значений ломает структурированность — используйте шаблоны вроде {UserId}.

Best practices

  • Группируйте настройки в классы Options и привязывайте секциями.
  • Пишите структурированные логи с именованными параметрами — их можно фильтровать и агрегировать.
  • Управляйте уровнями логов (Information/Warning/Error) через конфигурацию по окружениям.

Провайдеры конфигурации и приоритет источников

Конфигурация в ASP.NET Core — это не один файл, а композиция провайдеров, выстроенных в стопку: appsettings.json, затем appsettings.{Environment}.json, затем User Secrets (в Development), переменные окружения и аргументы командной строки. Все они сливаются в единый IConfiguration по принципу «последний побеждает»: значение из переменной окружения переопределит то же значение из JSON. Это даёт гибкость без правки кода: один и тот же образ контейнера в разных средах настраивается разными переменными.

Паттерн Options — рекомендуемый способ потреблять настройки. Вы описываете класс, привязываете секцию через Configure<T> и получаете типизированный объект. Есть варианты: IOptions<T> (значение фиксируется на старте), IOptionsSnapshot<T> (перечитывается на каждый запрос, удобно в Scoped), IOptionsMonitor<T> (реагирует на изменения в рантайме). Дополнительно можно валидировать настройки при старте, чтобы приложение падало сразу при кривой конфигурации, а не на первом запросе.

Зачем логи структурированные и как ими управлять

Разница между $"User {id} updated" и LogInformation("User {UserId} updated", id) огромна. Первый вариант — просто текст, второй — структурированная запись, где UserId сохраняется как отдельное поле. В системах сбора логов (Seq, Elasticsearch, Application Insights) по такому полю можно искать, фильтровать и строить дашборды: «покажи все события по UserId=42». Текст искать так нельзя. Поэтому в значениях логов используют именованные шаблоны, а не интерполяцию строк.

Логирование настраивается уровнями (Trace, Debug, Information, Warning, Error, Critical) и категориями (обычно имя класса через ILogger<T>). Через конфигурацию задают, какой уровень писать для каких категорий: в Development можно опуститься до Debug, в Production оставить Warning и выше, чтобы не захлёбываться в шуме. Отдельное правило безопасности: в логи никогда не пишут секреты, токены и персональные данные — лог часто доступен шире, чем сами данные, и утечка через него вполне реальна.

Итог: конфигурация и логирование — это сервисы, доступные через DI; IOptions даёт типобезопасные настройки, а ILogger — структурированные логи. Дальше — глубже разберём конвейер middleware.

Проверьте себя
1. Зачем использовать IOptions вместо чтения настроек по строковым ключам?
AIOptions работает быстрее в рантайме
BДаёт типизированный доступ к настройкам вместо хрупких строковых ключей
CIOptions хранит данные в БД
DЭто одно и то же
2. Что такое структурированное логирование?
AЛоги в одну строку без полей
BЛогирование с именованными параметрами (например UserId), по которым можно искать и фильтровать
CЗапись логов только в файл
DЛоги без сообщений