.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")`
Плохо: _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() перед завершением процессаCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub dex-it/claude-code-marketplace --plugin dex-skill-dotnet-logging