Monorepo for wisp.place. A static site hosting service built on top of the AT Protocol. wisp.place
README.md

@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#

  1. Create a Grafana Cloud account at https://grafana.com/

  2. Get your Loki credentials:

    • Go to your Grafana Cloud portal
    • Navigate to "Loki" → "Details"
    • Copy the Push endpoint URL and create an API key
  3. Get your Prometheus credentials:

    • Navigate to "Prometheus" → "Details"
    • Copy the Remote Write endpoint and create an API key
  4. 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#

  1. Logs → Buffered → Batched → Sent to Grafana Loki
  2. Metrics → Aggregated → Exported via OTLP → Sent to Prometheus
  3. 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