create-evlog-adapter

Create a new built-in evlog adapter for sending events to external observability platforms. Requires completing all 8 mandatory touchpoints: adapter source, build config, package exports, tests, documentation page, overview updates, skill registry entry, and file renumbering Adapter architecture includes config interface, factory function, send functions, error handling with try/catch, and configurable timeout via AbortController Config priority follows a four-level hierarchy: function overrides, runtime config nested paths, environment variables with NUXT_{NAME}_* and {NAME}_* prefixes Multi-framework documentation required with code examples for Nuxt/Nitro, Hono, Express, Fastify, Elysia, NestJS, and standalone usage

INSTALLATION
npx skills add https://github.com/hugorcd/evlog --skill create-evlog-adapter
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

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 or null to skip. Use resolveAdapterConfig for the standard precedence (overrides → runtimeConfig.evlog.{name}runtimeConfig.{name}NUXT_{NAME}_*{NAME}_*).
  • **encode(events, config)** — produce { url, headers, body } for a batch of events (or null to skip). HTTP transport, retries, timeout, and error logging are handled by defineHttpDrain.

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 a mode option (see PostHog).
  • No HTTP code in the adapter. Don't call fetch directly — let defineHttpDrain do it. If your service truly needs custom transport (e.g. binary envelopes), use defineDrain and call httpPost from evlog/toolkit.
  • No bespoke config resolution. Always go through resolveAdapterConfig. If you need to support a deprecated alias (tokenapiKey), include both in the ConfigField[] and fall through in resolve().
  • Exported converters. If the service needs a specific event shape, export a to{Name}Event() (or buildPayload()) 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 links array** — add a link entry with icon and path
  • **::card-group section** — add a card block before the Custom card
  • **Zero-Config Setup .env example** — 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
BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card