Create Evlog Framework Integration
Create a new evlog framework integration to add automatic wide-event logging to an HTTP framework. Use when adding middleware/plugin support for a framework (e.g., Koa, H3 standalone, Deno Fresh, etc.) to the evlog package. Covers source code, build config, package exports, tests, example app, and all documentation.
- Skill ID
- evloghq/evlog/create-evlog-framework-integration
- Publisher
- evloghq
- Repository
- evlog
- Installs
- 0
- Files
- 1
- Synced
- Sep 23, 2026
Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.
evloghq/evlog/create-evlog-framework-integrationInstalls these files- SKILL.md
What this skill tells the agent
Create evlog Framework Integration
Add a new framework integration to evlog. The recommended path is the manifest mode built on defineFrameworkIntegration from evlog/toolkit, for any framework with a request/response middleware shape. For frameworks with a fundamentally different lifecycle you'll fall back to the lower-level createMiddlewareLogger.
Two paths
- Manifest mode (preferred, ~30–80 lines of glue). Call
defineFrameworkIntegration({ name, extractRequest, attachLogger, storage? })once at module level, then write a tiny middleware that callsintegration.start(ctx, options)and runs the framework'snext()insiderunWith. Reference implementations: all ofpackages/evlog/src/{hono,express,fastify,elysia,nestjs,orpc,react-router,sveltekit,workers}/index.tsuse it. - Custom mode: use
createMiddlewareLoggerdirectly when the framework's lifecycle doesn't fit a standard middleware. Current custom-mode integrations: Next.js (src/next/), Nitro v2/v3 (src/nitro/,src/nitro-v3/), Eve (src/eve/).
Manifest mode now covers all classic HTTP frameworks. Use custom mode only when you can't extract a request synchronously at the start of the lifecycle (server actions, module-level hooks, agent turns).
Required API surface (from AGENTS.md)
Every framework integration must expose:
evlog()middleware/plugin accepting the fullBaseEvlogOptions(drain,enrich,keep,include,exclude,routes,plugins)useLogger()(ALS-backed). Workers is the one sanctioned exception (ALS needs a compat flag there;defineWorkerFetchattaches the logger instead)log.fork()support (automatic whenstorageis provided to the manifest)- The framework-native accessor (
c.get('log'),req.log,event.locals.log, …)
PR Title
feat({framework}): add {Framework} middleware integrationScope timing caveat: the semantic PR check reads its scope list from the base branch, so a brand-new scope can't validate the very PR that introduces it. Either register the scope in a small preceding PR, or use an unscoped title (feat: add {Framework} middleware integration) on the introducing PR.
Touchpoints Checklist
| # | File | Action |
|---|---|---|
| 1 | packages/evlog/src/{framework}/index.ts | Create integration source |
| 2 | packages/evlog/tsdown.config.ts | Add build entry + external |
| 3 | packages/evlog/package.json | Add exports + typesVersions + optional peer dep + keyword |
| 4 | packages/evlog/test/frameworks/{framework}.test.ts | Create tests (real request driver + describeStandardHttpMatrix) |
| 5 | packages/evlog/test/toolkit/__snapshots__/api-surface.test.ts.snap | Regenerated by pnpm run build + pnpm test |
| 6 | apps/docs/content/4.integrate/frameworks/{NN}.{framework}.md | Create framework docs page |
| 7 | apps/docs/content/4.integrate/frameworks/00.overview.md | Add table row + card |
| 8 | apps/docs/content/1.start/3.installation.md | Add card in "Choose Your Framework" |
| 9 | apps/docs/content/0.landing.md | Add framework code snippet slot |
| 10 | apps/docs/app/components/features/FeatureFrameworks.vue | Add framework tab |
| 11 | skills/review-logging-patterns/SKILL.md | Add framework setup section + update frontmatter description |
| 12 | packages/evlog/README.md | Add framework section + row in the Framework Support table |
| 13 | examples/{framework}/ | Create example app with test UI (auto-discovered by pnpm example {framework}, no root script needed) |
| 14 | .changeset/{framework}-integration.md | Create changeset (minor) |
| 15 | .github/workflows/semantic-pull-request.yml + .github/pull_request_template.md | Register {framework} as a PR scope in both files |
Important: Do NOT consider the task complete until all 15 touchpoints have been addressed.
Naming Conventions
| Placeholder | Example (Hono) | Usage |
|---|---|---|
{framework} | hono | Directory names, import paths, file names, PR scope |
{Framework} | Hono | PascalCase in type/interface names |
Shared Utilities
All integrations share the same core utilities. Never reimplement logic that exists in shared/. These are also publicly available as evlog/toolkit for community-built integrations (see Custom Integration docs).
| Utility | Location | Purpose |
|---|---|---|
defineFrameworkIntegration | ../shared/integration | Manifest factory: extract request, create logger, attach, run with ALS |
createMiddlewareLogger | ../shared/middleware | Lower-level lifecycle (custom mode): logger creation, route filtering, tail sampling, emit, enrich, drain |
BaseEvlogOptions | ../shared/middleware | Base user-facing options type with drain, enrich, keep, include, exclude, routes, plugins |
createLoggerStorage | ../shared/storage (evlog/toolkit/storage) | Factory returning { storage, useLogger } for AsyncLocalStorage-backed useLogger(). Prefer evlog/toolkit/storage on Workers / edge |
shouldDeferEmitForResponse | ../shared/streamResponse | Defer the wide event until a streaming body closes (see Hono/Elysia) |
defineFrameworkIntegration automatically:
- normalizes both Web
Headersand NodeIncomingHttpHeaders(so you don't need to pick a header extractor) - generates a
requestIdwhen none is present - calls
createMiddlewareLoggerand surfaces its{ logger, finish, skipped, middlewareOptions } - attaches
log.fork()automatically whenstorageis provided - exposes
runWith(fn)to run downstream handlers inside the integration's ALS - forwards
waitUntilwhen the runtime provides one (Workers, Hono on Workers)
Step 1: Integration Source
Create packages/evlog/src/{framework}/index.ts.
Template Structure (manifest mode)
import type { AuditableLogger } from '../audit'
import { defineFrameworkIntegration } from '../shared/integration'
import type { BaseEvlogOptions } from '../shared/middleware'
import { createLoggerStorage } from '../shared/storage'
const { storage, useLogger } = createLoggerStorage(
'middleware context. Make sure the evlog middleware is registered before your routes.',
'evlog:{framework}',
)
export type Evlog{Framework}Options = BaseEvlogOptions
export { useLogger }
// Type augmentation for typed logger access (framework-specific):
// - Express: declare module 'express-serve-static-core' { interface Request { log: AuditableLogger } }
// - Hono: export type EvlogVariables = { Variables: { log: AuditableLogger } }
const integration = defineFrameworkIntegration<{Framework}Context>({
name: '{framework}',
extractRequest: (ctx) => ({
method: /* ctx.method */,
path: /* ctx.path */,
headers: /* Web Headers OR Node headers OR plain object */,
requestId: /* x-request-id header or undefined → auto-generated */,
}),
attachLogger: (ctx, logger) => {
// Store in framework-idiomatic location:
// - Hono: c.set('log', logger)
// - Express: req.log = logger
},
storage,
})
export function evlog(options: Evlog{Framework}Options = {}): FrameworkMiddleware {
return async (ctx, next) => {
const { skipped, finish, runWith } = integration.start(ctx, options)
if (skipped) {
await next()
return
}
try {
await runWith(() => next())
await finish({ status: /* extract status from ctx */ })
} catch (error) {
await finish({ error: error as Error })
throw error
}
}
}Reference Implementations
- Hono:
src/hono/index.ts.c.set('log', logger)+ ALSuseLogger(), streaming deferral viashouldDeferEmitForResponse,waitUntildetection - Express:
src/express/index.ts.req.log, ALS storage,res.on('finish')for terminal status
More from evloghq
Use this skill in a project
Open any RiverX project, search the Skills panel for evloghq/evlog/create-evlog-framework-integration, and install it in one click.
