Help us improve
Share bugs, ideas, or general feedback.
.NET structured logging — Serilog, ILogger, Seq. Активируется при serilog, seq, ILogger, log level, correlation, LogError, LogWarning, PII, пустой catch, необработанное исключение, опциональный шаг, side-feature, спам логов
How this skill is triggered — by the user, by Claude, or both
Slash command
/dex-skill-dotnet-logging:dotnet-loggingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Плохо: `_logger.LogInformation($"Order {orderId} created")`
Share bugs, ideas, or general feedback.
Плохо: _logger.LogInformation($"Order {orderId} created")
Правильно: _logger.LogInformation("Order {OrderId} created", orderId)
Почему: строка аллоцируется ВСЕГДА, даже если уровень отключен. Structured logging подставляет параметры только при активном уровне + позволяет искать по свойствам в Seq
Плохо: Serilog.Log.Information("Something happened")
Правильно: _logger.LogInformation("Something happened") через DI ILogger<T>
Почему: теряется категория (имя класса), невозможно переопределить MinimumLevel для конкретного namespace, не mockable в тестах
Плохо: public MyService(ILogger logger) — без generic параметра
Правильно: public MyService(ILogger<MyService> logger)
Почему: без <T> все логи идут в категорию "Default". Невозможно фильтровать по source в Seq: SourceContext = 'MyService'
Плохо: catch (Exception) { } — исключение проглочено молча
Правильно: catch (Exception ex) { _logger.LogError(ex, "..."); throw; }
Почему: ошибка исчезает бесследно, дебаг превращается в гадание. Как минимум зафиксируй в логах
Плохо: catch (Exception ex) { _logger.LogError(ex, "Error"); } — без throw
Правильно: _logger.LogError(ex, "..."); throw; — логируем И пробрасываем
Почему: вызывающий код считает операцию успешной. Данные повреждены/не сохранены, но ответ 200 OK
Плохо: _logger.LogError("Something failed for {OrderId}", orderId) — где stack trace?
Правильно: _logger.LogError(ex, "Failed to process order {OrderId}", orderId)
Почему: без exception невозможно найти причину ошибки в Seq. Первый параметр Error — всегда Exception
Плохо: внутри основного флоу прямой вызов вспомогательной операции (сбор метрик, аналитика, side-feature), у которой может бросить исключение, и выше по стеку нет общего обработчика
Правильно: try { await OptionalStep(); } catch (Exception ex) { _logger.LogError(ex, "..."); return; } — залогируй и выйди, не пробрасывай
Почему: side-feature не должна валить основной сценарий. Без обёртки одно исключение из опционального шага кладёт весь handler/pipeline. Правило: для каждого вызова в коде — «должен ли этот шаг ронять основной флоу? если нет — обернуть в try/catch + log + return»
Для разделения обязательных и опциональных шагов через outbox / транзакционную границу — см.
dex-skill-dotnet-async-patterns(«Mixed обязательный + опциональный шаг»).
Плохо: _logger.LogInformation("Request: {@Request}", httpRequest) — весь HttpRequest в лог
Правильно: _logger.LogInformation("Request {Method} {Path}", method, path) — только нужные поля
Почему: JSON сериализация большого объекта = CPU + память, Seq/ELK раздувается, sensitive data может протечь
Плохо: {@Entity} на EF entity с навигационными свойствами — циклические ссылки
Правильно: {@Dto} на проекцию или примитивы: {EntityId}, {EntityName}
Почему: circular reference = StackOverflow или гигантский JSON. Serilog destructure policy не спасет от всех случаев
Плохо: _logger.LogWarning("Invalid input: {Field}", field) — validation = нормально
Правильно: _logger.LogDebug("Validation failed for {Field}: {Reason}", field, reason)
Почему: Warning = что-то подозрительное, алерт-система засоряется штатными событиями
Плохо: _logger.LogInformation("Cache miss for key {Key}", key) — в production это спам
Правильно: _logger.LogDebug("Cache miss for key {Key}", key)
Почему: Information включен в production, Debug — нет. Cache miss на каждый запрос = тысячи бесполезных записей
Плохо: _logger.LogError("User {UserId} not found") — 404 это не ошибка
Правильно: _logger.LogWarning("User {UserId} not found", userId)
Почему: Error -> алерт дежурному. User not found — штатная ситуация, не требует реакции
Плохо: foreach (var item in items) _logger.LogInformation("Processing {ItemId}", item.Id)
Правильно: _logger.LogInformation("Processing {Count} items for {OrderId}", items.Count, orderId)
Почему: 10000 items = 10000 записей в Seq. Шум, за которым не видно реальных событий
Плохо: LogInformation("Запрашиваем данные из X") + LogInformation("Получили N записей") + LogInformation("Отправляем в Y") на каждом шаге handler'а
Правильно: Debug для шагов флоу, Information — только для завершённого бизнес-события («Order created», «Analysis completed»)
Почему: Information включён в production и идёт в алерт-системы. Шаги флоу = log flooding, ключевые бизнес-события тонут в шуме. Вопрос для self-check каждого LogInformation: «это важно оператору в 3 часа ночи в инциденте, или только разработчику при отладке?» — если второе, это Debug
Плохо: LogInformation("Starting helper Y") + LogInformation("Helper Y finished") внутри приватного метода сервиса
Правильно: Information только на границе слоя (handler / controller) для завершённой бизнес-операции, внутренние helper'ы — Debug
Почему: log-уровень задаёт границу видимости в production. Внутренние шаги — деталь реализации, они не нужны оператору. Information на каждом helper = дублирование + невозможно найти реальное событие в потоке
Плохо: _logger.LogInformation("User {Email} from {Phone}", email, phone)
Правильно: логируй UserId, не PII. Если нужен email — маскируй: m***@gmail.com
Почему: GDPR/ФЗ-152, логи хранятся годами, доступ к Seq/ELK шире чем к production БД
Плохо: Log.Fatal("DB connection lost"); Environment.Exit(1) — буфер не сброшен
Правильно: Log.Fatal("..."); Log.CloseAndFlush(); Environment.Exit(1)
Почему: Serilog буферизирует запись. Без CloseAndFlush() последнее (самое важное!) сообщение не доходит до Seq/файла
Плохо: повторять {JobId} в каждой строке лога внутри background job
Правильно: using (_logger.BeginScope(new { JobId = jobId })) — добавляет свойство ко ВСЕМ логам в блоке
Почему: без scope — дублирование, забытый JobId в одном вызове -> невозможно связать лог с задачей в Seq
ILogger<T> через DI, не статический LogLog.CloseAndFlush() перед завершением процессаnpx claudepluginhub dex-it/claude-code-marketplace --plugin dex-skill-dotnet-loggingCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.