mcpField
C
create-evlog-enricher

create-evlog-enricher

github.com/evloghqDevelopment1.9k792 installsUpdated 2d ago
Repository

About create-evlog-enricher

A repository skill for contributors adding a built-in evlog enricher. It defines the `defineEnricher` pattern and covers field behavior, tests, documentation, default composition decisions, and release updates.

Key features

  1. 01defineEnricher implementation pattern
  2. 02Single event-field enrichment
  3. 03Overwrite and missing-data behavior
  4. 04Enricher test requirements
  5. 05Documentation and README updates
  6. 06Default composition decision guidance

Use cases

  1. 01Adding a field derived from request context
  2. 02Checking behavior when source data is absent
  3. 03Testing preservation and overwrite options
  4. 04Documenting and releasing a built-in enricher

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/evloghq/evlog --skill create-evlog-enricher

After installing, type/create-evlog-enricherin Claude Code to confirm it loaded.

Details

From the project README

Create evlog Enricher

Add a new built-in enricher to evlog. Every enricher is built on the public toolkit primitive defineEnricher from evlog/toolkit, so a community enricher has the same shape as a built-in one.

PR Title

feat(core): add the {name} enricher

Enrichers live in the core package surface (evlog/enrichers), so use the core scope unless a dedicated scope exists.

Touchpoints Checklist

#FileAction
1packages/evlog/src/enrichers/index.tsAdd enricher source (one defineEnricher call)
2Same file: createDefaultEnrichers()Decide whether the enricher belongs in the default composition (see below)
3packages/evlog/test/toolkit/enrichers.test.tsAdd tests (one describe block per enricher)
4apps/docs/content/5.use-cases/5.enrichers.mdAdd a section for the enricher + update the import list and, if applicable, the "All built-in enrichers" default composition text
5skills/review-logging-patterns/SKILL.mdAdd the enricher to the Built-in: line in the Enrichers section
6packages/evlog/README.mdAdd the enricher to the Built-in Enrichers section (root README.md is a symlink to it)
7.changeset/{name}-enricher.mdCreate changeset (minor)

Important: Do NOT consider the task complete until all 7 touchpoints have been addressed.

Should it join createDefaultEnrichers()?

createDefaultEnrichers() composes user agent, geo, request size, and trace context via composeEnrichers (from ../shared/compose). Add the new enricher to the composition only if it is universally applicable and reads nothing but the request (headers/env). Anything requiring service-specific setup or extra cost stays opt-in. Changing the default composition is a behavior change for every existing createDefaultEnrichers() user: call it out explicitly in the changeset.

Naming Conventions

PlaceholderExample (UserAgent)Usage
{name}userAgentcamelCase for event field key
{Name}UserAgentPascalCase in function/interface names
{DISPLAY}User AgentHuman-readable display name

Step 1: Enricher Source: built on defineEnricher

Add the enricher to packages/evlog/src/enrichers/index.ts. Read references/enricher-template.md for the full annotated template.

The contract is defineEnricher<T>({ name, field, compute }, options?). You only ship one piece of logic:

  • compute(ctx): return the computed value (typed as T) or undefined to skip.

defineEnricher handles the rest:

  • merging via mergeEventField (respecting options.overwrite, default false)
  • error isolation (throws are caught and logged, never propagated)
  • skipping when compute returns undefined

Key rules:

  • Use the toolkit helpers: getHeader() for case-insensitive header lookup, normalizeNumber() for numeric strings. Both from ../shared/headers (re-exported by evlog/toolkit).
  • Single event field: each enricher writes one top-level field on ctx.event. If the enricher must additionally pin top-level fields (like createTraceContextEnricher does for event.traceId / event.parentSpanId), wrap the defineEnricher result in a closure, see that enricher for the pattern.
  • Factory pattern: create{Name}Enricher(options: EnricherOptions = {}) returns the result of defineEnricher(...), directly in the normal case, or through the thin closure wrapper when the enricher also pins top-level fields (see the single-event-field rule above).
  • No side effects: never throw, never log; rely on defineEnricher's built-in error handling if something goes wrong.
  • Export the Info type: {Name}Info describing the field shape, exported alongside the factory.

Step 2: Tests

Add tests to packages/evlog/test/toolkit/enrichers.test.ts, following the existing structure (one describe block per enricher) and packages/evlog/test/README.md conventions.

Required test categories:

  1. Sets the field from its source: verify the enricher populates the event field correctly, reading whatever it actually reads (ctx.request, ctx.response, process.env, ctx.event, or headers)
  2. Skips when source data missing: verify no field is set when the required input is absent
  3. Preserves existing data: verify overwrite: false (default) doesn't replace user-provided fields
  4. Overwrites when requested: verify overwrite: true replaces existing fields
  5. Handles edge cases: empty strings and malformed values, plus case-insensitive lookup for a header-based enricher
  6. Default composition: if the enricher joined createDefaultEnrichers(), extend that composition's tests

Step 3: Update the Enrichers Docs Page

Edit apps/docs/content/5.use-cases/5.enrichers.md:

  1. Add the enricher to the import list at the top
  2. Add a ## {DISPLAY} section following the structure of the existing ones:
## {DISPLAY}

[One-sentence description of what the enricher does.]

**Sets:** `event.{name}`

\`\`\`typescript
const enrich = create{Name}Enricher()
\`\`\`

**Output shape:**

\`\`\`typescript
interface {Name}Info {
  // fields
}
\`\`\`

**Example output:**

\`\`\`json
{
  "{name}": {
    // example values
  }
}
\`\`\`
  1. If the enricher joined the default composition, update the "All built-in enrichers" section text listing what createDefaultEnrichers() composes.

Custom-enricher authoring docs live separately at apps/docs/content/6.extend/5.custom-enrichers.md, with no change needed there unless the toolkit contract itself changed.

Step 4: Update the Public Skill

In skills/review-logging-patterns/SKILL.md (published on evlog.dev), find the Enrichers section and add the new enricher to the Built-in: line.

Step 5: Update README

Add the enricher to the Built-in Enrichers section in packages/evlog/README.md (the root README.md is a symlink to it).

Step 6: Changeset

Create .changeset/{name}-enricher.md with a minor bump describing what the enricher sets and when to use it. Mention explicitly if the default composition changed.

Verification

cd packages/evlog
pnpm run lint
pnpm run typecheck
pnpm run test
pnpm run build

Comments

Markdown supported