create-evlog-enricher

Create a new built-in evlog enricher to add derived context to wide events. Use when adding a new enricher (e.g., for deployment metadata, tenant context, feature flags, etc.) to the evlog package. Covers source code, tests, and all documentation.

hugorcd/evlog778 installsMITSynced Aug 26

Works with

Claude CodeCursorCodex CLIGitHub CopilotGemini CLI
---
name: create-evlog-enricher
description: Create a new built-in evlog enricher to add derived context to wide events. Use when adding a new enricher (e.g., for deployment metadata, tenant context, feature flags, etc.) to the evlog package. Covers source code, tests, and all documentation.
license: MIT
---

# 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

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

| Placeholder | Example (UserAgent) | Usage |
|-------------|---------------------|-------|
| `{name}` | `userAgent` | camelCase for event field key |
| `{Name}` | `UserAgent` | PascalCase in function/interface names |
| `{DISPLAY}` | `User Agent` | Human-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](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.spanId`), 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:

```markdown
## {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
  }
}
\`\`\`
```

3. 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 `apps/docs/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

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

More Deployment & CI/CD skills

azure-enterprise-infra-planner

microsoft/azure-skills

Architect and provision enterprise Azure infrastructure from workload descriptions. For cloud architects and platform engineers planning networking, identity, security, compliance, and multi-resource topologies with WAF alignment. Generates Bicep or Terraform directly (no azd). WHEN: 'plan Azure infrastructure', 'architect Azure landing zone', 'design hub-spoke network', 'plan multi-region DR topology', 'set up VNets firewalls and private endpoints', 'subscription-scope Bicep deployment', 'Azure Backup for VM workloads'. PREFER azure-prepare FOR app-centric workflows.

387.5k

azure-kubernetes-app-deploy

microsoft/azure-skills

Use when deploying an existing web application or API to an already-running Azure Kubernetes Service cluster. Detects the framework, generates a Dockerfile and Kubernetes manifests, validates against AKS Deployment Safeguards, and deploys with verification. WHEN: deploy app to AKS, deploy to existing AKS cluster, containerize app for Kubernetes, generate K8s manifests for Azure, set up CI/CD for AKS, my AKS deployment is failing safeguard checks, I have a Django/Express/Spring Boot app to run on AKS. DO NOT USE FOR: creating or provisioning an AKS cluster (use azure-kubernetes), assessing migration to AKS Automatic (use azure-kubernetes-automatic-readiness), or deploying to non-AKS targets like Web Apps, Container Apps, or Functions.

380.4k

finetuning

microsoft/azure-skills

Fine-tune models on Microsoft Foundry using SFT (supervised), DPO (preference), or RFT (reinforcement with graders). Covers dataset preparation, training job submission, deployment, and evaluation. USE FOR: fine-tune, SFT, DPO, RFT, training data, grader, distillation, fine-tuned model, training job, large file upload, calibrate grader, deploy fine-tuned model, evaluate fine-tuned model. DO NOT USE FOR: general model deployment without fine-tuning (use deploy-model), agent creation (use agents), prompt optimization without training (use prompt-optimizer).

323.2k

← All Deployment & CI/CD skills

Check your AI visibility

One URL in, a 0–100 score and the exact fixes out.

RUN THE CHECK

Browse all the tools

15 tools across six categories
13 of them never send your data anywhere

Free · No signup · No trial clock

SEE THE DIRECTORY