evlog Documentation

repository·main·Indexed 23 days ago

https://github.com/hugorcd/evlog

An observability tool designed to score and diagnose the information provided by applications during failures. The project includes a CLI for mapping observability coverage, a telemetry dashboard for @evlog/telemetry run events with an MCP endpoint for AI assistants, and integration modules for Nitro v2 and v3.

Tokens
221K
Snippets
607
Records
906
Agent score
81%

What's inside evlog

  1. Overview of evlog framework integrations

    main

    evlog provides native integrations for all major TypeScript frameworks. While the core API (log.set(), createError(), parseError()) remains consistent across all environments, the setup process (bootstrapping) and the method for accessing the request-scoped logger vary depending on the framework.

    If you are not using an HTTP framework, you can use:

    • Standalone TypeScript: For scripts, libraries, and workers.
    • Cloudflare Workers: For edge environments.
  2. Explore evlog use case recipes

    main

    evlog provides several 'recipes' to solve specific problems using its core primitives (wide events, structured errors, drains, and enrichers). These are not separate features or runtimes, but specific implementations of the existing evlog logger and pipeline.

    Available recipes include:

    • Client Logging: Send browser logs to your server with batching, retries, and sendBeacon fallback.
    • AI SDK: Capture AI SDK calls including token usage, tool calls, streaming metrics, and cost.
    • Better Auth: Identify authenticated users (including org/role) on every wide event.
    • Audit Logs: Build tamper-evident audit trails using hash chains, denials, and redaction-aware diffs.
    • Telemetry: Instrument CLIs and automation with one wide event per run, including consent and disclosure.
    • eve: Export wide events from eve agent turns (tokens, tools, drains).
    • Enrichers: Add derived context like User-Agent, geo, request size, or trace context to every event.
  3. Core features and privacy of @evlog/telemetry

    main

    The @evlog/telemetry package provides a wide-event telemetry model designed for tools running on remote machines (CLIs, CI/CD, automation).

    Key Capabilities:

    • Privacy by Shape: Automatically protects sensitive data. It captures booleans and numbers, but for strings, it only captures their presence unless they are explicitly allowlisted. This prevents leaking paths, tokens, or raw argv.
    • Resilience: The telemetry process never throws or blocks the host tool's exit. The flush() operation is hard-capped at 500ms.
    • Data Persistence: For short-lived environments like CI, it uses a disk-buffered NDJSON outbox that drains during the next invocation.
    • Disclosure Management: Use generateDisclosure() to derive markdown and JSON documentation from your runtime configuration to inform users about what is being tracked.
    • Server-side Validation: Use parseIngestBody() to validate POST bodies on your ingestion endpoint, allowing for tool allowlisting, envelope checks, and custom key filtering.

    Opt-out Mechanisms:

    Users can opt-out via:

    • Environment variables: DO_NOT_TRACK or EVLOG_TELEMETRY=0.
    • CLI command: telemetry disable (which also purges undelivered data).
  4. Core capabilities of the evlog Next.js example

    main

    This Next.js example demonstrates the following integration patterns for evlog:

    • Server-side Configuration: Using createEvlog to define enrichers, sampling rates, routes, and the drain pipeline.
    • Event Building: Constructing wide events using log.set(...) across different handler stages.
    • Structured Error Handling: Returning errors with createError, which includes why, fix, and link fields.
    • Client-side Logging: Using EvlogProvider to enable client-side logging via log, setIdentity, and clearIdentity.
    • Log Ingestion: Ingesting client-side logs through the /api/evlog/ingest endpoint.
    • Middleware Integration: Setting up evlogMiddleware (e.g., in a proxy.ts file) to intercept and process requests.
  5. What are Wide Events?

    main

    Wide events are the core concept of evlog. Instead of emitting multiple scattered log lines (e.g., logger.info('started'), logger.info('finished')), you accumulate all relevant context (user data, business state, operation results) over a single unit of work—such as an HTTP request, a background job, or a script—and emit it as one comprehensive, structured event.

    This approach solves common logging problems:

    • Scattered context: All information is contained in one event rather than spread across lines.
    • Correlation: No need to manually pass IDs to match logs; the event is the single source of truth.
    • Reduced Noise: One high-value event replaces many low-value log lines.
    • Completeness: Ensures that even if an operation fails, the accumulated context is captured in the final event.
  6. Understand evlog's core API advantages

    main

    Compared to other loggers, evlog provides several unique API capabilities designed for modern observability:

    • Wide Events: Implements the wide-event observability pattern, allowing you to accumulate context throughout a request and emit one typed event at the end.
    • Sub-operation Logging (log.fork): Allows spinning off a child wide event from a parent, useful for batched operations or per-item processing within a single request while maintaining correlation.
    • Structured Errors: Errors include why (root cause), fix (actionable next step), and link (documentation URL) fields that propagate from server to client.
    • Source Distinction: Automatically carries a source field (server or client) to differentiate error origins in dashboards.
    • Built-in Browser Support: Provides a browser-safe build that strips Node.js APIs and a transport mechanism to batch client-side events and ship them to your server via HTTP.
  7. Configure Head Sampling for log levels

    main

    Head sampling allows you to randomly decide whether to keep a log based on its level before the request completes. This is useful for reducing costs at scale. You provide a rates object where values are percentages (0-100).

    Note: error logs default to 100% and are always logged unless explicitly set to 0.

    initLogger({
      sampling: {
        rates: {
          info: 10,   // Keep 10% of info logs
          warn: 50,   // Keep 50% of warning logs
          debug: 0,   // Disable debug logs
        },
      },
    })
  8. Understand coverage classification

    main

    Entry points are classified into four categories, which are used in the JSON summary block and coverage report rows:

    ClassCriteria
    instrumentedA handler passing both wide-event and context, OR a page that handles its fetch errors
    partialA handler passing exactly one of wide-event or context
    darkA handler passing neither, OR a page that swallows fetch errors
    exemptInfrastructure (e.g., evlog's own code) or a page that fetches nothing. These are counted separately from your own entry points.
  9. What are Wide Events and when to use them

    main

    Wide events are comprehensive log entries that capture all context for a single logical operation (like an HTTP request or a background job) in a single JSON object. Instead of scattering information across multiple log lines, a wide event emits once with all relevant metadata.

    When to use Wide Events

    • HTTP request handling: Yes (one event per request)
    • Background job execution: Yes (one event per job)
    • User actions (login, checkout): Yes (one event per action)
    • Database queries: No (use simple logs)
    • Cache hits/misses: No (include details in the parent wide event)
    • Debug statements: No (remove in production)
  10. Identify exempt entry points in evlog map

    main

    Certain files are automatically treated as exempt and do not count against your instrumentation requirements or observability gaps:

    • Internal Ingest Endpoints: evlog's own client-log ingest endpoints (like /api/evlog/ingest) are considered plumbing. Rules report n/a for these with the reason attached.
    • Static Pages: Pages that fetch no data are exempt. Since they have no data fetching, rules like page-error-handling have nothing to validate and report n/a automatically.
  11. Fan out events to multiple destinations

    main

    To send the same event to multiple destinations (e.g., Axiom for storage and Sentry for errors), create a single drain function that uses Promise.allSettled to dispatch the batch to all destinations in parallel. This ensures that one slow or failing destination does not block the others or cause the entire pipeline to reject.

    Best Practices:

    • Use Promise.allSettled so one failing drain doesn't reject the whole batch.
    • Tune batch and retry settings once at the pipeline level; these settings apply to all destinations.
    • For destinations requiring different filtering (e.g., only sending errors to Sentry), use per-drain minLevel options or wrap the destination in a filter function.
    import { createDrainPipeline } from 'evlog/pipeline'
    import { createAxiomDrain } from 'evlog/axiom'
    import { createDatadogDrain } from 'evlog/datadog'
    import { createSentryDrain } from 'evlog/sentry'
    import { createFsDrain } from 'evlog/fs'
    import type { DrainContext }
    
    const pipeline = createDrainPipeline<DrainContext>({
      batch: { size: 50, intervalMs: 5000 },
      retry: { maxAttempts: 3 },
      maxBufferSize: 1000,
    })
    
    const axiom = createAxiomDrain()
    const datadog = createDatadogDrain()
    const sentry = createSentryDrain({ minLevel: 'error' })
    const fs = createFsDrain({ dir: '.evlog/logs', maxFiles: 14 })
    
    export const drain = pipeline(async (batch) => {
      await Promise.allSettled([
        axiom(batch),
        datadog(batch),
        sentry(batch),
        fs(batch),
      ])
    })
  12. Understand the evlog core concept: Wide Events

    main

    Unlike traditional line-by-line JSON logging (like Pino), evlog is built around the concept of wide events. Instead of a stream of disconnected log lines, evlog accumulates context and emits a single, structured, queryable row for an event.

    This allows you to capture multiple dimensions in one place, such as:

    • User context (e.g., user.plan)
    • Business metrics (e.g., cart.total)
    • Technical metadata (e.g., status, duration, flags)
    • Structured error details (e.g., why, fix, link)

    This model enables powerful querying, such as filtering by status >= 400 or grouping by user.plan across a single event record.