Skip to main content

review-logging-patterns

Review code for logging patterns and suggest evlog adoption. Optionally use @evlog/cli (`evlog init` to wire evlog, `evlog agents` to write the conventions into AGENTS.md, `evlog map` to score entry-point coverage, `--baseline` to gate regressions in CI) on Nuxt, Nitro, Next.js, TanStack Start, and Hono. Guides setup on those plus SvelteKit, React Router, NestJS, Express, Fastify, Elysia, oRPC, Cloudflare Workers, AWS Lambda, Astro, and standalone TypeScript. Detects console.log spam, unstructured errors, and missing context. Covers wide events, structured errors, drain adapters (Axiom, OTLP, HyperDX, PostHog, Sentry, Better Stack, Datadog, Loki, ClickHouse, NuxtHub, Memory), sampling, enrichers, and AI SDK integration.

インストールへ移動

ソース情報

リポジトリ
evloghq/evlog
ソースの最終更新活動
2026年9月17日 21:10
検出された SKILL.md の言語
英語
スター
1,863
フォーク
62

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

ファイルエクスプローラー
5 ファイル

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
review-logging-patterns
description
Review code for logging patterns and suggest evlog adoption. Optionally use @evlog/cli (`evlog init` to wire evlog, `evlog agents` to write the conventions into AGENTS.md, `evlog map` to score entry-point coverage, `--baseline` to gate regressions in CI) on Nuxt, Nitro, Next.js, TanStack Start, and Hono. Guides setup on those plus SvelteKit, React Router, NestJS, Express, Fastify, Elysia, oRPC, Cloudflare Workers, AWS Lambda, Astro, and standalone TypeScript. Detects console.log spam, unstructured errors, and missing context. Covers wide events, structured errors, drain adapters (Axiom, OTLP, HyperDX, PostHog, Sentry, Better Stack, Datadog, Loki, ClickHouse, NuxtHub, Memory), sampling, enrichers, and AI SDK integration.
license
MIT
metadata
{"author":"HugoRCD","version":"0.9"}
# Review logging patterns Review and improve logging patterns in TypeScript/JavaScript codebases. Transform scattered console.logs into structured wide events and convert generic errors into self-documenting structured errors. ## When to Use - Setting up evlog in a new or existing project (any supported framework) - Reviewing code for logging best practices - Converting console.log statements to structured logging - Improving error handling with better context - Configuring log draining, sampling, or enrichment ## Quick Reference | Working on... | Resource | | ----------------------- | ------------------------------------------------------------------ | | Setup (CLI) | [`evlog init`](https://www.evlog.dev/cli/init): wire evlog into the project | | Project conventions (CLI) | [`evlog agents`](https://www.evlog.dev/cli/agents): write the evlog block into the project's AGENTS.md | | Coverage map (CLI) | [`evlog map`](https://www.evlog.dev/cli/map): score dark entry points | | CI gating (CLI) | [`evlog map --min-score / --baseline`](https://www.evlog.dev/cli/ci): gate regressions | | Wide events patterns | [references/wide-events.md](references/wide-events.md) | | Error handling | [references/structured-errors.md](references/structured-errors.md) | | Code review checklist | [references/code-review.md](references/code-review.md) | | Drain pipeline | [references/drain-pipeline.md](references/drain-pipeline.md) | | Audit logs | [build-audit-logs](../build-audit-logs/SKILL.md) skill + [docs](https://www.evlog.dev/use-cases/audit/overview) | ## Audit logs For security-sensitive actions (auth, billing, admin, data export), use evlog's audit layer: a typed `audit` field on wide events, not a parallel logger. See the **`build-audit-logs`** skill for end-to-end setup (`log.audit`, `withAudit`, denials, `auditEnricher`, `auditOnly`, `signed`, `mockAudit`). ```typescript log.audit({ action: 'invoice.refund', actor: { type: 'user', id: user.id }, target: { type: 'invoice', id: invoice.id }, outcome: 'success', }) ``` Docs: https://www.evlog.dev/use-cases/audit/overview ## Installation ```bash npm install evlog ``` ## Use the CLI (recommended on Nuxt, Nitro, Next.js, TanStack Start, Hono) `@evlog/cli` is a **separate package** from `evlog`, early but worth trying. It reads the project on disk (no traffic, no config). On the five supported frameworks it covers the whole loop: **wire evlog in** (`init`), **score coverage** (`map`), **lock the score in CI** (`--min-score`, `--baseline`). If the CLI is unavailable, the framework has no adapter yet, or the user declines, continue with the manual sections below; the skill does not depend on it. **Ask before installing anything**; prefer `npx` / `pnpm dlx` for one-shots. ### 1. Setup: `evlog init` On a project that doesn't use evlog yet, prefer `init` over hand-writing the setup, since it detects the framework, reads what the project already has, and generates config, drains, enrichers, and extras in one pass. It is fully scriptable for agents: ```bash # preview everything without writing (always start here) npx @evlog/cli init --dry-run --yes # then apply — flags instead of prompts npx @evlog/cli init --yes \ --service my-app \ --drain fs \ --prodDrain axiom \ --extras enrichers,pipeline,sampling \ --sampling medium ``` Useful flags: `--framework` (override detection: `nuxt`, `nitro`, `next`, `tanstack-start`, `hono`), `--prodDrain` (comma-separated: `axiom`, `otlp`, `posthog`, `sentry`, `better-stack`, `datadog`, `hyperdx`), `--extras` (`enrichers`, `pipeline`, `sampling`, `vite`, `error-catalog`, `audit-catalog`, `ai`, `better-auth`), `--enrichers`, `--sampling` (traffic tier: `all`, `low`, `medium`, `high`, `very-high`), `--apps` (monorepo: which workspace packages), `--no-install`. Review the `--dry-run` output with the user before applying. Docs: https://www.evlog.dev/cli/init ### 2. Score: `evlog map` ```bash npx @evlog/cli map --no-write # agents: npx @evlog/cli map --json --no-write ``` What you get: - A project score and which entry points are still dark - **FIX FIRST**: the three most valuable places to fix - **GOING FURTHER**: opportunities (catalogs, audit coverage, AI logging, auth identity) that never cost points - Per-file inspect: `npx @evlog/cli map <file> --no-write` shows the shape the handler could take - Re-run after fixes and watch the score move Work FIX FIRST in order, keep changes minimal (`useLogger()`, `log.set()`, `log.audit()`, `createError({ why, fix })`), then re-run with `--no-write`. Omit `--no-write` only when the user wants `evlog.map.json` written. ### 3. Lock it in CI: `--min-score` and `--baseline` After fixing, propose making the score durable. This is where the CLI earns its keep: ```bash # in CI, after pnpm add -D @evlog/cli (project-local, pinned by the lockfile) pnpm exec evlog map --min-score 80 # absolute gate: exits 1 below the threshold pnpm exec evlog map --baseline # ratchet: exits 1 if this PR made things worse ``` `--baseline` compares the fresh scan against the committed `evlog.map.json`, **per entry point and per requirement**, so a refactor that instruments one route and breaks another fails even if the total score is unchanged. Disabling a passing check with a comment counts as a regression too. New uninstrumented routes are listed as `NEW AND DARK` without failing. Workflow: commit `evlog.map.json` once, add the `--baseline` run to CI (`pnpm add -D @evlog/cli` for a pinned version, and ask first), then re-run `map` without `--baseline` to accept an intentional change. Docs: https://www.evlog.dev/cli/ci Early days: adapters and rules are still evolving; expect scores to move between releases. Docs: https://www.evlog.dev/cli/map · Rules: https://www.evlog.dev/cli/rules --- ## Framework Setup ### Nuxt ```typescript // nuxt.config.ts export default defineNuxtConfig({ modules: ['evlog/nuxt'], evlog: { env: { service: 'my-app' }, include: ['/api/**'], }, }) ``` `useLogger`, `log`, and `parseError` are **auto-imported**. `createError` is not: a bare one resolves to h3's, which drops `why`, `fix`, and `link`. Import it from `evlog`. ```typescript // server/api/checkout.post.ts — no imports needed export default defineEventHandler(async (event) => { const log = useLogger(event) log.set({ user: { id: user.id, plan: user.plan } }) return { success: true } }) ``` Drain, enrich, and tail sampling use Nitro hooks in server plugins: ```typescript // server/plugins/evlog-drain.ts import { createAxiomDrain } from 'evlog/axiom' export default defineNitroPlugin((nitroApp) => { nitroApp.hooks.hook('evlog:drain', createAxiomDrain()) }) ``` Client transport (auto-configured Vue plugin): ```typescript // nuxt.config.ts evlog: { transport: { enabled: true }, // logs sent to /api/_evlog/ingest } ``` Client-side: `log`, `setIdentity`, `clearIdentity` are auto-imported in components. ### Next.js **Step 1: Create central config.** All exports come from here: ```typescript // lib/evlog.ts import type { DrainContext } from 'evlog' import { createEvlog } from 'evlog/next' import { createUserAgentEnricher, createRequestSizeEnricher } from 'evlog/enrichers' import { createDrainPipeline } from 'evlog/pipeline' const enrichers = [createUserAgentEnricher(), createRequestSizeEnricher()] const pipeline = createDrainPipeline<DrainContext>({ batch: { size: 50, intervalMs: 5000 } }) const drain = pipeline(createAxiomDrain({ dataset: 'logs', apiKey: process.env.AXIOM_API_KEY! })) export const { withEvlog, useLogger, log, createError } = createEvlog({ service: 'my-app', sampling: { rates: { info: 10 }, keep: [{ status: 400 }, { duration: 1000 }], }, routes: { '/api/auth/**': { service: 'auth-service' }, '/api/checkout/**': { service: 'checkout-service' }, }, keep: (ctx) => { const user = ctx.context.user as { premium?: boolean } | undefined if (user?.premium) ctx.shouldKeep = true }, enrich: (ctx) => { for (const enricher of enrichers) enricher(ctx) }, drain, }) ``` **Step 2: Wrap route handlers** with `withEvlog()`: ```typescript // app/api/checkout/route.ts import { withEvlog, useLogger } from '@/lib/evlog' export const POST = withEvlog(async (request: Request) => { const log = useLogger() // Zero arguments — uses AsyncLocalStorage log.set({ user: { id: 'user_123', plan: 'enterprise' } }) log.set({ cart: { items: 3, total: 14999 } }) return Response.json({ success: true }) }) ``` **Step 3: Server Actions.** Same `withEvlog()` wrapper: ```typescript // app/actions.ts 'use server' import { withEvlog, useLogger } from '@/lib/evlog' export const checkout = withEvlog(async (formData: FormData) => { const log = useLogger() log.set({ action: 'checkout', source: 'server-action' }) return { success: true } }) ``` **Step 4: Middleware** (optional, sets `x-request-id` + timing headers): ```typescript // proxy.ts import { evlogMiddleware } from 'evlog/next' export const proxy = evlogMiddleware() export const config = { matcher: ['/api/:path*'] } ``` **Step 5: Client Provider.** Wrap the root layout: ```tsx // app/layout.tsx import { EvlogProvider } from 'evlog/next/client' export default function Layout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <body> <EvlogProvider service="my-app" transport={{ enabled: true, endpoint: '/api/evlog/ingest' }}> {children} </EvlogProvider> </body> </html> ) } ``` **Step 6: Client logging.** In any client component: ```tsx 'use client' import { log, setIdentity, clearIdentity } from 'evlog/next/client' setIdentity({ userId: 'usr_123' }) log.info({ action: 'checkout_click' }) clearIdentity() ``` **Step 7 (optional): Instrumentation.** Startup plus global `onRequestError` (SSR/RSC errors outside `withEvlog`). Use `defineNodeInstrumentation(() => import('./lib/evlog'))` in root `instrumentation.ts` to gate Node + cache the import, **or** write `register`/`onRequestError` manually. Both are valid. For custom logic, wrap evlog’s `register`/`onRequestError` inside `lib/evlog.ts` (compose with your own init or metrics), then re-export. Export `createInstrumentation()` from `lib/evlog.ts` alongside `createEvlog()`. See framework docs for coexistence with `lockLogger`. **Step 8: Client ingest endpoint.** Receives client logs: ```typescript // app/api/evlog/ingest/route.ts import { NextRequest } from 'next/server' const VALID_LEVELS = ['info', 'error', 'warn', 'debug'] as const export async function POST(request: NextRequest) { const origin = request.headers.get('origin') const host = request.headers.get('host') if (origin && new URL(origin).host !== host) { return Response.json({ error: 'Invalid origin' }, { status: 403 }) } const body = await request.json()
GitHubで見る
この SKILL.md は非常に大きいため、SkillsMP では最初のセクションだけを表示しています。 GitHubで見る