@manifest-cyber/observability-ts
v0.2.8
Published
Unified observability library for Manifest Cyber services - Metrics (Prometheus) and Tracing (OpenTelemetry)
Readme
@manifest-cyber/observability-ts
Unified observability library for Manifest Cyber's TypeScript services - Prometheus metrics and OpenTelemetry tracing.
Installation
npm install @manifest-cyber/observability-ts
# Optional: for tracing features
npm install @opentelemetry/sdk-node @opentelemetry/exporter-trace-otlp-grpcQuick Start
Metrics
import { createCounter, startMetricsServer } from '@manifest-cyber/observability-ts';
const requestsTotal = createCounter({
name: 'http_requests_total',
help: 'Total HTTP requests',
labelNames: ['method', 'status'],
});
await startMetricsServer({ serviceName: 'my-service' });
requestsTotal.inc({ method: 'GET', status: '200' });Tracing
import { initTracing, withSpan } from '@manifest-cyber/observability-ts';
await initTracing({ serviceName: 'my-service' });
await withSpan('process.request', async (span) => {
span.setAttribute('user.id', userId);
return await processRequest();
});Tree-Shakeable Imports
import { createCounter } from '@manifest-cyber/observability-ts/metrics';
import { initTracing } from '@manifest-cyber/observability-ts/tracing';Features
Metrics (Prometheus)
- Counter, Gauge, Histogram, Summary
- HTTP metrics server (
/metricson port 9090) - Timer utilities and operation tracking
- Type-safe with TypeScript generics
Tracing (OpenTelemetry)
- W3C Trace Context propagation
- OTLP export (VictoriaTraces, Jaeger, Tempo)
- Automatic HTTP/gRPC/database instrumentation
- Manual span creation with
createSpan()andwithSpan() - SQS and RabbitMQ trace propagation
- Logger integration for trace correlation
API Overview
Metrics
import { createCounter, createHistogram, createGauge } from '@manifest-cyber/observability-ts/metrics';
// Counter
const counter = createCounter({
name: 'operation_total',
help: 'Total operations',
labelNames: ['type', 'status'],
});
counter.inc({ type: 'api', status: 'success' });
// Histogram
const histogram = createHistogram({
name: 'request_duration_seconds',
help: 'Request duration',
labelNames: ['route', 'method'],
});
histogram.observe({ route: '/api/users', method: 'GET' }, 0.42);
// Gauge
const gauge = createGauge({
name: 'active_connections',
help: 'Active connections',
});
gauge.set(42);Tracing
import { initTracing, withSpan, createSpan } from '@manifest-cyber/observability-ts/tracing';
// Initialize
await initTracing({
serviceName: 'my-service',
exporter: {
type: 'otlp-grpc',
endpoint: 'http://localhost:4317',
},
sampling: {
type: 'parentBased',
parentBased: {
root: { type: 'traceIdRatio', ratio: 0.1 }, // 10% sampling
},
},
});
// Automatic span lifecycle
await withSpan('database.query', async (span) => {
span.setAttribute('db.statement', 'SELECT * FROM users');
return await db.query('SELECT * FROM users');
});
// Manual span management
const span = createSpan('manual.operation');
try {
await doWork();
span.setStatus({ code: SpanStatusCode.OK });
} finally {
span.end();
}Trace Propagation
import {
injectTraceContext,
extractTraceContext,
createMessageTraceContext,
extractMessageTraceContext
} from '@manifest-cyber/observability-ts/tracing';
// HTTP Client
const headers = {};
injectTraceContext(headers);
await axios.get('https://api.example.com/users', { headers });
// HTTP Server
app.use((req, res, next) => {
extractTraceContext(req.headers);
next();
});
// SQS Producer
await sqs.sendMessage({
QueueUrl: queueUrl,
MessageBody: JSON.stringify(data),
MessageAttributes: createMessageTraceContext(),
});
// SQS Consumer
extractMessageTraceContext(message.MessageAttributes);Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| SERVICE_NAME | Service name | 'unknown-service' |
| MFST_METRICS_PORT | Metrics server port | 9090 |
| OTEL_EXPORTER_OTLP_ENDPOINT | OTLP endpoint | 'http://localhost:4317' |
| OTEL_TRACING_ENABLED | Enable/disable tracing | true |
| ENV | Environment (dev/staging/prod) | 'development' |
Example: Express API
import express from 'express';
import {
createCounter,
createHistogram,
startMetricsServer,
initTracing,
withSpan,
extractTraceContext,
} from '@manifest-cyber/observability-ts';
await initTracing({ serviceName: 'api-service' });
await startMetricsServer({ serviceName: 'api-service' });
const httpRequests = createCounter({
name: 'http_requests_total',
help: 'Total HTTP requests',
labelNames: ['method', 'route', 'status'],
});
const httpDuration = createHistogram({
name: 'http_request_duration_seconds',
help: 'HTTP request duration',
labelNames: ['method', 'route', 'status'],
});
const app = express();
app.use((req, res, next) => {
extractTraceContext(req.headers);
const start = Date.now();
res.on('finish', () => {
const duration = (Date.now() - start) / 1000;
const labels = {
method: req.method,
route: req.route?.path || req.path,
status: res.statusCode.toString(),
};
httpRequests.inc(labels);
httpDuration.observe(labels, duration);
});
next();
});
app.get('/api/users/:id', async (req, res) => {
await withSpan('http.GET /api/users/:id', async (span) => {
span.setAttribute('user.id', req.params.id);
const user = await fetchUser(req.params.id);
res.json(user);
});
});
app.listen(3000);Migration from @manifest-cyber/metrics
npm uninstall @manifest-cyber/metrics
npm install @manifest-cyber/observability-tsUpdate imports - all existing code is backward compatible:
// Before
import { createCounter } from '@manifest-cyber/metrics';
// After
import { createCounter } from '@manifest-cyber/observability-ts';Links
- NPM: https://www.npmjs.com/package/@manifest-cyber/observability-ts
- GitHub: https://github.com/manifest-cyber/observability-ts
- Issues: https://github.com/manifest-cyber/observability-ts/issues
License
MIT © Manifest Cyber
