nugi-ts-logs-sdk
v1.0.2
Published
Framework-agnostic TypeScript observability SDK (logs, metrics, traces, queries, check-ins, distributed tracing) with batching, retries and buffering. Works with Express, Fastify, NestJS and plain Node.
Maintainers
Readme
nugi-ts-logs-sdk
Framework-agnostic TypeScript observability SDK. One client covering the same
surface as nugi-logs-sdk (Nest) — logs, metrics, traces, errors, DB query
capture, and cron check-ins — with distributed tracing, request
tracking, and a delivery pipeline that does batching, retries and bounded
buffering. Ships adapters for Express, Fastify, and NestJS, and
works with plain Node / JavaScript via CommonJS require().
Designed to never crash or block the host app: every collection and delivery path is fault-tolerant and non-blocking.
Install
npm install nugi-ts-logs-sdkRequires Node 16+ (uses the built-in fetch on Node 18+; on Node 16 provide a
global fetch polyfill). No mandatory runtime dependencies. @nestjs/common
and rxjs are optional peers, only needed if you use the NestJS adapter.
Quick start
import { createClient } from 'nugi-ts-logs-sdk';
export const nugi = createClient({
apiKey: process.env.NUGI_API_KEY!,
appId: process.env.NUGI_APP_ID!,
endpoint: process.env.NUGI_ENDPOINT, // https://logs.example.com
environment: process.env.NUGI_ENVIRONMENT || 'production',
serviceName: process.env.NUGI_SERVICE_NAME || 'api',
release: process.env.NUGI_RELEASE, // optional deploy version
captureConsole: true,
captureQueries: true, // pg / mysql2 / sequelize / typeorm
reportQueryErrors: true,
slowQueryThresholdMs: 500,
captureUncaught: true,
// batching / reliability (defaults shown)
batchSize: 100,
flushIntervalMs: 5000,
maxBufferSize: 10_000,
maxRetries: 3,
sampleRate: 1,
redact: ['ssn', 'creditCard'],
});Logs
nugi.logger.info('order placed', { orderId, amount });
nugi.logger.error('payment failed', { orderId, reason });
const scoped = nugi.logger.child({ component: 'billing' });
scoped.warn('retrying charge');Metrics
nugi.metrics.counter('orders.placed').add(1, { channel: 'web' });
nugi.metrics.gauge('queue.depth').set(42);
nugi.metrics.histogram('db.query.duration', 'ms').record(12.5);
// Nest-SDK-compatible helpers
nugi.metrics.count('orders.placed');
nugi.metrics.distribution('latency', 12.5, { unit: 'ms' });
nugi.metrics.setAttributes({ region: 'eu-west' });
nugi.metrics.withScope({ tenant: 'acme' }, () => {
nugi.metrics.count('checkout.step');
});Traces & distributed tracing
await nugi.tracer.startActiveSpan('charge-card', { kind: 'client' }, async (span) => {
span.setAttribute('gateway', 'stripe');
const headers = {};
span.inject(headers); // W3C traceparent
await fetch(url, { headers });
nugi.logger.info('charging');
});
// Continue an inbound trace (W3C + x-trace-id / B3 / ALB fallbacks)
import { extractContext } from 'nugi-ts-logs-sdk';
const parent = extractContext(req.headers);
nugi.tracer.startActiveSpan('handle', { kind: 'server', parent }, () => { /* ... */ });Error tracking
try { /* ... */ } catch (err) {
nugi.trackError(err, { handled: true, attributes: { orderId } });
}
createClient({ /* ... */, captureUncaught: true });Errors include Sentry-style stack frames with source context when available.
Database query capture
Auto-patches pg, mysql2, sequelize, and typeorm when installed. Prisma needs an explicit wrap:
const prisma = nugi.wrapPrisma(new PrismaClient());Manual:
nugi.logQuery('SELECT 1', 12, { orm: 'pg', success: true });Failed queries can auto-trackError (reportQueryErrors, default on). Slow
queries (≥ slowQueryThresholdMs) emit db.query.slow metrics and land in the
Queries dashboard (dual-written to /api/v1/logs + batch kind: 'query').
Cron check-ins
nugi.checkins.ok('nightly-report');
nugi.checkins.error('nightly-report', 'DB timeout');
nugi.checkins.capture('nightly-report', 'in_progress');Express
import { nugiExpress, nugiExpressErrorHandler } from 'nugi-ts-logs-sdk';
app.use(nugiExpress(nugi)); // early
// ... routes ...
app.use(nugiExpressErrorHandler(nugi)); // lastHTTP instrumentation records access logs, server spans, RED metrics
(http.server.requests / duration / errors / client_errors), real client
IP (X-Forwarded-For), and a requestId.
Express (JavaScript / CommonJS)
const { createClient, nugiExpress, nugiExpressErrorHandler } = require('nugi-ts-logs-sdk');
const nugi = createClient({ /* ... */ });
app.use(nugiExpress(nugi));
app.use(nugiExpressErrorHandler(nugi));Fastify
import { nugiFastify } from 'nugi-ts-logs-sdk';
fastify.register(nugiFastify(nugi));NestJS
import { NugiModule, NUGI_CLIENT } from 'nugi-ts-logs-sdk';
@Module({
imports: [
NugiModule.forRoot({
apiKey: process.env.NUGI_API_KEY!,
appId: process.env.NUGI_APP_ID!,
endpoint: process.env.NUGI_ENDPOINT,
captureQueries: true,
http: { captureRequestBody: false },
}),
],
})
export class AppModule {}Inject the client anywhere:
constructor(@Inject(NUGI_CLIENT) private readonly nugi: NugiClient) {}Delivery guarantees
- Batching — flush at
batchSizeor everyflushIntervalMs. - Buffering — bounded at
maxBufferSize; oldest dropped on overload. - Retries — exponential backoff + jitter on transient failures.
- Graceful shutdown —
await nugi.shutdown()(also wired to process signals whenflushOnExitis on). - Legacy dual-write — DB queries also POST to
/api/v1/logsso the Queries UI stays populated alongside the Nest SDK.
Wire format
Each flush POSTs an EventEnvelope to endpoint + ingestPath
(default /api/v1/telemetry/batch) with an X-API-Key header:
{
"resource": { "appId": "...", "serviceName": "...", "environment": "...",
"release": "...",
"host": { "hostname": "...", "serverIp": "...", "serverIps": { "ipv4": [], "ipv6": [] },
"sdkVersion": "...", "nodeVersion": "...", "os": "...", "pid": 123 } },
"events": [ /* log | metric | span | error | query */ ],
"sentAt": "2026-07-05T20:00:00.000Z"
}API surface
createClient(config) / new NugiClient(config) →
{ logger, metrics, tracer, checkins, trackError, logQuery, wrapPrisma, flush, shutdown, resource, bufferSize }.
Also exported: query capture helpers (startQueryCapture, instrumentPrisma,
normalizeDbError), stack frames (buildStackFrames), W3C + vendor
propagation, sanitize helpers, and framework adapters.
