dotnet-logger-message-design

Designs and tests high-performance LoggerMessage logging in .NET with FakeLogger assertions.

2|Updated Jul 18, 2026
One-click install
npx skills add https://github.com/Arasz/ai-badger --skill dotnet-logger-message-design-arasz
Or copy as Structured Prompt for Agent
Please help me install this Agent Skill.
Skill: dotnet-logger-message-design
Source: https://github.com/Arasz/ai-badger/tree/main/features/dotnet/skills/dotnet-workload/references/dotnet-logger-message-design
Command: npx skills add https://github.com/Arasz/ai-badger --skill dotnet-logger-message-design-arasz

SYSTEM DOCUMENTATION & REQUIREMENTS

💡 This Skill includes references (resource) components.

What problem does it solve? Designing [LoggerMessage] source-generated logging in .NET involves subtle constraints — templates cannot format collections, EventIds need per-category ranges, and call-site interpolation defeats the source generator — while testing log output requires knowing the exact FakeLogger generic vs non-generic compile contract. This Skill codifies those design rules and testing patterns so log lines stay inside the high-performance invariant and tests actually compile and assert correctly. ## Core Features & Use Cases - LoggerMessage design rules: nested static partial Log classes, explicit EventIds allocated per category range, pre-joining collections at the call site, per-item detail logs vs counts, and avoiding record widening that ripples into API/MCP wire contracts. - FakeLogger testing guidance: generic vs non-generic compile contract (CS1503 avoidance), LatestRecord/AllRecords assertions, fresh-collector isolation under xunit parallelism, and RED-first EventId tests with non-vacuous guard assertions. - Use Case: When a finding says "feature promises to log ranked candidates but logs only counts", use this Skill to design the new EventId (e.g. 507), write the RED FakeLogger test asserting level and message contents, and decide implement-vs-correct-docs by counting promise surfaces. ## Quick Start Use the dotnet-logger-message-design skill to design a new LoggerMessage detail log with an explicit EventId and write a RED-first FakeLogger test asserting its level and message contents.

Frequently Asked Questions about dotnet-logger-message-design

High-intent search queries and answers about installing and using this skill.

FAQPage Schema
How do I log a collection parameter with [LoggerMessage] in .NET?

LoggerMessage templates cannot format collections — an IReadOnlyList<string> renders as its type name. Pre-join at the call site with string.Join(", ", items); that is parameter building, not interpolation, so it satisfies the no-interpolation invariant.

How do I test ILogger output with FakeLogger in .NET?

Use FakeLogger from the Microsoft.Extensions.Logging.Testing package and assert via logger.Collector.LatestRecord or Collector.AllRecords, checking record.Id.Id, record.Level, and record.Message. The Message property is the fully formatter-rendered template, so parameter substitutions are applied.

FakeLogger vs FakeLogger<T> — which one for ILogger<T> constructors?

Use FakeLogger<T> for constructors taking ILogger<T>. The non-generic FakeLogger implements ILogger directly but is not ILogger<T>, so passing it to an ILogger<T> parameter causes compile error CS1503. The non-generic form only plugs into plain-ILogger LoggerMessage methods.

Why does string interpolation break [LoggerMessage] performance?

Interpolated strings are built before the logging call, so the allocation happens even when the level is disabled and structured parameters are lost. Pass the message template and arguments separately so the source generator keeps the call allocation-free on disabled paths.

How should EventIds be allocated for LoggerMessage methods?

Allocate from the class's next free id within its own range; EventIds are scoped per logger category, so cross-class reuse is benign but same-category collisions are not. Use one EventId per (message shape, level) pair — two levels means two [LoggerMessage] methods.

Why does a 'no records with EventId X' test pass before the feature exists?

A negative assertion passes vacuously when the feature is not implemented yet. Pair it with a positive assertion — for example that the counts record IS present — so the test proves the logging path actually ran and the guard is meaningful.