@lanternade/keelstat-sdk
v0.1.1
Published
Lightweight observability SDK — traces, errors, events, metrics, and logs via OTLP to Axiom
Readme
@lanternade/keelstat-sdk
Lightweight observability SDK — traces, errors, events, metrics, and logs via OTLP to Axiom. Zero dependencies. Works in Node.js, Bun, Cloudflare Workers, Vercel Edge, and browsers.
Install
npm install @lanternade/keelstat-sdkQuick Start
import { keelstat } from '@lanternade/keelstat-sdk';
// Set KEELSTAT_AXIOM_API_KEY in your environment — that's all you need.
const result = await keelstat.trace('checkout.process', async (span) => {
span.setAttribute('cart.items', 3);
return await processCheckout();
});
keelstat.error(new Error('Payment failed'), { userId: 'u123' });
await keelstat.flush(); // Call before serverless function exitsAPI Reference
keelstat.trace(name, fn, options?) — Wrapper Mode
Wraps an async function with a span. Automatically ends the span and captures errors.
const result = await keelstat.trace('db.query', async (span) => {
span.setAttribute('db.statement', 'SELECT ...');
return await db.query('SELECT ...');
});Returns Promise<T> — the return value of fn.
keelstat.trace(name, options?) — Manual Mode
Creates a span that you end yourself. Use when you can't wrap the work in a single function.
const span = keelstat.trace('background.job');
try {
await doWork();
span.setStatus({ code: 'ok' });
} catch (err) {
span.setStatus({ code: 'error', message: err.message });
throw err;
} finally {
span.end();
}Returns KeelstatSpanHandle with setAttribute, setAttributes, addEvent, setStatus, end, traceId, spanId.
Trace Options
| Option | Type | Description |
|--------|------|-------------|
| kind | 'internal' \| 'server' \| 'client' \| 'producer' \| 'consumer' | Span kind (default: 'internal') |
| attributes | Record<string, string \| number \| boolean> | Initial span attributes |
| parent | KeelstatSpanHandle | Explicit parent span for manual nesting |
keelstat.error(error, context?)
Records an error with automatic fingerprinting and deduplication.
keelstat.error(new Error('Connection timeout'), {
userId: 'u123',
level: 'fatal',
attributes: { endpoint: '/api/checkout' },
});Duplicate errors within a 60-second window are suppressed. Override with { force: true }. Custom fingerprinting via { fingerprint: ['payment', 'stripe'] }.
| Context Field | Type | Description |
|---------------|------|-------------|
| userId | string | User ID for attribution |
| level | 'debug' \| 'info' \| 'warn' \| 'error' \| 'fatal' | Severity (default: 'error') |
| attributes | Record<string, string \| number \| boolean> | Additional attributes |
| force | boolean | Bypass deduplication |
| fingerprint | string[] | Custom fingerprint components |
keelstat.event(name, properties?)
Records a structured event.
keelstat.event('button_clicked', {
button: 'add_to_cart',
product_id: 'sku_123',
});keelstat.metric(name, value, options?)
Records a metric data point.
keelstat.metric('queue.depth', 42, {
type: 'gauge',
unit: 'items',
attributes: { queue: 'emails' },
});| Option | Type | Description |
|--------|------|-------------|
| type | 'counter' \| 'gauge' \| 'histogram' | Metric type (default: 'gauge') |
| unit | string | Unit of measurement |
| attributes | Record<string, string \| number \| boolean> | Dimensions |
keelstat.log(level, message, attributes?)
Records a structured log with trace correlation.
keelstat.log('info', 'Order processed', { orderId: 'abc-123' });Logs emitted inside a trace() block are automatically correlated via traceId and spanId.
Lifecycle Methods
keelstat.init(config?)
Explicitly initialize with custom config. Optional — the SDK lazy-initializes from env vars on first method call.
import { keelstat, defineConfig } from '@lanternade/keelstat-sdk';
keelstat.init({
apiKey: 'xaat-...',
dataset: 'my-app',
serviceName: 'checkout-service',
debug: true,
});keelstat.flush()
Flush all buffered signals. Call this before a serverless function exits.
await keelstat.flush();keelstat.shutdown()
Graceful shutdown — flushes, stops timers, and resets state.
await keelstat.shutdown();Identity Methods
keelstat.identify(userId, traits?)
Associate a user with all subsequent signals.
keelstat.identify('user_123', { plan: 'pro', company: 'Acme' });keelstat.resetUser()
Clear the current user identity.
keelstat.resetUser();Configuration
All options can be set via defineConfig(), keelstat.init(), or KEELSTAT_* environment variables. Resolution order: explicit config > env vars > auto-detect > defaults.
| Field | Type | Default | Env Var | Description |
|-------|------|---------|---------|-------------|
| apiKey | string | '' | KEELSTAT_AXIOM_API_KEY | Axiom API key (required) |
| endpoint | string | 'https://api.axiom.co' | KEELSTAT_ENDPOINT | OTLP endpoint |
| dataset | string | 'keelstat' | KEELSTAT_DATASET | Axiom dataset name |
| serviceName | string | 'unknown-service' | KEELSTAT_SERVICE_NAME | Service name |
| serviceVersion | string | '0.0.0' | KEELSTAT_SERVICE_VERSION | Service version (auto-detected from VERCEL_GIT_COMMIT_SHA, GITHUB_SHA) |
| environment | string | 'development' | KEELSTAT_ENVIRONMENT | Deployment environment (auto-detected from VERCEL_ENV, NODE_ENV) |
| sampleRate | number | 1.0 | KEELSTAT_SAMPLE_RATE | Trace sample rate (0.0–1.0) |
| enabled | boolean | true | KEELSTAT_ENABLED | Enable/disable SDK |
| debug | boolean | false | KEELSTAT_DEBUG | Enable debug logging to console |
| batchSize | number | 100 | KEELSTAT_BATCH_SIZE | Batch size before auto-flush |
| flushIntervalMs | number | 5000 | KEELSTAT_FLUSH_INTERVAL_MS | Flush interval in ms |
| maxRetries | number | 3 | — | Max retry attempts for failed exports |
| retryBaseDelayMs | number | 1000 | — | Base retry delay in ms |
| mode | 'direct' | 'direct' | KEELSTAT_MODE | Export mode |
| release | string | '' | KEELSTAT_RELEASE | Release identifier (auto-detected from git SHA) |
| sessionCookieName | string | 'keelstat_session' | KEELSTAT_SESSION_COOKIE_NAME | Session cookie name |
| sessionLocalStorageKey | string | 'keelstat_device' | KEELSTAT_SESSION_LS_KEY | Device ID localStorage key |
| sessionTimeoutMs | number | 1800000 | KEELSTAT_SESSION_TIMEOUT_MS | Session inactivity timeout (30 min) |
| sessionSecureCookie | boolean | auto | KEELSTAT_SESSION_SECURE_COOKIE | Secure flag on session cookie |
Framework Guides
Next.js
// lib/keelstat.ts
import { keelstat } from '@lanternade/keelstat-sdk';
export { keelstat };
// In a Server Action or Route Handler:
export async function POST(req: Request) {
return keelstat.trace('api.checkout', async (span) => {
const body = await req.json();
span.setAttribute('cart.items', body.items.length);
const result = await processCheckout(body);
await keelstat.flush();
return Response.json(result);
});
}Express
import express from 'express';
import { keelstat } from '@lanternade/keelstat-sdk';
const app = express();
app.post('/checkout', async (req, res) => {
await keelstat.trace('api.checkout', async (span) => {
span.setAttribute('cart.items', req.body.items.length);
const result = await processCheckout(req.body);
res.json(result);
});
});
process.on('SIGTERM', async () => {
await keelstat.shutdown();
process.exit(0);
});Cloudflare Workers
import { keelstat } from '@lanternade/keelstat-sdk';
export default {
async fetch(request: Request, env: Env): Promise<Response> {
keelstat.init({ apiKey: env.KEELSTAT_AXIOM_API_KEY });
return keelstat.trace('worker.request', async (span) => {
span.setAttribute('http.url', request.url);
const response = await handleRequest(request);
await keelstat.flush();
return response;
});
},
};Browser
import { keelstat } from '@lanternade/keelstat-sdk';
keelstat.init({
apiKey: 'xaat-...', // Use a client-safe ingest token
serviceName: 'my-spa',
});
document.querySelector('#checkout-btn')?.addEventListener('click', () => {
keelstat.event('button_clicked', { button: 'checkout' });
});
// Flush on page unload
window.addEventListener('beforeunload', () => {
keelstat.flush();
});TypeScript
The SDK is fully typed. All public types are exported:
import type {
KeelstatSDK,
KeelstatConfig,
KeelstatConfigInput,
KeelstatSpanHandle,
TraceOptions,
ErrorContext,
MetricOptions,
LogLevel,
Attributes,
} from '@lanternade/keelstat-sdk';Safety Guarantees
- Never throws — all methods gracefully degrade to no-ops on failure
- Silent when disabled —
enabled: falseor missing API key produces zero network calls - No ambient pollution — does not modify global types or objects
- Zero dependencies — the SDK is fully self-contained
- Tree-shakeable — marked
sideEffects: false
API Stability
Every export is annotated with a stability tier:
| Tier | Meaning |
|------|---------|
| @public | Stable. Breaking changes require a major version bump (post-1.0). |
| @beta | API shape may evolve in minor versions. Documented in changelog. |
The 5 core methods (trace, error, event, metric, log), lifecycle methods (init, flush, shutdown), defineConfig, and all associated types are @public.
Session management (SessionManager, identify, resetUser, SessionConfig, SessionContext) is @beta — the interface is functional but may change based on feedback.
Internal types (BufferedSpan, Exporter, BatchProcessor, etc.) are not exported and carry no stability guarantees.
See API_REPORT.md for the full export inventory.
