@evlog/nuxthub stores your evlog wide events directly in your NuxtHub database. No external logging service needed — your logs live next to your data, with automatic cleanup based on a retention policy.
Why Self-Hosted Logs?
External logging services (Axiom, Datadog, etc.) are great for production at scale. But sometimes you want:
- Zero external dependencies — logs stored in the same database as your app
- Full data ownership — no third-party access to your log data
- Free tier friendly — no per-event pricing, just your existing database
- Development & staging — full log visibility without paying for a service
@evlog/nuxthub works as a drop-in drain. Your existing evlog setup stays the same — you just get a database-backed storage layer on top.
Install
pnpm add @evlog/nuxthub
npm install @evlog/nuxthub
yarn add @evlog/nuxthub
bun add @evlog/nuxthub
Or with nuxi:
npx nuxi module add @evlog/nuxthub
Setup
Add the module to your nuxt.config.ts:
export default defineNuxtConfig({
modules: ['@evlog/nuxthub'],
evlog: {
retention: '7d',
},
})
That's it. The module automatically:
- Installs
evlog/nuxtand@nuxthub/coreif not already registered - Registers the
evlog_eventsdatabase schema with NuxtHub - Hooks into
evlog:drainto store every event in the database - Schedules a cleanup task based on your retention policy
@evlog/nuxthub uses Drizzle ORM to interact with the database.How It Works
Request → evlog wide event → evlog:drain hook → INSERT into evlog_events table
↓
Cron task (automatic) → DELETE events older than retention
Every wide event emitted by evlog is stored as a row in the evlog_events table. The drain plugin handles both single events and batches (when used with the pipeline).
Database Schema
The evlog_events table stores indexed columns for fast querying and a data JSON column for all remaining fields:
| Column | Type | Description |
|---|---|---|
id | text | UUID primary key |
timestamp | text | Event timestamp |
level | text | Log level (info, warn, error, debug) |
service | text | Service name |
environment | text | Environment (production, staging, etc.) |
method | text | HTTP method |
path | text | Request path |
status | integer | HTTP status code |
duration_ms | integer | Request duration in milliseconds |
request_id | text | Request correlation ID |
source | text | Event source (server, client) |
error | text | Error details (JSON string) |
data | text | All remaining event fields (JSON) |
created_at | text | Row insertion timestamp |
Indexed columns: timestamp, level, service, status, request_id, created_at.
Dialect Support
The schema is automatically registered for your NuxtHub database dialect:
- SQLite (default for Cloudflare D1)
- MySQL
- PostgreSQL
The correct schema is selected via the hub:db:schema:extend hook based on your NuxtHub configuration.
Combining with External Adapters
@evlog/nuxthub doesn't replace external adapters — you can use both. The module registers its own evlog:drain hook, so any other drain plugins you have will still work:
import { createAxiomDrain } from 'evlog/axiom'
export default defineNitroPlugin((nitroApp) => {
// This runs alongside @evlog/nuxthub's built-in drain
nitroApp.hooks.hook('evlog:drain', createAxiomDrain())
})
Next Steps
- Retention & Cleanup — Configure retention policy and cleanup scheduling