Skip to content

telemetryFive kinds, one envelope

Product events, errors, spans, state transitions and billable usage are one Mongo collection with a discriminated `kind` โ€” driven by a registry your app owns, not a vocabulary this package ships.

js
import express from 'express';
import mongoose from 'mongoose';
import { z } from 'zod';
import {
  createDashboard, createIngest, createTelemetry, defineRegistry,
} from '@jeffjassky/telemetry';

// This package ships no event names. This block is the contract.
const REGISTRY = defineRegistry({
  'account.signed_up': {
    kind: 'event', origin: 'server', subjects: ['account'],
    attrs: z.object({ source: z.string().max(64) }),
    indexedAttrs: ['source'],
    rollups: [{ by: ['subject'], subjects: ['account'], capture: ['attr:source'] }],
    description: 'Account created',
  },
  'llm.completion': {
    kind: 'span', origin: 'server', subjects: ['org'],
    attrs: z.object({ gen_ai_request_model: z.string(), feature: z.string().max(64) }),
    metrics: z.object({ tokens_in: z.number().int(), tokens_out: z.number().int(), cost_usd: z.number() }),
    rollups: [{
      as: 'llm_cost', by: ['attr:gen_ai_request_model'],
      bucket: 'day', sum: ['cost_usd'], retentionDays: null,
    }],
    description: 'Single model call',
  },
});

await mongoose.connect(process.env.MONGO_URL);

const t = createTelemetry({ registry: REGISTRY, connection: mongoose });
await t.syncIndexes();   // boot โ€” await this before the first write

await t.emit('account.signed_up', {
  tenantId: 'acc_9',
  subjects: [{ type: 'account', id: 'acct_1' }],
  actor: 'user:u_1',
  attrs: { source: 'ads' },
});

const app = express();
app.use('/telemetry/ingest', createIngest({ telemetry: t }));
app.use('/telemetry', createDashboard({
  telemetry: t,
  viewerAdapter: { resolveViewer: (req) => req.user && { tenantId: req.user.orgId, role: req.user.role } },
  mountPath: '/telemetry',
}));

Released under the MIT License.