@wisp/observability#
Framework-agnostic observability package with Grafana integration for logs and metrics persistence.
Features#
- In-memory storage for local development
- Grafana Loki integration for log persistence
- Prometheus/OTLP integration for metrics
- Framework middleware for Elysia and Hono
- Automatic batching and buffering for efficient data transmission
Installation#
bun add @wisp/observability
Basic Usage#
Without Grafana (In-Memory Only)#
import { createLogger, metricsCollector } from '@wisp/observability'
const logger = createLogger('my-service')
// Log messages
logger.info('Server started')
logger.error('Failed to connect', new Error('Connection refused'))
// Record metrics
metricsCollector.recordRequest('/api/users', 'GET', 200, 45, 'my-service')
With Grafana Integration#
import { initializeGrafanaExporters, createLogger } from '@wisp/observability'
// Initialize at application startup
initializeGrafanaExporters({
lokiUrl: 'https://logs-prod.grafana.net',
lokiAuth: {
bearerToken: 'your-loki-api-key'
},
prometheusUrl: 'https://prometheus-prod.grafana.net',
prometheusAuth: {
bearerToken: 'your-prometheus-api-key'
},
serviceName: 'wisp-app',
serviceVersion: '1.0.0',
batchSize: 100,
flushIntervalMs: 5000
})
// Now all logs and metrics will be sent to Grafana automatically
const logger = createLogger('my-service')
logger.info('This will be sent to Grafana Loki')
Configuration#
Environment Variables#
You can configure Grafana integration using environment variables:
# Loki configuration
GRAFANA_LOKI_URL=https://logs-prod.grafana.net
# Authentication Option 1: Bearer Token (Grafana Cloud)
GRAFANA_LOKI_TOKEN=your-loki-api-key
# Authentication Option 2: Username/Password (Self-hosted or some Grafana setups)
GRAFANA_LOKI_USERNAME=your-username
GRAFANA_LOKI_PASSWORD=your-password
# Prometheus configuration
GRAFANA_PROMETHEUS_URL=https://prometheus-prod.grafana.net/api/prom
# Authentication Option 1: Bearer Token (Grafana Cloud)
GRAFANA_PROMETHEUS_TOKEN=your-prometheus-api-key
# Authentication Option 2: Username/Password (Self-hosted or some Grafana setups)
GRAFANA_PROMETHEUS_USERNAME=your-username
GRAFANA_PROMETHEUS_PASSWORD=your-password
Programmatic Configuration#
import { initializeGrafanaExporters } from '@wisp/observability'
initializeGrafanaExporters({
// Loki configuration for logs
lokiUrl: 'https://logs-prod.grafana.net',
lokiAuth: {
// Option 1: Bearer token (recommended for Grafana Cloud)
bearerToken: 'your-api-key',
// Option 2: Basic auth
username: 'your-username',
password: 'your-password'
},
// Prometheus/OTLP configuration for metrics
prometheusUrl: 'https://prometheus-prod.grafana.net',
prometheusAuth: {
bearerToken: 'your-api-key'
},
// Service metadata
serviceName: 'wisp-app',
serviceVersion: '1.0.0',
// Batching configuration
batchSize: 100, // Flush after this many entries
flushIntervalMs: 5000, // Flush every 5 seconds
// Enable/disable exporters
enabled: true
})
Middleware Integration#
Elysia#
import { Elysia } from 'elysia'
import { observabilityMiddleware } from '@wisp/observability/middleware/elysia'
import { initializeGrafanaExporters } from '@wisp/observability'
// Initialize Grafana exporters
initializeGrafanaExporters({
lokiUrl: process.env.GRAFANA_LOKI_URL,
lokiAuth: { bearerToken: process.env.GRAFANA_LOKI_TOKEN }
})
const app = new Elysia()
.use(observabilityMiddleware({ service: 'main-app' }))
.get('/', () => 'Hello World')
.listen(3000)
Hono#
import { Hono } from 'hono'
import { observabilityMiddleware, observabilityErrorHandler } from '@wisp/observability/middleware/hono'
import { initializeGrafanaExporters } from '@wisp/observability'
// Initialize Grafana exporters
initializeGrafanaExporters({
lokiUrl: process.env.GRAFANA_LOKI_URL,
lokiAuth: { bearerToken: process.env.GRAFANA_LOKI_TOKEN }
})
const app = new Hono()
app.use('*', observabilityMiddleware({ service: 'hosting-service' }))
app.onError(observabilityErrorHandler({ service: 'hosting-service' }))
Grafana Cloud Setup#
-
Create a Grafana Cloud account at https://grafana.com/
-
Get your Loki credentials:
- Go to your Grafana Cloud portal
- Navigate to "Loki" → "Details"
- Copy the Push endpoint URL and create an API key
-
Get your Prometheus credentials:
- Navigate to "Prometheus" → "Details"
- Copy the Remote Write endpoint and create an API key
-
Configure your application:
initializeGrafanaExporters({ lokiUrl: 'https://logs-prod-xxx.grafana.net', lokiAuth: { bearerToken: 'glc_xxx' }, prometheusUrl: 'https://prometheus-prod-xxx.grafana.net/api/prom', prometheusAuth: { bearerToken: 'glc_xxx' } })
Data Flow#
- Logs → Buffered → Batched → Sent to Grafana Loki
- Metrics → Aggregated → Exported via OTLP → Sent to Prometheus
- Errors → Deduplicated → Sent to Loki with error tag
Performance Considerations#
- Logs and metrics are batched to reduce network overhead
- Default batch size: 100 entries
- Default flush interval: 5 seconds
- Failed exports are logged but don't block application
- In-memory buffers are capped to prevent memory leaks
Graceful Shutdown#
The exporters automatically register shutdown handlers:
import { shutdownGrafanaExporters } from '@wisp/observability'
// Manual shutdown if needed
process.on('beforeExit', async () => {
await shutdownGrafanaExporters()
})
License#
Private