create-evlog-adapter
Create a new built-in evlog adapter to send wide events to an external observability platform. Use when adding a new drain adapter (e.g., for Datadog, Sentry, Loki, Elasticsearch, etc.) to the evlog package. Covers source code, build config, package exports, tests, and all documentation.
How do I install this agent skill?
npx skills add https://github.com/hugorcd/evlog --skill create-evlog-adapterIs this agent skill safe to install?
- Gen Agent Trust Hubpass
This skill provides a structured development workflow and templates for adding new observability adapters to a logging package. It follows industry-standard patterns for configuration management, unit testing, and documentation without any detected malicious behaviors.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
- Runlayerpass
1/3 files flagged
- ZeroLeakswarn
1 finding · Score: 69/100
What does this agent skill do?
Create evlog Adapter
Add a new built-in adapter to evlog. Every adapter follows the same architecture and is built on the public toolkit primitives in evlog/toolkit — so a community adapter has the same shape as a built-in one.
PR Title
Recommended format for the pull request title:
feat: add {name} adapter
The exact wording may vary depending on the adapter (e.g., feat: add OTLP adapter, feat: add Axiom drain adapter), but it should always follow the feat: conventional commit prefix.
Touchpoints Checklist
| # | File | Action |
|---|---|---|
| 1 | packages/evlog/src/adapters/{name}.ts | Create adapter source (built on defineHttpDrain from ../shared/drain) |
| 2 | packages/evlog/tsdown.config.ts | Add build entry |
| 3 | packages/evlog/package.json | Add exports + typesVersions entries |
| 4 | packages/evlog/test/adapters/{name}.test.ts | Create tests |
| 5 | apps/docs/content/4.adapters/{n}.{name}.md | Create adapter doc page (before custom.md) |
| 6 | apps/docs/content/4.adapters/1.overview.md | Add adapter to overview (links, card, env vars) |
| 7 | skills/review-logging-patterns/SKILL.md | Add adapter row in the Drain Adapters table |
| 8 | Renumber custom.md | Ensure custom.md stays last after the new adapter |
Important: Do NOT consider the task complete until all 8 touchpoints have been addressed.
Naming Conventions
Use these placeholders consistently:
| Placeholder | Example (Datadog) | Usage |
|---|---|---|
{name} | datadog | File names, import paths, env var suffix |
{Name} | Datadog | PascalCase in function/interface names |
{NAME} | DATADOG | SCREAMING_CASE in env var prefixes |
Standard option naming (use these exact names):
| Concept | Standard option name |
|---|---|
| Bearer-style API secret | apiKey |
| Base URL of the ingest API | endpoint |
| Service identifier | serviceName |
| Request timeout (ms) | timeout |
If a service historically used a different name (token, sourceToken, …) keep it as a deprecated alias — see Axiom and Better Stack for the pattern.
Step 1: Adapter Source — built on defineHttpDrain
Create packages/evlog/src/adapters/{name}.ts. Read references/adapter-template.md for the full annotated template.
The contract is now defineHttpDrain<TConfig>({ resolve, encode }). You only ship two pieces of logic:
resolve()— produce a fully-resolved config ornullto skip. UseresolveAdapterConfigfor the standard precedence (overrides →runtimeConfig.evlog.{name}→runtimeConfig.{name}→ env vars). ListNUXT_{NAME}_*before{NAME}_*inConfigField.envfor silent Nuxt compat; show only{NAME}_*in user-facing messages viaformatPublicEnvKeys.encode(events, config)— produce{ url, headers, body }for a batch of events (ornullto skip). HTTP transport, retries, timeout, and error logging are handled bydefineHttpDrain.
Key rules:
- Single factory. Export one
create{Name}Drain(overrides?: Partial<{Name}Config>). No dual-API factories: if a service has multiple ingest modes (logs vs events), expose them via amodeoption (see PostHog). - No HTTP code in the adapter. Don't call
fetchdirectly — letdefineHttpDraindo it. If your service truly needs custom transport (e.g. binary envelopes), usedefineDrainand callhttpPostfromevlog/toolkit. - No bespoke config resolution. Always go through
resolveAdapterConfig. If you need to support a deprecated alias (token→apiKey), include both in theConfigField[]and fall through inresolve(). - Exported converters. If the service needs a specific event shape, export a
to{Name}Event()(orbuildPayload()) helper so it can be tested independently.
Step 2: Build Config
Add a build entry in packages/evlog/tsdown.config.ts alongside the existing adapters:
'adapters/{name}': 'src/adapters/{name}.ts',
Place it after the last adapter entry in tsdown.config.ts (follow existing ordering in that file).
Step 3: Package Exports
In packages/evlog/package.json, add two entries:
In exports (after the last adapter, currently ./posthog):
"./{name}": {
"types": "./dist/adapters/{name}.d.mts",
"import": "./dist/adapters/{name}.mjs"
}
In typesVersions["*"] (after the last adapter):
"{name}": [
"./dist/adapters/{name}.d.mts"
]
Step 4: Tests
Create packages/evlog/test/adapters/{name}.test.ts.
Read references/test-template.md for the full annotated template.
Required test categories:
- URL construction (default + custom endpoint)
- Headers (auth, content-type, service-specific)
- Request body format (JSON structure matches service API)
- Skip behavior when
apiKey(or required field) is missing - Batch operations
- Deprecated alias still works (when applicable)
Step 5: Adapter Documentation Page
Create apps/docs/content/4.adapters/{n}.{name}.md where {n} is the next number before custom.md (custom should always be last).
Use the existing Axiom adapter page (apps/docs/content/4.adapters/2.axiom.md) as a reference for frontmatter structure, tone, and sections. Key sections: intro, quick setup, configuration (env vars table + priority), advanced usage, querying in the target service, troubleshooting, direct API usage, next steps.
Important: multi-framework examples. The Quick Start section must include a ::code-group with tabs for all supported frameworks (Nuxt/Nitro, Hono, Express, Fastify, Elysia, NestJS, Standalone). Do not only show Nitro examples. See any existing adapter page for the pattern.
Step 6: Update Adapters Overview Page
Edit apps/docs/content/4.adapters/1.overview.md to add the new adapter in three places (follow the pattern of existing adapters):
- Frontmatter
linksarray — add a link entry with icon and path ::card-groupsection — add a card block before the Custom card- Zero-Config Setup
.envexample — add the adapter's env vars
Step 7: Update skills/review-logging-patterns/SKILL.md
In skills/review-logging-patterns/SKILL.md (the public skill distributed to users), find the Drain Adapters table and add a new row:
| {Name} | `evlog/{name}` | `{NAME}_API_KEY`, `{NAME}_DATASET` (or equivalent) |
Follow the pattern of the existing rows (Axiom, OTLP, PostHog, Sentry, Better Stack).
Step 8: Renumber custom.md
If the new adapter's number conflicts with custom.md, renumber custom.md to be the last entry. For example, if the new adapter is 5.{name}.md, rename 5.custom.md to 6.custom.md.
Verification
After completing all steps, run:
cd packages/evlog
pnpm run lint
pnpm run typecheck
pnpm run test
pnpm run build
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/hugorcd/evlog/create-evlog-adapter">View create-evlog-adapter on skillZs</a>