node-observe 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/histogram.ts","../src/memory.ts","../src/slack.ts","../src/util.ts","../src/express.ts","../src/mongo.ts","../src/redis.ts","../src/insights.ts"],"names":["finish"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { HistogramSnapshot } from \"./types\";\n\n/**\n * A bounded latency histogram. Keeps the most recent `cap` samples in a ring\n * buffer (so memory is constant) while tracking lifetime count/min/max/sum and\n * an error tally. Percentiles are computed over the retained window.\n */\nexport class Histogram {\n private readonly buffer: number[] = [];\n private head = 0;\n private readonly cap: number;\n\n count = 0;\n errors = 0;\n private sum = 0;\n private minV = Infinity;\n private maxV = -Infinity;\n\n constructor(cap = 1024) {\n this.cap = Math.max(1, cap);\n }\n\n /** Record one duration (ms). Mark `isError` to count it toward the error tally. */\n record(value: number, isError = false): void {\n if (!Number.isFinite(value) || value < 0) return;\n this.count++;\n this.sum += value;\n if (isError) this.errors++;\n if (value < this.minV) this.minV = value;\n if (value > this.maxV) this.maxV = value;\n\n if (this.buffer.length < this.cap) {\n this.buffer.push(value);\n } else {\n this.buffer[this.head] = value;\n this.head = (this.head + 1) % this.cap;\n }\n }\n\n /** Linear-interpolation percentile over the retained window. `p` in [0,100]. */\n percentile(p: number): number {\n const n = this.buffer.length;\n if (n === 0) return 0;\n const sorted = [...this.buffer].sort((a, b) => a - b);\n if (n === 1) return sorted[0] as number;\n const rank = (p / 100) * (n - 1);\n const lo = Math.floor(rank);\n const hi = Math.ceil(rank);\n const frac = rank - lo;\n return (sorted[lo] as number) * (1 - frac) + (sorted[hi] as number) * frac;\n }\n\n snapshot(): HistogramSnapshot {\n const round = (n: number) => Math.round(n * 100) / 100;\n return {\n count: this.count,\n errors: this.errors,\n mean: this.count ? round(this.sum / this.count) : 0,\n min: this.count ? round(this.minV) : 0,\n max: this.count ? round(this.maxV) : 0,\n p50: round(this.percentile(50)),\n p95: round(this.percentile(95)),\n p99: round(this.percentile(99)),\n };\n }\n}\n\n/** A named set of histograms plus simple counters. */\nexport class MetricsRegistry {\n private readonly histograms = new Map<string, Histogram>();\n private readonly counters = new Map<string, number>();\n\n constructor(private readonly cap = 1024) {}\n\n record(name: string, value: number, isError = false): void {\n let h = this.histograms.get(name);\n if (!h) {\n h = new Histogram(this.cap);\n this.histograms.set(name, h);\n }\n h.record(value, isError);\n }\n\n increment(name: string, by = 1): void {\n this.counters.set(name, (this.counters.get(name) ?? 0) + by);\n }\n\n snapshot(): Record<string, HistogramSnapshot> {\n const out: Record<string, HistogramSnapshot> = {};\n for (const [name, h] of this.histograms) out[name] = h.snapshot();\n return out;\n }\n\n counterSnapshot(): Record<string, number> {\n return Object.fromEntries(this.counters);\n }\n\n reset(): void {\n this.histograms.clear();\n this.counters.clear();\n }\n}\n","import type { MemorySnapshot } from \"./types\";\n\ninterface Sample {\n t: number; // ms timestamp\n heapUsed: number; // bytes\n}\n\n/**\n * Tracks heap-used over time and estimates the growth trend via least-squares\n * linear regression. A sustained positive slope is the signal of a memory leak —\n * far more reliable than reacting to a single high reading (which is usually\n * just GC timing).\n */\nexport class MemoryMonitor {\n private readonly samples: Sample[] = [];\n private readonly window: number;\n\n constructor(window = 30) {\n this.window = Math.max(2, window);\n }\n\n /** Add the current memory reading. `now`/`mem` are injectable for testing. */\n sample(now: number, mem: NodeJS.MemoryUsage): void {\n this.samples.push({ t: now, heapUsed: mem.heapUsed });\n if (this.samples.length > this.window) this.samples.shift();\n }\n\n /** Heap growth rate in MB per minute over the retained window. */\n growthMbPerMin(): number {\n if (this.samples.length < 2) return 0;\n const n = this.samples.length;\n const t0 = this.samples[0]!.t;\n let sx = 0;\n let sy = 0;\n let sxx = 0;\n let sxy = 0;\n for (const s of this.samples) {\n const x = (s.t - t0) / 60000; // minutes since window start\n const y = s.heapUsed / (1024 * 1024); // MB\n sx += x;\n sy += y;\n sxx += x * x;\n sxy += x * y;\n }\n const denom = n * sxx - sx * sx;\n if (denom === 0) return 0;\n const slope = (n * sxy - sx * sy) / denom; // MB per minute\n return Math.round(slope * 100) / 100;\n }\n\n snapshot(mem: NodeJS.MemoryUsage): MemorySnapshot {\n const mb = (b: number) => Math.round((b / (1024 * 1024)) * 100) / 100;\n return {\n heapUsedMb: mb(mem.heapUsed),\n heapTotalMb: mb(mem.heapTotal),\n rssMb: mb(mem.rss),\n externalMb: mb(mem.external),\n growthMbPerMin: this.growthMbPerMin(),\n };\n }\n\n /** Number of samples currently retained. */\n get size(): number {\n return this.samples.length;\n }\n}\n","import type { Alert, Logger } from \"./types\";\n\nconst EMOJI: Record<string, string> = {\n \"slow-request\": \"🐢\",\n \"slow-mongo\": \"🍃\",\n \"slow-redis\": \"🧱\",\n \"memory-leak\": \"📈\",\n \"memory-high\": \"🚨\",\n};\n\n/** Format an alert as Slack message text. */\nexport function formatSlackMessage(alert: Alert, service: string): string {\n const emoji = EMOJI[alert.type] ?? \"⚠️\";\n return (\n `${emoji} *[${service}] ${alert.type}*\\n` +\n `${alert.message}\\n` +\n `• value: \\`${alert.value}\\` • threshold: \\`${alert.threshold}\\` • at: ${alert.at}`\n );\n}\n\n/**\n * Post an alert to a Slack incoming webhook. Uses the global `fetch` (Node 18+);\n * failures are logged, never thrown — observability must not crash the app.\n */\nexport async function postToSlack(\n webhook: string,\n alert: Alert,\n service: string,\n logger?: Logger,\n): Promise<void> {\n if (typeof fetch !== \"function\") {\n logger?.warn?.(\"node-observe: global fetch unavailable (need Node 18+); skipping Slack alert\");\n return;\n }\n try {\n const res = await fetch(webhook, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ text: formatSlackMessage(alert, service) }),\n });\n if (!res.ok) {\n logger?.error?.(`node-observe: Slack webhook returned ${res.status}`);\n }\n } catch (err) {\n logger?.error?.(`node-observe: Slack alert failed: ${(err as Error).message}`);\n }\n}\n","/**\n * Collapse high-cardinality path segments so routes group together in metrics.\n * Numeric ids, Mongo ObjectIds, UUIDs, and long hex tokens all become `:id`.\n *\n * `/users/64b8.../posts/12` → `/users/:id/posts/:id`\n */\nexport function normalizePath(path: string): string {\n const clean = path.split(\"?\")[0] ?? path;\n return clean\n .split(\"/\")\n .map((seg) => {\n if (seg === \"\") return seg;\n if (/^\\d+$/.test(seg)) return \":id\";\n if (/^[0-9a-f]{24}$/i.test(seg)) return \":id\"; // ObjectId\n if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(seg)) return \":id\"; // UUID\n if (/^[0-9a-f]{32,}$/i.test(seg)) return \":id\"; // long hex token\n return seg;\n })\n .join(\"/\");\n}\n\n/** Elapsed milliseconds since an `process.hrtime.bigint()` start, as a float. */\nexport function elapsedMs(start: bigint): number {\n return Number(process.hrtime.bigint() - start) / 1e6;\n}\n","import type { Insights } from \"./insights\";\nimport { elapsedMs } from \"./util\";\n\n/** Minimal shapes so we don't need `express` as a dependency. */\nexport interface ObserveRequest {\n method?: string;\n route?: { path?: string };\n baseUrl?: string;\n originalUrl?: string;\n path?: string;\n url?: string;\n}\nexport interface ObserveResponse {\n statusCode?: number;\n on(event: \"finish\" | \"close\", listener: () => void): void;\n}\nexport type ObserveNext = () => void;\n\n/**\n * Express/Connect middleware that records request latency on response finish.\n * Prefers the matched route pattern (`req.route.path`) to keep cardinality low,\n * falling back to the raw path.\n */\nexport function instrumentExpress(insights: Insights) {\n return function nodeObserveMiddleware(req: ObserveRequest, res: ObserveResponse, next: ObserveNext): void {\n const start = process.hrtime.bigint();\n let done = false;\n const finish = () => {\n if (done) return;\n done = true;\n const ms = elapsedMs(start);\n const method = req.method ?? \"GET\";\n const route = routeOf(req);\n insights.recordRequest(method, route, res.statusCode ?? 0, ms);\n };\n res.on(\"finish\", finish);\n res.on(\"close\", finish);\n next();\n };\n}\n\nfunction routeOf(req: ObserveRequest): string {\n // `req.route.path` is the matched pattern (e.g. \"/users/:id\") — best for grouping.\n if (req.route?.path) {\n const base = req.baseUrl ?? \"\";\n return base + req.route.path || req.route.path;\n }\n return req.path ?? (req.originalUrl ?? req.url ?? \"unknown\").split(\"?\")[0] ?? \"unknown\";\n}\n","import type { Insights } from \"./insights\";\nimport { elapsedMs } from \"./util\";\n\nconst START = Symbol(\"nodeObserveStart\");\n\n/** Minimal Mongoose shapes (avoids a hard dependency on `mongoose`). */\ninterface SchemaLike {\n pre(hook: string | RegExp | string[], fn: (this: any) => void): void;\n post(hook: string | RegExp | string[], fn: (this: any, ...args: any[]) => void): void;\n}\ninterface MongooseLike {\n plugin(fn: (schema: SchemaLike) => void): unknown;\n}\n\nconst QUERY_HOOK = /^(find|findOne|findOneAnd|count|countDocuments|estimatedDocumentCount|update|updateOne|updateMany|delete|deleteOne|deleteMany|replaceOne)/;\n\n/**\n * Register a global Mongoose plugin that times every query, aggregate, and\n * document save, recording `<Model>.<op>` durations on the given Insights.\n *\n * Call this **before** your models are compiled (i.e. right after you import\n * mongoose) so the plugin applies to every schema.\n *\n * @example\n * ```ts\n * import mongoose from \"mongoose\";\n * insights.instrumentMongoose(mongoose);\n * ```\n */\nexport function instrumentMongoose(mongoose: unknown, insights: Insights): void {\n const m = mongoose as MongooseLike;\n if (!m || typeof m.plugin !== \"function\") {\n insights.logger.warn?.(\"node-observe: instrumentMongoose called with a non-mongoose value; skipping\");\n return;\n }\n\n m.plugin((schema: SchemaLike) => {\n // --- query middleware (find/update/delete/count/...) ---\n schema.pre(QUERY_HOOK, function (this: any) {\n this[START] = process.hrtime.bigint();\n });\n schema.post(QUERY_HOOK, function (this: any) {\n finish(insights, modelName(this), this.op ?? \"query\", this[START]);\n });\n\n // --- aggregate middleware ---\n schema.pre(\"aggregate\", function (this: any) {\n this[START] = process.hrtime.bigint();\n });\n schema.post(\"aggregate\", function (this: any) {\n finish(insights, aggregateModelName(this), \"aggregate\", this[START]);\n });\n\n // --- document save middleware ---\n schema.pre(\"save\", function (this: any) {\n this[START] = process.hrtime.bigint();\n });\n schema.post(\"save\", function (this: any) {\n finish(insights, this?.constructor?.modelName ?? \"Document\", \"save\", this[START]);\n });\n });\n}\n\nfunction finish(insights: Insights, model: string, op: string, start: bigint | undefined): void {\n if (typeof start !== \"bigint\") return;\n insights.recordMongo(`${model}.${op}`, elapsedMs(start));\n}\n\nfunction modelName(query: any): string {\n return query?.model?.modelName ?? query?.mongooseCollection?.name ?? \"Model\";\n}\n\nfunction aggregateModelName(agg: any): string {\n return agg?._model?.modelName ?? agg?.model?.()?.modelName ?? \"Model\";\n}\n","import type { Insights } from \"./insights\";\nimport { elapsedMs } from \"./util\";\n\nconst PATCHED = Symbol.for(\"nodeObserve.redisPatched\");\n\ninterface CommandLike {\n name?: string;\n promise?: Promise<unknown>;\n}\ninterface RedisLike {\n sendCommand?: (command: CommandLike, ...rest: unknown[]) => unknown;\n [PATCHED]?: boolean;\n}\n\n/**\n * Instrument an ioredis client by wrapping `sendCommand`, timing every command\n * (`get`, `set`, `hgetall`, …) via the command's own promise. Safe to call once\n * per client; re-instrumenting the same client is a no-op.\n *\n * @example\n * ```ts\n * import Redis from \"ioredis\";\n * const redis = new Redis(url);\n * insights.instrumentRedis(redis);\n * ```\n */\nexport function instrumentRedis(client: unknown, insights: Insights): void {\n const c = client as RedisLike;\n if (!c || typeof c.sendCommand !== \"function\") {\n insights.logger.warn?.(\"node-observe: instrumentRedis called with a non-ioredis value; skipping\");\n return;\n }\n if (c[PATCHED]) return;\n c[PATCHED] = true;\n\n const original = c.sendCommand.bind(c);\n c.sendCommand = function patchedSendCommand(command: CommandLike, ...rest: unknown[]) {\n const start = process.hrtime.bigint();\n const name = (command?.name ?? \"unknown\").toLowerCase();\n const result = original(command, ...rest);\n const promise = command?.promise ?? (result as Promise<unknown> | undefined);\n if (promise && typeof (promise as Promise<unknown>).then === \"function\") {\n (promise as Promise<unknown>).then(\n () => insights.recordRedis(name, elapsedMs(start), false),\n () => insights.recordRedis(name, elapsedMs(start), true),\n );\n }\n return result;\n };\n}\n","import { MetricsRegistry } from \"./histogram\";\nimport { MemoryMonitor } from \"./memory\";\nimport { postToSlack } from \"./slack\";\nimport { normalizePath } from \"./util\";\nimport { instrumentExpress } from \"./express\";\nimport { instrumentMongoose } from \"./mongo\";\nimport { instrumentRedis } from \"./redis\";\nimport type { Alert, AlertType, InsightsOptions, InsightsSnapshot, Logger } from \"./types\";\n\nconst consoleLogger: Logger = {\n info: (m) => console.log(m),\n error: (m) => console.error(m),\n warn: (m) => console.warn(m),\n debug: (m) => console.debug(m),\n};\n\nconst DEFAULT_THRESHOLDS = {\n requestMs: 1000,\n mongoMs: 300,\n redisMs: 100,\n heapGrowthMbPerMin: 50,\n heapUsedMb: 0, // disabled by default\n};\n\n/**\n * The central observability handle. Create one per process, attach the\n * instrumentations you want, and read {@link Insights.snapshot} (or mount\n * {@link Insights.handler}) to see latency percentiles, query timings, memory\n * trend, and counters. Threshold breaches raise {@link Alert}s, optionally\n * pushed to Slack.\n *\n * @example\n * ```ts\n * const insights = new Insights({ serviceName: \"api\", slackWebhook: process.env.SLACK_URL });\n * app.use(insights.express());\n * insights.instrumentMongoose(mongoose);\n * insights.instrumentRedis(redis);\n * insights.startMemoryMonitor();\n * app.get(\"/insights\", insights.handler());\n * ```\n */\nexport class Insights {\n readonly http: MetricsRegistry;\n readonly mongo: MetricsRegistry;\n readonly redis: MetricsRegistry;\n private readonly counters: MetricsRegistry;\n private readonly memory: MemoryMonitor;\n\n readonly serviceName: string;\n readonly thresholds: Required<NonNullable<InsightsOptions[\"thresholds\"]>>;\n readonly logger: Logger;\n private readonly opts: InsightsOptions;\n private readonly cooldownMs: number;\n private readonly lastAlertAt = new Map<string, number>();\n private memoryTimer: ReturnType<typeof setInterval> | undefined;\n private readonly startedAt = Date.now();\n\n constructor(options: InsightsOptions = {}) {\n this.opts = options;\n this.serviceName = options.serviceName ?? \"node-observe\";\n this.thresholds = { ...DEFAULT_THRESHOLDS, ...(options.thresholds ?? {}) };\n this.logger = options.logger ?? consoleLogger;\n this.cooldownMs = options.alertCooldownMs ?? 60_000;\n this.memory = new MemoryMonitor();\n const cap = options.sampleSize ?? 1024;\n this.http = new MetricsRegistry(cap);\n this.mongo = new MetricsRegistry(cap);\n this.redis = new MetricsRegistry(cap);\n this.counters = new MetricsRegistry(cap);\n }\n\n // ---- recording (used by the instrumentations) -----------------------------\n\n /** Record an HTTP request. `route` is normalized when `normalizePaths` is on. */\n recordRequest(method: string, route: string, statusCode: number, ms: number): void {\n const path = this.opts.normalizePaths === false ? route : normalizePath(route);\n const isError = statusCode >= 500;\n const key = `${method.toUpperCase()} ${path}`;\n this.http.record(key, ms, isError);\n this.counters.increment(`http.status.${Math.floor(statusCode / 100)}xx`);\n if (ms > this.thresholds.requestMs) {\n this.raise(\"slow-request\", key, `${key} took ${ms.toFixed(0)}ms`, ms, this.thresholds.requestMs);\n }\n }\n\n /** Record a Mongo operation, e.g. `User.find`. */\n recordMongo(operation: string, ms: number, isError = false): void {\n this.mongo.record(operation, ms, isError);\n if (ms > this.thresholds.mongoMs) {\n this.raise(\"slow-mongo\", operation, `Mongo ${operation} took ${ms.toFixed(0)}ms`, ms, this.thresholds.mongoMs);\n }\n }\n\n /** Record a Redis command, e.g. `get`. */\n recordRedis(command: string, ms: number, isError = false): void {\n this.redis.record(command, ms, isError);\n if (ms > this.thresholds.redisMs) {\n this.raise(\"slow-redis\", command, `Redis ${command} took ${ms.toFixed(0)}ms`, ms, this.thresholds.redisMs);\n }\n }\n\n // ---- instrumentation attach points ----------------------------------------\n\n /** Express/Connect middleware that times every request. */\n express() {\n return instrumentExpress(this);\n }\n\n /** Instrument a Mongoose instance so every query/aggregate/save is timed. */\n instrumentMongoose(mongoose: unknown): void {\n instrumentMongoose(mongoose, this);\n }\n\n /** Instrument an ioredis client so every command is timed. */\n instrumentRedis(client: unknown): void {\n instrumentRedis(client, this);\n }\n\n /** Begin periodic memory sampling + leak detection. Returns a stop function. */\n startMemoryMonitor(): () => void {\n if (this.memoryTimer) return () => this.stopMemoryMonitor();\n const interval = this.opts.memorySampleIntervalMs ?? 15_000;\n this.memoryTimer = setInterval(() => this.checkMemory(), interval);\n // Don't keep the event loop alive just for monitoring.\n this.memoryTimer.unref?.();\n return () => this.stopMemoryMonitor();\n }\n\n stopMemoryMonitor(): void {\n if (this.memoryTimer) {\n clearInterval(this.memoryTimer);\n this.memoryTimer = undefined;\n }\n }\n\n /**\n * Take one memory reading and evaluate the leak/high-heap thresholds.\n * `now`/`mem` are injectable for testing; both default to live process values.\n */\n checkMemory(now: number = Date.now(), mem: NodeJS.MemoryUsage = process.memoryUsage()): void {\n this.memory.sample(now, mem);\n const snap = this.memory.snapshot(mem);\n\n if (\n this.memory.size >= 5 &&\n snap.growthMbPerMin > this.thresholds.heapGrowthMbPerMin\n ) {\n this.raise(\n \"memory-leak\",\n \"heap\",\n `Heap growing ${snap.growthMbPerMin}MB/min (now ${snap.heapUsedMb}MB)`,\n snap.growthMbPerMin,\n this.thresholds.heapGrowthMbPerMin,\n );\n }\n if (this.thresholds.heapUsedMb > 0 && snap.heapUsedMb > this.thresholds.heapUsedMb) {\n this.raise(\n \"memory-high\",\n \"heap\",\n `Heap usage ${snap.heapUsedMb}MB`,\n snap.heapUsedMb,\n this.thresholds.heapUsedMb,\n );\n }\n }\n\n // ---- output ---------------------------------------------------------------\n\n snapshot(): InsightsSnapshot {\n const mem = process.memoryUsage();\n return {\n service: this.serviceName,\n uptimeSec: Math.round((Date.now() - this.startedAt) / 1000),\n http: this.http.snapshot(),\n mongo: this.mongo.snapshot(),\n redis: this.redis.snapshot(),\n counters: this.counters.counterSnapshot(),\n memory: this.memory.snapshot(mem),\n generatedAt: new Date().toISOString(),\n };\n }\n\n /** Express handler that responds with the JSON snapshot. */\n handler() {\n return (_req: unknown, res: { json?: (b: unknown) => void; setHeader?: (k: string, v: string) => void; end?: (s: string) => void }) => {\n const body = this.snapshot();\n if (typeof res.json === \"function\") {\n res.json(body);\n } else {\n res.setHeader?.(\"Content-Type\", \"application/json\");\n res.end?.(JSON.stringify(body));\n }\n };\n }\n\n // ---- alerting -------------------------------------------------------------\n\n private raise(type: AlertType, key: string, message: string, value: number, threshold: number): void {\n const dedupeKey = `${type}:${key}`;\n const now = Date.now();\n const last = this.lastAlertAt.get(dedupeKey) ?? 0;\n if (now - last < this.cooldownMs) return; // within cooldown — suppress\n this.lastAlertAt.set(dedupeKey, now);\n\n const alert: Alert = {\n type,\n key,\n message,\n value: Math.round(value * 100) / 100,\n threshold,\n at: new Date(now).toISOString(),\n };\n\n try {\n this.opts.onAlert?.(alert);\n } catch (err) {\n this.logger.error(`node-observe: onAlert handler threw: ${(err as Error).message}`);\n }\n if (this.opts.slackWebhook) {\n void postToSlack(this.opts.slackWebhook, alert, this.serviceName, this.logger);\n }\n }\n}\n"]}
@@ -0,0 +1,273 @@
1
+ /** Minimal shapes so we don't need `express` as a dependency. */
2
+ interface ObserveRequest {
3
+ method?: string;
4
+ route?: {
5
+ path?: string;
6
+ };
7
+ baseUrl?: string;
8
+ originalUrl?: string;
9
+ path?: string;
10
+ url?: string;
11
+ }
12
+ interface ObserveResponse {
13
+ statusCode?: number;
14
+ on(event: "finish" | "close", listener: () => void): void;
15
+ }
16
+ type ObserveNext = () => void;
17
+ /**
18
+ * Express/Connect middleware that records request latency on response finish.
19
+ * Prefers the matched route pattern (`req.route.path`) to keep cardinality low,
20
+ * falling back to the raw path.
21
+ */
22
+ declare function instrumentExpress(insights: Insights): (req: ObserveRequest, res: ObserveResponse, next: ObserveNext) => void;
23
+
24
+ /** Minimal logger contract. `console` satisfies it. */
25
+ interface Logger {
26
+ info(message: string): void;
27
+ error(message: string | Error): void;
28
+ warn?(message: string): void;
29
+ debug?(message: string): void;
30
+ }
31
+ /** Summary statistics for a set of recorded durations (all in milliseconds). */
32
+ interface HistogramSnapshot {
33
+ count: number;
34
+ /** Number of samples flagged as errors (e.g. HTTP 5xx, failed query). */
35
+ errors: number;
36
+ mean: number;
37
+ min: number;
38
+ max: number;
39
+ p50: number;
40
+ p95: number;
41
+ p99: number;
42
+ }
43
+ /** Categories of alert this library can raise. */
44
+ type AlertType = "slow-request" | "slow-mongo" | "slow-redis" | "memory-leak" | "memory-high";
45
+ interface Alert {
46
+ type: AlertType;
47
+ /** Stable key used for de-duplication / cooldown (e.g. the route or query). */
48
+ key: string;
49
+ message: string;
50
+ value: number;
51
+ threshold: number;
52
+ /** ISO timestamp. */
53
+ at: string;
54
+ }
55
+ interface InsightsThresholds {
56
+ /** Alert when a single request exceeds this many ms. Default: 1000. */
57
+ requestMs?: number;
58
+ /** Alert when a single Mongo operation exceeds this many ms. Default: 300. */
59
+ mongoMs?: number;
60
+ /** Alert when a single Redis command exceeds this many ms. Default: 100. */
61
+ redisMs?: number;
62
+ /** Alert when heap grows faster than this many MB/min (sustained). Default: 50. */
63
+ heapGrowthMbPerMin?: number;
64
+ /** Alert when heap-used exceeds this many MB. Default: disabled (0). */
65
+ heapUsedMb?: number;
66
+ }
67
+ interface InsightsOptions {
68
+ /** Label included in snapshots and alerts. Default: "node-observe". */
69
+ serviceName?: string;
70
+ /** Slack incoming-webhook URL. When set, alerts are posted to Slack. */
71
+ slackWebhook?: string;
72
+ /** Per-metric thresholds for alerting. */
73
+ thresholds?: InsightsThresholds;
74
+ /** Ring-buffer size per metric (bounds memory; affects percentile accuracy). Default: 1024. */
75
+ sampleSize?: number;
76
+ /** Minimum ms between repeats of the same alert key. Default: 60000. */
77
+ alertCooldownMs?: number;
78
+ /** How often to sample memory, in ms. Default: 15000. */
79
+ memorySampleIntervalMs?: number;
80
+ /** Custom alert sink, called for every alert (in addition to Slack). */
81
+ onAlert?: (alert: Alert) => void;
82
+ /** Logger for internal errors. Default: console. */
83
+ logger?: Logger;
84
+ /**
85
+ * Collapse high-cardinality URL paths (numeric / ObjectId segments → `:id`)
86
+ * so routes group together. Default: true. Set false to keep raw paths.
87
+ */
88
+ normalizePaths?: boolean;
89
+ }
90
+ /** Full point-in-time view of everything node-observe is tracking. */
91
+ interface InsightsSnapshot {
92
+ service: string;
93
+ /** Process uptime in seconds. */
94
+ uptimeSec: number;
95
+ http: Record<string, HistogramSnapshot>;
96
+ mongo: Record<string, HistogramSnapshot>;
97
+ redis: Record<string, HistogramSnapshot>;
98
+ counters: Record<string, number>;
99
+ memory: MemorySnapshot;
100
+ generatedAt: string;
101
+ }
102
+ interface MemorySnapshot {
103
+ heapUsedMb: number;
104
+ heapTotalMb: number;
105
+ rssMb: number;
106
+ externalMb: number;
107
+ /** Estimated heap growth rate over the recent window, MB/min (negative = shrinking). */
108
+ growthMbPerMin: number;
109
+ }
110
+
111
+ /**
112
+ * A bounded latency histogram. Keeps the most recent `cap` samples in a ring
113
+ * buffer (so memory is constant) while tracking lifetime count/min/max/sum and
114
+ * an error tally. Percentiles are computed over the retained window.
115
+ */
116
+ declare class Histogram {
117
+ private readonly buffer;
118
+ private head;
119
+ private readonly cap;
120
+ count: number;
121
+ errors: number;
122
+ private sum;
123
+ private minV;
124
+ private maxV;
125
+ constructor(cap?: number);
126
+ /** Record one duration (ms). Mark `isError` to count it toward the error tally. */
127
+ record(value: number, isError?: boolean): void;
128
+ /** Linear-interpolation percentile over the retained window. `p` in [0,100]. */
129
+ percentile(p: number): number;
130
+ snapshot(): HistogramSnapshot;
131
+ }
132
+ /** A named set of histograms plus simple counters. */
133
+ declare class MetricsRegistry {
134
+ private readonly cap;
135
+ private readonly histograms;
136
+ private readonly counters;
137
+ constructor(cap?: number);
138
+ record(name: string, value: number, isError?: boolean): void;
139
+ increment(name: string, by?: number): void;
140
+ snapshot(): Record<string, HistogramSnapshot>;
141
+ counterSnapshot(): Record<string, number>;
142
+ reset(): void;
143
+ }
144
+
145
+ /**
146
+ * The central observability handle. Create one per process, attach the
147
+ * instrumentations you want, and read {@link Insights.snapshot} (or mount
148
+ * {@link Insights.handler}) to see latency percentiles, query timings, memory
149
+ * trend, and counters. Threshold breaches raise {@link Alert}s, optionally
150
+ * pushed to Slack.
151
+ *
152
+ * @example
153
+ * ```ts
154
+ * const insights = new Insights({ serviceName: "api", slackWebhook: process.env.SLACK_URL });
155
+ * app.use(insights.express());
156
+ * insights.instrumentMongoose(mongoose);
157
+ * insights.instrumentRedis(redis);
158
+ * insights.startMemoryMonitor();
159
+ * app.get("/insights", insights.handler());
160
+ * ```
161
+ */
162
+ declare class Insights {
163
+ readonly http: MetricsRegistry;
164
+ readonly mongo: MetricsRegistry;
165
+ readonly redis: MetricsRegistry;
166
+ private readonly counters;
167
+ private readonly memory;
168
+ readonly serviceName: string;
169
+ readonly thresholds: Required<NonNullable<InsightsOptions["thresholds"]>>;
170
+ readonly logger: Logger;
171
+ private readonly opts;
172
+ private readonly cooldownMs;
173
+ private readonly lastAlertAt;
174
+ private memoryTimer;
175
+ private readonly startedAt;
176
+ constructor(options?: InsightsOptions);
177
+ /** Record an HTTP request. `route` is normalized when `normalizePaths` is on. */
178
+ recordRequest(method: string, route: string, statusCode: number, ms: number): void;
179
+ /** Record a Mongo operation, e.g. `User.find`. */
180
+ recordMongo(operation: string, ms: number, isError?: boolean): void;
181
+ /** Record a Redis command, e.g. `get`. */
182
+ recordRedis(command: string, ms: number, isError?: boolean): void;
183
+ /** Express/Connect middleware that times every request. */
184
+ express(): (req: ObserveRequest, res: ObserveResponse, next: ObserveNext) => void;
185
+ /** Instrument a Mongoose instance so every query/aggregate/save is timed. */
186
+ instrumentMongoose(mongoose: unknown): void;
187
+ /** Instrument an ioredis client so every command is timed. */
188
+ instrumentRedis(client: unknown): void;
189
+ /** Begin periodic memory sampling + leak detection. Returns a stop function. */
190
+ startMemoryMonitor(): () => void;
191
+ stopMemoryMonitor(): void;
192
+ /**
193
+ * Take one memory reading and evaluate the leak/high-heap thresholds.
194
+ * `now`/`mem` are injectable for testing; both default to live process values.
195
+ */
196
+ checkMemory(now?: number, mem?: NodeJS.MemoryUsage): void;
197
+ snapshot(): InsightsSnapshot;
198
+ /** Express handler that responds with the JSON snapshot. */
199
+ handler(): (_req: unknown, res: {
200
+ json?: (b: unknown) => void;
201
+ setHeader?: (k: string, v: string) => void;
202
+ end?: (s: string) => void;
203
+ }) => void;
204
+ private raise;
205
+ }
206
+
207
+ /**
208
+ * Tracks heap-used over time and estimates the growth trend via least-squares
209
+ * linear regression. A sustained positive slope is the signal of a memory leak —
210
+ * far more reliable than reacting to a single high reading (which is usually
211
+ * just GC timing).
212
+ */
213
+ declare class MemoryMonitor {
214
+ private readonly samples;
215
+ private readonly window;
216
+ constructor(window?: number);
217
+ /** Add the current memory reading. `now`/`mem` are injectable for testing. */
218
+ sample(now: number, mem: NodeJS.MemoryUsage): void;
219
+ /** Heap growth rate in MB per minute over the retained window. */
220
+ growthMbPerMin(): number;
221
+ snapshot(mem: NodeJS.MemoryUsage): MemorySnapshot;
222
+ /** Number of samples currently retained. */
223
+ get size(): number;
224
+ }
225
+
226
+ /**
227
+ * Register a global Mongoose plugin that times every query, aggregate, and
228
+ * document save, recording `<Model>.<op>` durations on the given Insights.
229
+ *
230
+ * Call this **before** your models are compiled (i.e. right after you import
231
+ * mongoose) so the plugin applies to every schema.
232
+ *
233
+ * @example
234
+ * ```ts
235
+ * import mongoose from "mongoose";
236
+ * insights.instrumentMongoose(mongoose);
237
+ * ```
238
+ */
239
+ declare function instrumentMongoose(mongoose: unknown, insights: Insights): void;
240
+
241
+ /**
242
+ * Instrument an ioredis client by wrapping `sendCommand`, timing every command
243
+ * (`get`, `set`, `hgetall`, …) via the command's own promise. Safe to call once
244
+ * per client; re-instrumenting the same client is a no-op.
245
+ *
246
+ * @example
247
+ * ```ts
248
+ * import Redis from "ioredis";
249
+ * const redis = new Redis(url);
250
+ * insights.instrumentRedis(redis);
251
+ * ```
252
+ */
253
+ declare function instrumentRedis(client: unknown, insights: Insights): void;
254
+
255
+ /** Format an alert as Slack message text. */
256
+ declare function formatSlackMessage(alert: Alert, service: string): string;
257
+ /**
258
+ * Post an alert to a Slack incoming webhook. Uses the global `fetch` (Node 18+);
259
+ * failures are logged, never thrown — observability must not crash the app.
260
+ */
261
+ declare function postToSlack(webhook: string, alert: Alert, service: string, logger?: Logger): Promise<void>;
262
+
263
+ /**
264
+ * Collapse high-cardinality path segments so routes group together in metrics.
265
+ * Numeric ids, Mongo ObjectIds, UUIDs, and long hex tokens all become `:id`.
266
+ *
267
+ * `/users/64b8.../posts/12` → `/users/:id/posts/:id`
268
+ */
269
+ declare function normalizePath(path: string): string;
270
+ /** Elapsed milliseconds since an `process.hrtime.bigint()` start, as a float. */
271
+ declare function elapsedMs(start: bigint): number;
272
+
273
+ export { type Alert, type AlertType, Histogram, type HistogramSnapshot, Insights, type InsightsOptions, type InsightsSnapshot, type InsightsThresholds, type Logger, MemoryMonitor, type MemorySnapshot, MetricsRegistry, type ObserveNext, type ObserveRequest, type ObserveResponse, elapsedMs, formatSlackMessage, instrumentExpress, instrumentMongoose, instrumentRedis, normalizePath, postToSlack };
@@ -0,0 +1,273 @@
1
+ /** Minimal shapes so we don't need `express` as a dependency. */
2
+ interface ObserveRequest {
3
+ method?: string;
4
+ route?: {
5
+ path?: string;
6
+ };
7
+ baseUrl?: string;
8
+ originalUrl?: string;
9
+ path?: string;
10
+ url?: string;
11
+ }
12
+ interface ObserveResponse {
13
+ statusCode?: number;
14
+ on(event: "finish" | "close", listener: () => void): void;
15
+ }
16
+ type ObserveNext = () => void;
17
+ /**
18
+ * Express/Connect middleware that records request latency on response finish.
19
+ * Prefers the matched route pattern (`req.route.path`) to keep cardinality low,
20
+ * falling back to the raw path.
21
+ */
22
+ declare function instrumentExpress(insights: Insights): (req: ObserveRequest, res: ObserveResponse, next: ObserveNext) => void;
23
+
24
+ /** Minimal logger contract. `console` satisfies it. */
25
+ interface Logger {
26
+ info(message: string): void;
27
+ error(message: string | Error): void;
28
+ warn?(message: string): void;
29
+ debug?(message: string): void;
30
+ }
31
+ /** Summary statistics for a set of recorded durations (all in milliseconds). */
32
+ interface HistogramSnapshot {
33
+ count: number;
34
+ /** Number of samples flagged as errors (e.g. HTTP 5xx, failed query). */
35
+ errors: number;
36
+ mean: number;
37
+ min: number;
38
+ max: number;
39
+ p50: number;
40
+ p95: number;
41
+ p99: number;
42
+ }
43
+ /** Categories of alert this library can raise. */
44
+ type AlertType = "slow-request" | "slow-mongo" | "slow-redis" | "memory-leak" | "memory-high";
45
+ interface Alert {
46
+ type: AlertType;
47
+ /** Stable key used for de-duplication / cooldown (e.g. the route or query). */
48
+ key: string;
49
+ message: string;
50
+ value: number;
51
+ threshold: number;
52
+ /** ISO timestamp. */
53
+ at: string;
54
+ }
55
+ interface InsightsThresholds {
56
+ /** Alert when a single request exceeds this many ms. Default: 1000. */
57
+ requestMs?: number;
58
+ /** Alert when a single Mongo operation exceeds this many ms. Default: 300. */
59
+ mongoMs?: number;
60
+ /** Alert when a single Redis command exceeds this many ms. Default: 100. */
61
+ redisMs?: number;
62
+ /** Alert when heap grows faster than this many MB/min (sustained). Default: 50. */
63
+ heapGrowthMbPerMin?: number;
64
+ /** Alert when heap-used exceeds this many MB. Default: disabled (0). */
65
+ heapUsedMb?: number;
66
+ }
67
+ interface InsightsOptions {
68
+ /** Label included in snapshots and alerts. Default: "node-observe". */
69
+ serviceName?: string;
70
+ /** Slack incoming-webhook URL. When set, alerts are posted to Slack. */
71
+ slackWebhook?: string;
72
+ /** Per-metric thresholds for alerting. */
73
+ thresholds?: InsightsThresholds;
74
+ /** Ring-buffer size per metric (bounds memory; affects percentile accuracy). Default: 1024. */
75
+ sampleSize?: number;
76
+ /** Minimum ms between repeats of the same alert key. Default: 60000. */
77
+ alertCooldownMs?: number;
78
+ /** How often to sample memory, in ms. Default: 15000. */
79
+ memorySampleIntervalMs?: number;
80
+ /** Custom alert sink, called for every alert (in addition to Slack). */
81
+ onAlert?: (alert: Alert) => void;
82
+ /** Logger for internal errors. Default: console. */
83
+ logger?: Logger;
84
+ /**
85
+ * Collapse high-cardinality URL paths (numeric / ObjectId segments → `:id`)
86
+ * so routes group together. Default: true. Set false to keep raw paths.
87
+ */
88
+ normalizePaths?: boolean;
89
+ }
90
+ /** Full point-in-time view of everything node-observe is tracking. */
91
+ interface InsightsSnapshot {
92
+ service: string;
93
+ /** Process uptime in seconds. */
94
+ uptimeSec: number;
95
+ http: Record<string, HistogramSnapshot>;
96
+ mongo: Record<string, HistogramSnapshot>;
97
+ redis: Record<string, HistogramSnapshot>;
98
+ counters: Record<string, number>;
99
+ memory: MemorySnapshot;
100
+ generatedAt: string;
101
+ }
102
+ interface MemorySnapshot {
103
+ heapUsedMb: number;
104
+ heapTotalMb: number;
105
+ rssMb: number;
106
+ externalMb: number;
107
+ /** Estimated heap growth rate over the recent window, MB/min (negative = shrinking). */
108
+ growthMbPerMin: number;
109
+ }
110
+
111
+ /**
112
+ * A bounded latency histogram. Keeps the most recent `cap` samples in a ring
113
+ * buffer (so memory is constant) while tracking lifetime count/min/max/sum and
114
+ * an error tally. Percentiles are computed over the retained window.
115
+ */
116
+ declare class Histogram {
117
+ private readonly buffer;
118
+ private head;
119
+ private readonly cap;
120
+ count: number;
121
+ errors: number;
122
+ private sum;
123
+ private minV;
124
+ private maxV;
125
+ constructor(cap?: number);
126
+ /** Record one duration (ms). Mark `isError` to count it toward the error tally. */
127
+ record(value: number, isError?: boolean): void;
128
+ /** Linear-interpolation percentile over the retained window. `p` in [0,100]. */
129
+ percentile(p: number): number;
130
+ snapshot(): HistogramSnapshot;
131
+ }
132
+ /** A named set of histograms plus simple counters. */
133
+ declare class MetricsRegistry {
134
+ private readonly cap;
135
+ private readonly histograms;
136
+ private readonly counters;
137
+ constructor(cap?: number);
138
+ record(name: string, value: number, isError?: boolean): void;
139
+ increment(name: string, by?: number): void;
140
+ snapshot(): Record<string, HistogramSnapshot>;
141
+ counterSnapshot(): Record<string, number>;
142
+ reset(): void;
143
+ }
144
+
145
+ /**
146
+ * The central observability handle. Create one per process, attach the
147
+ * instrumentations you want, and read {@link Insights.snapshot} (or mount
148
+ * {@link Insights.handler}) to see latency percentiles, query timings, memory
149
+ * trend, and counters. Threshold breaches raise {@link Alert}s, optionally
150
+ * pushed to Slack.
151
+ *
152
+ * @example
153
+ * ```ts
154
+ * const insights = new Insights({ serviceName: "api", slackWebhook: process.env.SLACK_URL });
155
+ * app.use(insights.express());
156
+ * insights.instrumentMongoose(mongoose);
157
+ * insights.instrumentRedis(redis);
158
+ * insights.startMemoryMonitor();
159
+ * app.get("/insights", insights.handler());
160
+ * ```
161
+ */
162
+ declare class Insights {
163
+ readonly http: MetricsRegistry;
164
+ readonly mongo: MetricsRegistry;
165
+ readonly redis: MetricsRegistry;
166
+ private readonly counters;
167
+ private readonly memory;
168
+ readonly serviceName: string;
169
+ readonly thresholds: Required<NonNullable<InsightsOptions["thresholds"]>>;
170
+ readonly logger: Logger;
171
+ private readonly opts;
172
+ private readonly cooldownMs;
173
+ private readonly lastAlertAt;
174
+ private memoryTimer;
175
+ private readonly startedAt;
176
+ constructor(options?: InsightsOptions);
177
+ /** Record an HTTP request. `route` is normalized when `normalizePaths` is on. */
178
+ recordRequest(method: string, route: string, statusCode: number, ms: number): void;
179
+ /** Record a Mongo operation, e.g. `User.find`. */
180
+ recordMongo(operation: string, ms: number, isError?: boolean): void;
181
+ /** Record a Redis command, e.g. `get`. */
182
+ recordRedis(command: string, ms: number, isError?: boolean): void;
183
+ /** Express/Connect middleware that times every request. */
184
+ express(): (req: ObserveRequest, res: ObserveResponse, next: ObserveNext) => void;
185
+ /** Instrument a Mongoose instance so every query/aggregate/save is timed. */
186
+ instrumentMongoose(mongoose: unknown): void;
187
+ /** Instrument an ioredis client so every command is timed. */
188
+ instrumentRedis(client: unknown): void;
189
+ /** Begin periodic memory sampling + leak detection. Returns a stop function. */
190
+ startMemoryMonitor(): () => void;
191
+ stopMemoryMonitor(): void;
192
+ /**
193
+ * Take one memory reading and evaluate the leak/high-heap thresholds.
194
+ * `now`/`mem` are injectable for testing; both default to live process values.
195
+ */
196
+ checkMemory(now?: number, mem?: NodeJS.MemoryUsage): void;
197
+ snapshot(): InsightsSnapshot;
198
+ /** Express handler that responds with the JSON snapshot. */
199
+ handler(): (_req: unknown, res: {
200
+ json?: (b: unknown) => void;
201
+ setHeader?: (k: string, v: string) => void;
202
+ end?: (s: string) => void;
203
+ }) => void;
204
+ private raise;
205
+ }
206
+
207
+ /**
208
+ * Tracks heap-used over time and estimates the growth trend via least-squares
209
+ * linear regression. A sustained positive slope is the signal of a memory leak —
210
+ * far more reliable than reacting to a single high reading (which is usually
211
+ * just GC timing).
212
+ */
213
+ declare class MemoryMonitor {
214
+ private readonly samples;
215
+ private readonly window;
216
+ constructor(window?: number);
217
+ /** Add the current memory reading. `now`/`mem` are injectable for testing. */
218
+ sample(now: number, mem: NodeJS.MemoryUsage): void;
219
+ /** Heap growth rate in MB per minute over the retained window. */
220
+ growthMbPerMin(): number;
221
+ snapshot(mem: NodeJS.MemoryUsage): MemorySnapshot;
222
+ /** Number of samples currently retained. */
223
+ get size(): number;
224
+ }
225
+
226
+ /**
227
+ * Register a global Mongoose plugin that times every query, aggregate, and
228
+ * document save, recording `<Model>.<op>` durations on the given Insights.
229
+ *
230
+ * Call this **before** your models are compiled (i.e. right after you import
231
+ * mongoose) so the plugin applies to every schema.
232
+ *
233
+ * @example
234
+ * ```ts
235
+ * import mongoose from "mongoose";
236
+ * insights.instrumentMongoose(mongoose);
237
+ * ```
238
+ */
239
+ declare function instrumentMongoose(mongoose: unknown, insights: Insights): void;
240
+
241
+ /**
242
+ * Instrument an ioredis client by wrapping `sendCommand`, timing every command
243
+ * (`get`, `set`, `hgetall`, …) via the command's own promise. Safe to call once
244
+ * per client; re-instrumenting the same client is a no-op.
245
+ *
246
+ * @example
247
+ * ```ts
248
+ * import Redis from "ioredis";
249
+ * const redis = new Redis(url);
250
+ * insights.instrumentRedis(redis);
251
+ * ```
252
+ */
253
+ declare function instrumentRedis(client: unknown, insights: Insights): void;
254
+
255
+ /** Format an alert as Slack message text. */
256
+ declare function formatSlackMessage(alert: Alert, service: string): string;
257
+ /**
258
+ * Post an alert to a Slack incoming webhook. Uses the global `fetch` (Node 18+);
259
+ * failures are logged, never thrown — observability must not crash the app.
260
+ */
261
+ declare function postToSlack(webhook: string, alert: Alert, service: string, logger?: Logger): Promise<void>;
262
+
263
+ /**
264
+ * Collapse high-cardinality path segments so routes group together in metrics.
265
+ * Numeric ids, Mongo ObjectIds, UUIDs, and long hex tokens all become `:id`.
266
+ *
267
+ * `/users/64b8.../posts/12` → `/users/:id/posts/:id`
268
+ */
269
+ declare function normalizePath(path: string): string;
270
+ /** Elapsed milliseconds since an `process.hrtime.bigint()` start, as a float. */
271
+ declare function elapsedMs(start: bigint): number;
272
+
273
+ export { type Alert, type AlertType, Histogram, type HistogramSnapshot, Insights, type InsightsOptions, type InsightsSnapshot, type InsightsThresholds, type Logger, MemoryMonitor, type MemorySnapshot, MetricsRegistry, type ObserveNext, type ObserveRequest, type ObserveResponse, elapsedMs, formatSlackMessage, instrumentExpress, instrumentMongoose, instrumentRedis, normalizePath, postToSlack };