SDKs
LogVista provides a TypeScript SDK package (@orphnet/logvista-sdks) with first-class adapters for Hono, Nuxt, Node.js, and browsers. The SDK handles batching, retries, redaction, and auto-capture so you can focus on your application.
Installation
bun add @orphnet/logvista-sdksLocal Development with bun link
When developing against an unreleased version of the SDK:
# 1. In the SDK repo — link the package
cd /path/to/logging-packages
bun link
# 2. In your project — use the linked package
cd /path/to/my-project
bun link @orphnet/logvista-sdks
# 3. In the SDK repo — rebuild on changes
bun run devChanges to the SDK source are immediately available in your project after the incremental build. Run bun unlink @orphnet/logvista-sdks in your project when done.
The package ships as a single npm module with subpath exports:
| Import | Runtime | Description |
|---|---|---|
@orphnet/logvista-sdks | Any | Core logger, transports, types |
@orphnet/logvista-sdks/hono | Cloudflare Workers | Hono middleware with auto-capture |
@orphnet/logvista-sdks/nuxt | Nuxt 3 | Nuxt module with composables |
@orphnet/logvista-sdks/node | Node.js / Bun | Node adapter with graceful shutdown |
@orphnet/logvista-sdks/browser | Browser | Browser adapter with sendBeacon flush |
Prerequisites
Before using the SDK, you need:
- A LogVista account and workspace
- A project with an API key (
sk_...prefix) - The API key must have
logs:writescope
See Getting Started and API Keys for setup instructions.
Core Concepts
Logger Instance
Every SDK adapter creates a LogVista instance with a .logger property. The logger provides:
- Category methods —
logger.ai(),logger.http(),logger.database(), etc. - Level methods —
logger.info(),logger.error(),logger.fatal(), etc. - Raw method —
logger.log()for full control
import { createLogVista } from '@orphnet/logvista-sdks'
const logvista = createLogVista({
apiKey: 'sk_...',
apiUrl: 'https://api.logvista.orph.dev',
projectId: 'my-project',
})
const logger = logvista.loggerLog Levels
Levels are ordered by severity. The level config option sets the minimum threshold — logs below it are dropped before reaching the transport.
| Level | Severity | Use Case |
|---|---|---|
trace | 0 | High-volume diagnostics |
debug | 1 | Development debugging |
log | 2 | General purpose |
info | 3 | Informational events (default) |
warn | 4 | Potential issues |
error | 5 | Errors requiring attention |
fatal | 6 | Unrecoverable failures |
Categories
Each log belongs to a category with typed sub-types. Categories map to the Log Categories system in the API.
| Category | Types | Use Case |
|---|---|---|
ai | llm, interaction, step, action, response, summary, embedding, tool-call | AI/LLM operations |
http | request, response, redirect | HTTP traffic |
system | startup, shutdown, error, debug, info, warn | Application lifecycle |
database | query, transaction, migration, slow-query, connection | Database operations |
auth | login, logout, token-refresh, permission-denied, registration | Authentication events |
queue | enqueue, dequeue, process, retry, dead-letter | Queue/job processing |
cache | hit, miss, set, evict, invalidate | Cache operations |
custom | any string | Custom events |
Fire-and-Forget Semantics
The SDK never throws and never blocks your application:
- Logger calls return synchronously
- Transport delivery is asynchronous
- Failed batches retry with exponential backoff
- After max retries, logs are dropped (with
onDropcallback) - Transport errors fire
onErrorcallback but never propagate
Hono Adapter
The Hono adapter provides middleware that auto-captures HTTP requests and responses while making the logger available on Hono's context.
Basic Setup
import { Hono } from 'hono'
import { logvistaMiddleware } from '@orphnet/logvista-sdks/hono'
const app = new Hono()
app.use('*', logvistaMiddleware({
apiKey: 'sk_...',
apiUrl: 'https://api.logvista.orph.dev',
projectId: 'my-api',
}))
app.get('/users', (c) => {
const logger = c.get('logger')
logger.database('query', { message: 'Listing users', data: { table: 'users' } })
return c.json({ users: [] })
})
export default appAuto-Capture
By default, the middleware logs every HTTP request and response:
Request log:
{
"level": "info",
"category": "http",
"type": "request",
"message": "GET /users",
"data": {
"method": "GET",
"path": "/users",
"headers": { "authorization": "[REDACTED]", "content-type": "application/json" },
"userAgent": "Mozilla/5.0 ..."
}
}Response log:
{
"level": "info",
"category": "http",
"type": "response",
"message": "GET /users → 200 (12ms)",
"data": {
"method": "GET",
"path": "/users",
"status": 200,
"duration": 12,
"contentLength": "256"
}
}Response log levels adjust automatically: info for 2xx/3xx, warn for 4xx, error for 5xx.
Auto-Capture Configuration
app.use('*', logvistaMiddleware({
apiKey: 'sk_...',
apiUrl: 'https://api.logvista.orph.dev',
projectId: 'my-api',
autoCapture: {
requests: true, // Log incoming requests
responses: true, // Log outgoing responses
errors: true, // Log uncaught handler errors
// Path filtering
excludePaths: ['/health', '/ready', '/favicon.ico'],
excludeMethods: ['OPTIONS'],
includePaths: null, // Allow-list (overrides exclude)
// Redaction
redactHeaders: ['authorization', 'cookie', 'x-api-key'],
redactBodyFields: ['password', 'token', 'secret'],
// Body capture (opt-in — can be expensive)
captureRequestBody: false,
captureResponseBody: false,
maxBodySize: 32768, // 32KB truncation limit
// Extras
captureQueryParams: true,
timingHeader: 'X-Response-Time', // null to disable
},
}))Lifecycle Hooks
Transform or skip auto-captured logs with lifecycle hooks:
app.use('*', logvistaMiddleware({
// ...
onRequest: (c, log) => {
// Add custom data
return { ...log, data: { ...log.data, userId: c.get('userId') } }
},
onResponse: (c, log) => {
// Skip 404 responses
if (log.data?.status === 404) return null
return log
},
onErrorLog: (c, error, log) => {
// Enrich error logs
return { ...log, data: { ...log.data, requestId: c.get('requestId') } }
},
}))Cloudflare waitUntil
The middleware wraps log flushing in executionCtx.waitUntil() so delivery happens after the response is sent:
app.use('*', logvistaMiddleware({
// ...
waitUntil: true, // Default — logs don't block the response
}))Typed Context
The logger is available on Hono's context with full TypeScript support:
app.post('/chat', async (c) => {
const logger = c.get('logger') // LogVistaLogger — fully typed
logger.ai('llm', {
message: 'GPT-4 call started',
data: { model: 'gpt-4', prompt_tokens: 200 },
})
const result = await callLLM()
logger.ai('response', {
message: 'GPT-4 call completed',
data: { completion_tokens: 150, duration: 820 },
})
return c.json(result)
})Nuxt Module
The Nuxt module auto-registers server middleware, client plugins, composables, and a proxy endpoint for secure client-side logging.
Setup
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@orphnet/logvista-sdks/nuxt'],
logvista: {
apiKey: process.env.LOGVISTA_API_KEY,
apiUrl: 'https://api.logvista.orph.dev',
projectId: 'my-nuxt-app',
level: 'info',
server: {
enabled: true,
autoCapture: {
requests: true,
responses: true,
errors: true,
excludePaths: ['/api/_health', '/_nuxt/**'],
redactHeaders: ['authorization', 'cookie'],
},
},
client: {
enabled: true,
autoCapture: {
errors: true, // window.onerror + unhandledrejection
navigation: true, // Route change tracking
fetchErrors: true, // Failed $fetch / fetch calls
},
},
defaultMeta: {
env: process.env.NODE_ENV,
service: 'my-nuxt-app',
},
},
})Client-Side — useLogger()
<script setup>
const logger = useLogger()
async function submitForm() {
logger.info('Form submitted', { data: { formId: 'contact' } })
try {
await $fetch('/api/contact', { method: 'POST', body: formData })
logger.info('Form submission successful')
} catch (error) {
logger.error('Form submission failed', { data: { error: error.message } })
}
}
</script>Server-Side — useLogVista(event)
// server/api/users.get.ts
export default defineEventHandler((event) => {
const logger = useLogVista(event)
logger.database('query', { message: 'Fetching user list' })
const users = await db.query('SELECT * FROM users')
logger.info(`Found ${users.length} users`)
return users
})Cloudflare Pages waitUntil
When deployed to Cloudflare Pages, the Nuxt server middleware automatically detects the Pages runtime and uses event.context.cloudflare.context.waitUntil() to flush logs after the response is sent. No configuration needed — it just works.
This ensures logs are delivered even after the response completes and the Pages Function starts winding down.
Client-Side Security
Client-side logs are routed through a Nitro proxy endpoint (POST /api/_logvista). The API key stays on the server — no credentials are exposed to the browser.
Auto-Captured Client Events
Route navigation:
{
"level": "info",
"category": "http",
"type": "request",
"message": "Navigation: /dashboard → /settings",
"data": { "from": "/dashboard", "to": "/settings" }
}Unhandled error:
{
"level": "error",
"category": "system",
"type": "error",
"message": "TypeError: Cannot read properties of undefined",
"data": { "stack": "...", "url": "/settings" }
}Failed fetch:
{
"level": "warn",
"category": "http",
"type": "response",
"message": "GET /api/users → 500",
"data": { "url": "/api/users", "status": 500, "duration": 340 }
}Node.js / Bun
For plain Node.js or Bun servers without a framework:
import { createLogVista } from '@orphnet/logvista-sdks/node'
const logvista = createLogVista({
apiKey: process.env.LOGVISTA_API_KEY!,
apiUrl: 'https://api.logvista.orph.dev',
projectId: 'my-service',
shutdownOnExit: true, // SIGTERM/SIGINT → flush before exit
})
const logger = logvista.logger
logger.info('Service started')
logger.database('query', { message: 'Connected to database' })
// Manual shutdown (e.g., in test teardown)
await logvista.shutdown()Browser (Vanilla)
For browser apps without Nuxt (React, Svelte, vanilla JS, etc.):
import { createLogVista } from '@orphnet/logvista-sdks/browser'
const logvista = createLogVista({
apiUrl: '/api/_logvista', // Proxy through your backend
projectId: 'my-app',
autoCapture: {
errors: true, // window.onerror + unhandledrejection
fetchErrors: true, // Intercept failed fetch calls
},
flushOnVisibilityChange: true, // Flush via sendBeacon when tab hides
flushOnUnload: true, // Flush on beforeunload
})
const logger = logvista.logger
logger.info('App loaded')TIP
Use navigator.sendBeacon() for reliable delivery when the user navigates away or closes the tab. The browser adapter handles this automatically.
Configuration Reference
Core Options
All adapters accept these core options:
| Option | Type | Default | Description |
|---|---|---|---|
apiKey | string | — | API key (sk_...) |
apiUrl | string | required | LogVista API URL |
projectId | string | required | Project ID |
transport | string | Transport | Transport[] | 'batch' | Transport type or instance(s) |
level | LogLevel | 'info' | Minimum log level |
categories | LogCategory[] | null | Allow-list (null = all) |
excludeCategories | LogCategory[] | null | Deny-list |
sampling.rate | number | 1.0 | Sampling rate (0.0–1.0) |
sampling.overrides | Record<LogLevel, number> | — | Per-level sampling |
defaultMeta | object | {} | Metadata added to every log |
sessionId | string | auto | Session identifier |
beforeSend | (log) => log | null | — | Transform or drop logs |
onError | (error, logs) => void | — | Transport failure callback |
onDrop | (logs, reason) => void | — | Dropped logs callback |
debug | boolean | false | Log SDK internals |
Batch Options
| Option | Type | Default | Description |
|---|---|---|---|
batch.size | number | 50 | Max buffer before flush |
batch.flushInterval | number | 5000 | Flush interval (ms) |
batch.maxRetries | number | 3 | Max retries per batch |
batch.backoff | string | function | 'exponential' | Backoff strategy |
batch.backoffBase | number | 1000 | Base delay (ms) |
Extending Categories
Add custom categories to the type system via TypeScript declaration merging:
// types/logvista.d.ts
declare module '@orphnet/logvista-sdks' {
interface CategoryRegistry {
billing: 'charge' | 'refund' | 'subscription'
email: 'send' | 'bounce' | 'delivery'
}
}Now you get full type safety for your custom categories:
logger.billing('charge', {
message: 'Payment processed',
data: { amount: 99.99, currency: 'USD' },
})Custom Transports
Build your own transport to send logs anywhere:
import type { Transport, LogEntry } from '@orphnet/logvista-sdks'
class DatadogTransport implements Transport {
send(log: LogEntry): void {
// Forward to Datadog, Sentry, etc.
}
async flush(): Promise<void> {
// Flush buffered entries
}
}
const logvista = createLogVista({
apiUrl: 'https://api.logvista.orph.dev',
projectId: 'my-project',
// Use multiple transports simultaneously
transport: [new BatchTransport(config), new DatadogTransport()],
})Cloudflare Runtime Support
Both adapters handle waitUntil automatically for reliable log delivery on edge runtimes:
| Runtime | waitUntil path | Adapter |
|---|---|---|
| Cloudflare Workers (Hono) | c.executionCtx.waitUntil() | Hono middleware |
| Cloudflare Pages (Nuxt) | event.context.cloudflare.context.waitUntil() | Nuxt server middleware |
| Node.js / Bun | N/A — process stays alive | Batch timer + shutdownOnExit |
| Browser | N/A | navigator.sendBeacon on unload |
TIP
On Cloudflare runtimes, the batch transport timer may not survive past the response. waitUntil ensures the flush completes even after the response is sent. Both adapters detect the runtime and call waitUntil automatically — no configuration required.
Log Pipeline
Every log flows through this pipeline before reaching the transport:
logger.ai('llm', {...})
│
▼
┌─ Level Gate ──────────┐
│ level >= minimum? │ → no → drop
└───────┬───────────────┘
▼
┌─ Category Filter ─────┐
│ in allow-list? │ → no → drop
│ not in deny-list? │
└───────┬───────────────┘
▼
┌─ Sampling ────────────┐
│ passes rate check? │ → no → drop
└───────┬───────────────┘
▼
┌─ Enrichment ──────────┐
│ add defaultMeta │
│ add timestamp/session ���
│ run beforeSend() │ → null → drop
└───────┬───────────────┘
▼
┌─ Transport ───────────┐
│ fire-and-forget │
└───────────────────────┘