Vite And Browser
Use the Vite integration when browser-side development events should be captured in the same local store as Node and CLI entries.
Configure The Plugin
// vite.config.ts
import { defineConfig } from 'vite'
import { leylines } from 'leylines/vite'
export default defineConfig({
plugins: [
leylines({
endpoint: '/__scoped_logs',
scope: 'browser',
captureConsole: ['warn', 'error'],
captureErrors: true,
captureRejections: true,
stripProduction: true,
}),
],
})
In serve mode, the plugin:
- opens the inferred local store
- registers a browser log ingestion endpoint
- injects
logger.connect(...)into HTML - optionally captures console calls, uncaught errors, and unhandled rejections
Production build capture is disabled by default. Use production: true only
when production browser capture is intentional.
leylines({
production: true,
captureConsole: ['error'],
})
The default endpoint is /__scoped_logs, and the default browser scope is
browser.
Use stripProduction: true when application logger calls should be removed from
production modules:
leylines({
stripProduction: true,
})
The source rewriter removes standalone browser logger calls after static
leylines/browser imports:
import { logger } from 'leylines/browser'
logger.info('router', 'route loaded')
logger.warn('checkout', 'submit retrying', { attempt: 2 })
Remaining logger references are replaced with a local no-op logger so unusual usage still builds without sending entries.
Vite Logger Capture
Capture Vite's own dev-server warnings and errors when agents need structured diagnostics instead of terminal output:
leylines({
viteLogger: {
scope: 'dev.vite',
levels: ['warn', 'error'],
},
})
Captured entries keep Vite mode, command, logger method, and Rollup/Vite error context such as plugin name, hook, module id, source location, frame, and stack when Vite provides them. Terminal output still goes through Vite's normal logger.
ley --scope-prefix dev.vite --min-level warn --json
For the default dev.vite scope, captureViteLogger is a shorthand:
leylines({
captureViteLogger: ['warn', 'error'],
})
Write Browser Entries
import { logger } from 'leylines/browser'
logger.info('router', 'route loaded', { route: '/settings' })
logger.warn('checkout', 'submit retrying', {
attempt: 2,
})
The exported logger is a side-effect-free singleton. Importing it does not
patch console methods, add event listeners, or send network requests. The Vite
plugin connects it during page load.
Before connection, logger writes are ignored. After the Vite plugin injects
logger.connect(...), browser entries are posted to the configured endpoint.
The first argument is the entry scope, and the second argument is the event
message. Keep scopes stable around the product area or component, such as
router or checkout.payment; put the event action in the message.
Tauri Log Forwarding
Install Tauri's log plugin in apps that should forward native-side records into the Vite plugin's local ingestion endpoint:
pnpm add @tauri-apps/plugin-log
Then attach forwarding from app startup code:
import { attachTauriLogger } from 'leylines/tauri'
const detachTauriLogs = attachTauriLogger({
scope: 'tauri',
metadata: { windowLabel: 'main' },
})
The Vite plugin connects leylines/browser before application modules run, so
Tauri records sent through attachTauriLogger are posted to the same
/__scoped_logs endpoint as browser entries. The default scope is tauri, and
each entry includes properties.source: 'tauri.log'.
Call the returned function when forwarding should stop:
detachTauriLogs()
Manual Connection
For non-Vite browser runtimes, connect the singleton explicitly:
import { logger } from 'leylines/browser'
logger.connect({
endpoint: '/__scoped_logs',
scope: 'browser',
captureConsole: false,
captureErrors: true,
captureRejections: true,
})
Repeated connect calls reconfigure the singleton without stacking duplicate
console or error capture hooks.
Console Capture
captureConsole accepts true or selected levels:
leylines({
captureConsole: ['warn', 'error'],
})
Captured console entries are still written to the original console method, then
sent to Leylines with properties.console: true.
Error Capture
captureErrors records uncaught browser errors. captureRejections records
unhandled promise rejections. Both default to true for the injected browser
logger.
leylines({
captureErrors: true,
captureRejections: true,
})
Inspect Browser Logs
ley --scope router
ley --scope-prefix browser
Use the Vite logger scope separately when you enabled Vite logger capture:
ley --scope-prefix dev.vite --min-level warn --json