flow-debugger 1.0.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.
- package/PORTFOLIO_README_SECTION.md +177 -0
- package/README.md +251 -0
- package/dashboard/app.js +339 -0
- package/dashboard/index.html +168 -0
- package/dashboard/style.css +846 -0
- package/dist/cjs/core/Analytics.js +174 -0
- package/dist/cjs/core/Analytics.js.map +1 -0
- package/dist/cjs/core/Classifier.js +66 -0
- package/dist/cjs/core/Classifier.js.map +1 -0
- package/dist/cjs/core/HealthMonitor.js +79 -0
- package/dist/cjs/core/HealthMonitor.js.map +1 -0
- package/dist/cjs/core/RootCause.js +89 -0
- package/dist/cjs/core/RootCause.js.map +1 -0
- package/dist/cjs/core/Sampler.js +34 -0
- package/dist/cjs/core/Sampler.js.map +1 -0
- package/dist/cjs/core/Timeline.js +90 -0
- package/dist/cjs/core/Timeline.js.map +1 -0
- package/dist/cjs/core/TraceEngine.js +222 -0
- package/dist/cjs/core/TraceEngine.js.map +1 -0
- package/dist/cjs/core/types.js +21 -0
- package/dist/cjs/core/types.js.map +1 -0
- package/dist/cjs/index.js +46 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/integrations/axios.js +136 -0
- package/dist/cjs/integrations/axios.js.map +1 -0
- package/dist/cjs/integrations/fetch.js +153 -0
- package/dist/cjs/integrations/fetch.js.map +1 -0
- package/dist/cjs/integrations/mongo.js +111 -0
- package/dist/cjs/integrations/mongo.js.map +1 -0
- package/dist/cjs/integrations/mysql.js +212 -0
- package/dist/cjs/integrations/mysql.js.map +1 -0
- package/dist/cjs/integrations/postgres.js +182 -0
- package/dist/cjs/integrations/postgres.js.map +1 -0
- package/dist/cjs/integrations/redis.js +105 -0
- package/dist/cjs/integrations/redis.js.map +1 -0
- package/dist/cjs/middleware/express.js +255 -0
- package/dist/cjs/middleware/express.js.map +1 -0
- package/dist/esm/core/Analytics.js +170 -0
- package/dist/esm/core/Analytics.js.map +1 -0
- package/dist/esm/core/Classifier.js +61 -0
- package/dist/esm/core/Classifier.js.map +1 -0
- package/dist/esm/core/HealthMonitor.js +75 -0
- package/dist/esm/core/HealthMonitor.js.map +1 -0
- package/dist/esm/core/RootCause.js +86 -0
- package/dist/esm/core/RootCause.js.map +1 -0
- package/dist/esm/core/Sampler.js +30 -0
- package/dist/esm/core/Sampler.js.map +1 -0
- package/dist/esm/core/Timeline.js +86 -0
- package/dist/esm/core/Timeline.js.map +1 -0
- package/dist/esm/core/TraceEngine.js +217 -0
- package/dist/esm/core/TraceEngine.js.map +1 -0
- package/dist/esm/core/types.js +18 -0
- package/dist/esm/core/types.js.map +1 -0
- package/dist/esm/index.js +22 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/integrations/axios.js +133 -0
- package/dist/esm/integrations/axios.js.map +1 -0
- package/dist/esm/integrations/fetch.js +149 -0
- package/dist/esm/integrations/fetch.js.map +1 -0
- package/dist/esm/integrations/mongo.js +107 -0
- package/dist/esm/integrations/mongo.js.map +1 -0
- package/dist/esm/integrations/mysql.js +209 -0
- package/dist/esm/integrations/mysql.js.map +1 -0
- package/dist/esm/integrations/postgres.js +179 -0
- package/dist/esm/integrations/postgres.js.map +1 -0
- package/dist/esm/integrations/redis.js +102 -0
- package/dist/esm/integrations/redis.js.map +1 -0
- package/dist/esm/middleware/express.js +219 -0
- package/dist/esm/middleware/express.js.map +1 -0
- package/dist/types/core/Analytics.d.ts +35 -0
- package/dist/types/core/Analytics.d.ts.map +1 -0
- package/dist/types/core/Classifier.d.ts +21 -0
- package/dist/types/core/Classifier.d.ts.map +1 -0
- package/dist/types/core/HealthMonitor.d.ts +14 -0
- package/dist/types/core/HealthMonitor.d.ts.map +1 -0
- package/dist/types/core/RootCause.d.ts +12 -0
- package/dist/types/core/RootCause.d.ts.map +1 -0
- package/dist/types/core/Sampler.d.ts +13 -0
- package/dist/types/core/Sampler.d.ts.map +1 -0
- package/dist/types/core/Timeline.d.ts +22 -0
- package/dist/types/core/Timeline.d.ts.map +1 -0
- package/dist/types/core/TraceEngine.d.ts +47 -0
- package/dist/types/core/TraceEngine.d.ts.map +1 -0
- package/dist/types/core/types.d.ts +118 -0
- package/dist/types/core/types.d.ts.map +1 -0
- package/dist/types/index.d.ts +18 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/integrations/axios.d.ts +22 -0
- package/dist/types/integrations/axios.d.ts.map +1 -0
- package/dist/types/integrations/fetch.d.ts +25 -0
- package/dist/types/integrations/fetch.d.ts.map +1 -0
- package/dist/types/integrations/mongo.d.ts +26 -0
- package/dist/types/integrations/mongo.d.ts.map +1 -0
- package/dist/types/integrations/mysql.d.ts +20 -0
- package/dist/types/integrations/mysql.d.ts.map +1 -0
- package/dist/types/integrations/postgres.d.ts +20 -0
- package/dist/types/integrations/postgres.d.ts.map +1 -0
- package/dist/types/integrations/redis.d.ts +20 -0
- package/dist/types/integrations/redis.d.ts.map +1 -0
- package/dist/types/middleware/express.d.ts +39 -0
- package/dist/types/middleware/express.d.ts.map +1 -0
- package/example/server.ts +234 -0
- package/jest.config.js +8 -0
- package/package.json +110 -0
- package/portfolio-repo/APIRESPONSE DASH.png +0 -0
- package/portfolio-repo/PAYLOAD.png +0 -0
- package/portfolio-repo/README.md +182 -0
- package/src/core/Analytics.ts +209 -0
- package/src/core/Classifier.ts +82 -0
- package/src/core/HealthMonitor.ts +92 -0
- package/src/core/RootCause.ts +105 -0
- package/src/core/Sampler.ts +35 -0
- package/src/core/Timeline.ts +108 -0
- package/src/core/TraceEngine.ts +266 -0
- package/src/core/types.ts +170 -0
- package/src/index.ts +42 -0
- package/src/integrations/axios.ts +164 -0
- package/src/integrations/fetch.ts +172 -0
- package/src/integrations/mongo.ts +130 -0
- package/src/integrations/mysql.ts +239 -0
- package/src/integrations/postgres.ts +217 -0
- package/src/integrations/redis.ts +122 -0
- package/src/middleware/express.ts +264 -0
- package/tests/Analytics.test.ts +136 -0
- package/tests/Classifier.test.ts +57 -0
- package/tests/RootCause.test.ts +69 -0
- package/tests/TraceEngine.test.ts +110 -0
- package/tsconfig.cjs.json +9 -0
- package/tsconfig.esm.json +9 -0
- package/tsconfig.json +31 -0
- package/tsconfig.types.json +8 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ─────────────────────────────────────────────────────────────
|
|
3
|
+
// flow-debugger — Express Middleware
|
|
4
|
+
// Auto-traces every request, provides /__debugger API + dashboard
|
|
5
|
+
// ─────────────────────────────────────────────────────────────
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.flowDebugger = flowDebugger;
|
|
41
|
+
const TraceEngine_1 = require("../core/TraceEngine");
|
|
42
|
+
const Analytics_1 = require("../core/Analytics");
|
|
43
|
+
const Sampler_1 = require("../core/Sampler");
|
|
44
|
+
const types_1 = require("../core/types");
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
const fs = __importStar(require("fs"));
|
|
47
|
+
// AsyncLocalStorage for automatic tracer context propagation
|
|
48
|
+
let asyncLocalStorage;
|
|
49
|
+
try {
|
|
50
|
+
const { AsyncLocalStorage } = require('async_hooks');
|
|
51
|
+
asyncLocalStorage = new AsyncLocalStorage();
|
|
52
|
+
}
|
|
53
|
+
catch (_) {
|
|
54
|
+
// Fallback for environments without async_hooks
|
|
55
|
+
asyncLocalStorage = null;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Create the flow-debugger Express middleware.
|
|
59
|
+
*
|
|
60
|
+
* Usage:
|
|
61
|
+
* const debugger = flowDebugger({ slowThreshold: 500 });
|
|
62
|
+
* app.use(debugger.middleware);
|
|
63
|
+
*
|
|
64
|
+
* // Auto-instrument databases
|
|
65
|
+
* mongoTracer(mongoose, { getTracer: debugger.getTracer });
|
|
66
|
+
* mysqlTracer(pool, { getTracer: debugger.getTracer });
|
|
67
|
+
*
|
|
68
|
+
* // Dashboard at: GET /__debugger/dashboard
|
|
69
|
+
* // API at: GET /__debugger
|
|
70
|
+
*/
|
|
71
|
+
function flowDebugger(config) {
|
|
72
|
+
const mergedConfig = { ...types_1.DEFAULT_CONFIG, ...config };
|
|
73
|
+
const engine = new TraceEngine_1.TraceEngine(mergedConfig);
|
|
74
|
+
const analytics = new Analytics_1.Analytics(mergedConfig.maxTraces);
|
|
75
|
+
const sampler = new Sampler_1.Sampler(mergedConfig.samplingRate, mergedConfig.alwaysSampleErrors);
|
|
76
|
+
// Current tracer per async context
|
|
77
|
+
let fallbackTracer = null;
|
|
78
|
+
const getTracer = () => {
|
|
79
|
+
if (asyncLocalStorage) {
|
|
80
|
+
try {
|
|
81
|
+
const store = asyncLocalStorage.getStore();
|
|
82
|
+
return store?.tracer || null;
|
|
83
|
+
}
|
|
84
|
+
catch (_) { }
|
|
85
|
+
}
|
|
86
|
+
return fallbackTracer;
|
|
87
|
+
};
|
|
88
|
+
const middleware = (req, res, next) => {
|
|
89
|
+
try {
|
|
90
|
+
// Skip debugger endpoints
|
|
91
|
+
if (req.path?.startsWith('/__debugger')) {
|
|
92
|
+
return handleDebuggerRoute(req, res, next, analytics, mergedConfig);
|
|
93
|
+
}
|
|
94
|
+
if (!mergedConfig.enabled)
|
|
95
|
+
return next();
|
|
96
|
+
// Sampling
|
|
97
|
+
if (!sampler.shouldSample())
|
|
98
|
+
return next();
|
|
99
|
+
const tracer = engine.startTrace(req.path || req.url, req.method);
|
|
100
|
+
req.tracer = tracer;
|
|
101
|
+
req.traceId = tracer.getTraceId();
|
|
102
|
+
// Set header for client correlation
|
|
103
|
+
res.setHeader('X-Trace-Id', tracer.getTraceId());
|
|
104
|
+
const run = () => {
|
|
105
|
+
fallbackTracer = tracer;
|
|
106
|
+
// Calculate payload size if body exists
|
|
107
|
+
let payloadSize;
|
|
108
|
+
try {
|
|
109
|
+
if (req.body) {
|
|
110
|
+
const bodyStr = JSON.stringify(req.body);
|
|
111
|
+
payloadSize = Buffer.byteLength(bodyStr, 'utf8');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (_) {
|
|
115
|
+
// ignore
|
|
116
|
+
}
|
|
117
|
+
// Hook into response finish
|
|
118
|
+
const originalEnd = res.end;
|
|
119
|
+
res.end = function (...args) {
|
|
120
|
+
try {
|
|
121
|
+
const trace = tracer.end(res.statusCode);
|
|
122
|
+
// Add environment and payload size to trace
|
|
123
|
+
trace.environment = mergedConfig.environment;
|
|
124
|
+
trace.payloadSize = payloadSize;
|
|
125
|
+
analytics.record(trace);
|
|
126
|
+
// If error and we had skipped sampling, re-sample
|
|
127
|
+
}
|
|
128
|
+
catch (_) {
|
|
129
|
+
// never crash
|
|
130
|
+
}
|
|
131
|
+
fallbackTracer = null;
|
|
132
|
+
return originalEnd.apply(res, args);
|
|
133
|
+
};
|
|
134
|
+
next();
|
|
135
|
+
};
|
|
136
|
+
// Use AsyncLocalStorage if available
|
|
137
|
+
if (asyncLocalStorage) {
|
|
138
|
+
asyncLocalStorage.run({ tracer }, run);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
run();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch (_) {
|
|
145
|
+
// Production-safe: if debugger fails, pass through
|
|
146
|
+
next();
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
return { middleware, engine, analytics, getTracer };
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Handle /__debugger routes
|
|
153
|
+
*/
|
|
154
|
+
function handleDebuggerRoute(req, res, next, analytics, config) {
|
|
155
|
+
if (!config.enableDashboard)
|
|
156
|
+
return next();
|
|
157
|
+
try {
|
|
158
|
+
const subPath = req.path.replace('/__debugger', '') || '/';
|
|
159
|
+
switch (subPath) {
|
|
160
|
+
case '/':
|
|
161
|
+
case '/api':
|
|
162
|
+
// JSON analytics API
|
|
163
|
+
res.json(analytics.getReport());
|
|
164
|
+
break;
|
|
165
|
+
case '/dashboard': {
|
|
166
|
+
// Serve the HTML dashboard
|
|
167
|
+
const dashboardPath = path.resolve(__dirname, '../../dashboard/index.html');
|
|
168
|
+
if (fs.existsSync(dashboardPath)) {
|
|
169
|
+
res.sendFile(dashboardPath);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
// Fallback: inline minimal dashboard
|
|
173
|
+
res.send(getInlineDashboard());
|
|
174
|
+
}
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
case '/dashboard/style.css': {
|
|
178
|
+
const cssPath = path.resolve(__dirname, '../../dashboard/style.css');
|
|
179
|
+
if (fs.existsSync(cssPath)) {
|
|
180
|
+
res.type('text/css').sendFile(cssPath);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
res.status(404).send('Not found');
|
|
184
|
+
}
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
case '/dashboard/app.js': {
|
|
188
|
+
const jsPath = path.resolve(__dirname, '../../dashboard/app.js');
|
|
189
|
+
if (fs.existsSync(jsPath)) {
|
|
190
|
+
res.type('application/javascript').sendFile(jsPath);
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
res.status(404).send('Not found');
|
|
194
|
+
}
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
case '/health':
|
|
198
|
+
res.json(analytics.getHealthMonitor().getAllHealth());
|
|
199
|
+
break;
|
|
200
|
+
case '/endpoint': {
|
|
201
|
+
const endpoint = req.query?.path || req.query?.endpoint;
|
|
202
|
+
if (endpoint) {
|
|
203
|
+
const report = analytics.getEndpointReport(String(endpoint));
|
|
204
|
+
res.json(report || { error: 'Endpoint not found' });
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
res.json({ error: 'Provide ?path=/your/endpoint' });
|
|
208
|
+
}
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
case '/search': {
|
|
212
|
+
const query = req.query?.q || req.query?.query;
|
|
213
|
+
const env = req.query?.env;
|
|
214
|
+
const limit = req.query?.limit ? parseInt(String(req.query.limit), 10) : 50;
|
|
215
|
+
if (query) {
|
|
216
|
+
const results = analytics.searchTraces(String(query), { env: env ? String(env) : undefined, limit });
|
|
217
|
+
res.json({ query, results, count: results.length });
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
res.json({ error: 'Provide ?q=search_term' });
|
|
221
|
+
}
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
default:
|
|
225
|
+
next();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
catch (err) {
|
|
229
|
+
res.status(500).json({ error: 'Debugger dashboard error' });
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/** Inline fallback dashboard if files aren't found */
|
|
233
|
+
function getInlineDashboard() {
|
|
234
|
+
return `<!DOCTYPE html>
|
|
235
|
+
<html lang="en">
|
|
236
|
+
<head>
|
|
237
|
+
<meta charset="UTF-8">
|
|
238
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
239
|
+
<title>Flow Debugger Dashboard</title>
|
|
240
|
+
<style>body{font-family:system-ui;background:#0a0a1a;color:#e0e0e0;padding:20px}
|
|
241
|
+
h1{color:#7c3aed}.card{background:#1a1a2e;border-radius:12px;padding:20px;margin:10px 0;border:1px solid #2a2a4a}</style>
|
|
242
|
+
</head>
|
|
243
|
+
<body>
|
|
244
|
+
<h1>🔍 Flow Debugger</h1>
|
|
245
|
+
<p>Dashboard files not found. API available at <a href="/__debugger" style="color:#7c3aed">/__debugger</a></p>
|
|
246
|
+
<div class="card" id="data">Loading...</div>
|
|
247
|
+
<script>
|
|
248
|
+
fetch('/__debugger').then(r=>r.json()).then(d=>{
|
|
249
|
+
document.getElementById('data').innerHTML='<pre>'+JSON.stringify(d,null,2)+'</pre>';
|
|
250
|
+
});
|
|
251
|
+
</script>
|
|
252
|
+
</body>
|
|
253
|
+
</html>`;
|
|
254
|
+
}
|
|
255
|
+
//# sourceMappingURL=express.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"express.js","sourceRoot":"","sources":["../../../src/middleware/express.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// flow-debugger — Analytics Engine
|
|
3
|
+
// Per-endpoint aggregation + service failure grouping
|
|
4
|
+
// ─────────────────────────────────────────────────────────────
|
|
5
|
+
import { HealthMonitor } from './HealthMonitor';
|
|
6
|
+
export class Analytics {
|
|
7
|
+
constructor(maxTraces = 1000) {
|
|
8
|
+
this.traces = [];
|
|
9
|
+
this.maxTraces = maxTraces;
|
|
10
|
+
this.startTime = new Date();
|
|
11
|
+
this.healthMonitor = new HealthMonitor();
|
|
12
|
+
}
|
|
13
|
+
/** Record a completed trace */
|
|
14
|
+
record(trace) {
|
|
15
|
+
try {
|
|
16
|
+
this.traces.push(trace);
|
|
17
|
+
// Trim if over limit (keep most recent)
|
|
18
|
+
if (this.traces.length > this.maxTraces) {
|
|
19
|
+
this.traces = this.traces.slice(-this.maxTraces);
|
|
20
|
+
}
|
|
21
|
+
// Update health monitor with step results
|
|
22
|
+
for (const step of trace.steps) {
|
|
23
|
+
this.healthMonitor.recordStep(step);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch (_) {
|
|
27
|
+
// never crash
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** Get full analytics report */
|
|
31
|
+
getReport() {
|
|
32
|
+
const endpoints = this.getEndpointStats();
|
|
33
|
+
const totalRequests = this.traces.length;
|
|
34
|
+
const totalErrors = this.traces.filter(t => t.classification === 'ERROR' || t.classification === 'CRITICAL').length;
|
|
35
|
+
const totalSlow = this.traces.filter(t => t.classification === 'WARN').length;
|
|
36
|
+
const uptime = Date.now() - this.startTime.getTime();
|
|
37
|
+
return {
|
|
38
|
+
totalRequests,
|
|
39
|
+
totalErrors,
|
|
40
|
+
totalSlow,
|
|
41
|
+
uptime,
|
|
42
|
+
endpoints,
|
|
43
|
+
serviceHealth: this.healthMonitor.getAllHealth(),
|
|
44
|
+
topFailures: this.getTopFailures(),
|
|
45
|
+
recentTraces: this.traces.slice(-20).reverse(),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** Get stats for a specific endpoint */
|
|
49
|
+
getEndpointReport(path) {
|
|
50
|
+
const stats = this.getEndpointStats();
|
|
51
|
+
return stats.find(s => s.path === path) || null;
|
|
52
|
+
}
|
|
53
|
+
/** Get aggregated stats per endpoint */
|
|
54
|
+
getEndpointStats() {
|
|
55
|
+
const grouped = new Map();
|
|
56
|
+
for (const trace of this.traces) {
|
|
57
|
+
const key = `${trace.method}:${trace.endpoint}`;
|
|
58
|
+
if (!grouped.has(key))
|
|
59
|
+
grouped.set(key, []);
|
|
60
|
+
grouped.get(key).push(trace);
|
|
61
|
+
}
|
|
62
|
+
const stats = [];
|
|
63
|
+
for (const [key, traces] of grouped) {
|
|
64
|
+
const [method, path] = key.split(':');
|
|
65
|
+
const durations = traces.map(t => t.totalDuration).sort((a, b) => a - b);
|
|
66
|
+
const errorCount = traces.filter(t => t.classification === 'ERROR' || t.classification === 'CRITICAL').length;
|
|
67
|
+
const slowCount = traces.filter(t => t.classification === 'WARN').length;
|
|
68
|
+
// Common issues
|
|
69
|
+
const issues = new Map();
|
|
70
|
+
for (const t of traces) {
|
|
71
|
+
if (t.rootCause) {
|
|
72
|
+
const key = t.rootCause.cause;
|
|
73
|
+
issues.set(key, (issues.get(key) || 0) + 1);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const commonIssues = [...issues.entries()]
|
|
77
|
+
.sort((a, b) => b[1] - a[1])
|
|
78
|
+
.slice(0, 5)
|
|
79
|
+
.map(([issue, count]) => `${issue} (${count}x)`);
|
|
80
|
+
// Service failure breakdown
|
|
81
|
+
const serviceFailures = this.getServiceFailures(traces);
|
|
82
|
+
stats.push({
|
|
83
|
+
path,
|
|
84
|
+
method,
|
|
85
|
+
totalRequests: traces.length,
|
|
86
|
+
errorCount,
|
|
87
|
+
slowCount,
|
|
88
|
+
avgDuration: durations.reduce((a, b) => a + b, 0) / durations.length,
|
|
89
|
+
p95Duration: durations[Math.floor(durations.length * 0.95)] || 0,
|
|
90
|
+
maxDuration: durations[durations.length - 1] || 0,
|
|
91
|
+
commonIssues,
|
|
92
|
+
serviceFailures,
|
|
93
|
+
recentTraces: traces.slice(-5).reverse(),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return stats.sort((a, b) => b.totalRequests - a.totalRequests);
|
|
97
|
+
}
|
|
98
|
+
/** Get service failure breakdown across all traces */
|
|
99
|
+
getTopFailures() {
|
|
100
|
+
return this.getServiceFailures(this.traces);
|
|
101
|
+
}
|
|
102
|
+
/** Calculate service failure stats from a set of traces */
|
|
103
|
+
getServiceFailures(traces) {
|
|
104
|
+
const failures = new Map();
|
|
105
|
+
let totalFailures = 0;
|
|
106
|
+
for (const trace of traces) {
|
|
107
|
+
for (const step of trace.steps) {
|
|
108
|
+
if (step.status === 'error' || step.status === 'timeout') {
|
|
109
|
+
failures.set(step.service, (failures.get(step.service) || 0) + 1);
|
|
110
|
+
totalFailures++;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return [...failures.entries()]
|
|
115
|
+
.map(([service, count]) => ({
|
|
116
|
+
service,
|
|
117
|
+
count,
|
|
118
|
+
percentage: totalFailures > 0 ? Math.round((count / totalFailures) * 100) : 0,
|
|
119
|
+
}))
|
|
120
|
+
.sort((a, b) => b.count - a.count);
|
|
121
|
+
}
|
|
122
|
+
/** Clear all stored traces */
|
|
123
|
+
clear() {
|
|
124
|
+
this.traces = [];
|
|
125
|
+
}
|
|
126
|
+
/** Get raw trace count */
|
|
127
|
+
getTraceCount() {
|
|
128
|
+
return this.traces.length;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Search traces by traceId, endpoint, or error message.
|
|
132
|
+
* Returns matching traces sorted by most recent first.
|
|
133
|
+
*/
|
|
134
|
+
searchTraces(query, options) {
|
|
135
|
+
if (!query)
|
|
136
|
+
return [];
|
|
137
|
+
const lowerQuery = query.toLowerCase();
|
|
138
|
+
const limit = options?.limit || 50;
|
|
139
|
+
const matches = this.traces.filter(trace => {
|
|
140
|
+
// Filter by environment if specified
|
|
141
|
+
if (options?.env && trace.environment !== options.env) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
// Match traceId
|
|
145
|
+
if (trace.traceId.toLowerCase().includes(lowerQuery)) {
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
// Match endpoint
|
|
149
|
+
if (trace.endpoint.toLowerCase().includes(lowerQuery)) {
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
// Match error message in steps
|
|
153
|
+
for (const step of trace.steps) {
|
|
154
|
+
if (step.error && step.error.toLowerCase().includes(lowerQuery)) {
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Match root cause
|
|
159
|
+
if (trace.rootCause && trace.rootCause.cause.toLowerCase().includes(lowerQuery)) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
return false;
|
|
163
|
+
});
|
|
164
|
+
return matches.slice(-limit).reverse();
|
|
165
|
+
}
|
|
166
|
+
getHealthMonitor() {
|
|
167
|
+
return this.healthMonitor;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=Analytics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Analytics.js","sourceRoot":"","sources":["../../../src/core/Analytics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// flow-debugger — Classifier
|
|
3
|
+
// Classifies steps and traces into severity levels
|
|
4
|
+
// ─────────────────────────────────────────────────────────────
|
|
5
|
+
/**
|
|
6
|
+
* Classify a single step based on its duration and status.
|
|
7
|
+
*
|
|
8
|
+
* INFO → normal, <slowThreshold ms
|
|
9
|
+
* WARN → slow (>slowThreshold ms)
|
|
10
|
+
* ERROR → step failed
|
|
11
|
+
* CRITICAL → dependency down / timeout
|
|
12
|
+
*/
|
|
13
|
+
export function classify(duration, status, config) {
|
|
14
|
+
const threshold = config.slowThreshold ?? 300;
|
|
15
|
+
if (status === 'timeout')
|
|
16
|
+
return 'CRITICAL';
|
|
17
|
+
if (status === 'error')
|
|
18
|
+
return 'ERROR';
|
|
19
|
+
if (duration > threshold)
|
|
20
|
+
return 'WARN';
|
|
21
|
+
return 'INFO';
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Classify an entire trace based on its steps.
|
|
25
|
+
* The trace gets the highest severity from any of its steps.
|
|
26
|
+
*/
|
|
27
|
+
export function classifyTrace(steps, _totalDuration, config) {
|
|
28
|
+
const threshold = config.slowThreshold ?? 300;
|
|
29
|
+
let maxLevel = 'INFO';
|
|
30
|
+
const priority = {
|
|
31
|
+
INFO: 0,
|
|
32
|
+
WARN: 1,
|
|
33
|
+
ERROR: 2,
|
|
34
|
+
CRITICAL: 3,
|
|
35
|
+
};
|
|
36
|
+
for (const step of steps) {
|
|
37
|
+
if (priority[step.classification] > priority[maxLevel]) {
|
|
38
|
+
maxLevel = step.classification;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// Also check total duration
|
|
42
|
+
if (maxLevel === 'INFO' && _totalDuration > threshold) {
|
|
43
|
+
maxLevel = 'WARN';
|
|
44
|
+
}
|
|
45
|
+
return maxLevel;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Classify a database query specifically — used by integrations
|
|
49
|
+
* to detect slow queries with a dedicated threshold.
|
|
50
|
+
*/
|
|
51
|
+
export function classifyQuery(duration, status, config) {
|
|
52
|
+
const threshold = config.slowQueryThreshold ?? 300;
|
|
53
|
+
if (status === 'timeout')
|
|
54
|
+
return 'CRITICAL';
|
|
55
|
+
if (status === 'error')
|
|
56
|
+
return 'ERROR';
|
|
57
|
+
if (duration > threshold)
|
|
58
|
+
return 'WARN';
|
|
59
|
+
return 'INFO';
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=Classifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Classifier.js","sourceRoot":"","sources":["../../../src/core/Classifier.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,6BAA6B;AAC7B,mDAAmD;AACnD,gEAAgE;AAShE;;;;;;;GAOG;AACH,MAAM,UAAU,QAAQ,CACpB,QAAgB,EAChB,MAAkB,EAClB,MAA6C;IAE7C,MAAM,SAAS,GAAG,MAAM,CAAC,aAAa,IAAI,GAAG,CAAC;IAE9C,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,UAAU,CAAC;IAC5C,IAAI,MAAM,KAAK,OAAO;QAAE,OAAO,OAAO,CAAC;IACvC,IAAI,QAAQ,GAAG,SAAS;QAAE,OAAO,MAAM,CAAC;IACxC,OAAO,MAAM,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CACzB,KAAkB,EAClB,cAAsB,EACtB,MAA6C;IAE7C,MAAM,SAAS,GAAG,MAAM,CAAC,aAAa,IAAI,GAAG,CAAC;IAC9C,IAAI,QAAQ,GAAwB,MAAM,CAAC;IAE3C,MAAM,QAAQ,GAAwC;QAClD,IAAI,EAAE,CAAC;QACP,IAAI,EAAE,CAAC;QACP,KAAK,EAAE,CAAC;QACR,QAAQ,EAAE,CAAC;KACd,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,IAAI,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrD,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC;QACnC,CAAC;IACL,CAAC;IAED,4BAA4B;IAC5B,IAAI,QAAQ,KAAK,MAAM,IAAI,cAAc,GAAG,SAAS,EAAE,CAAC;QACpD,QAAQ,GAAG,MAAM,CAAC;IACtB,CAAC;IAED,OAAO,QAAQ,CAAC;AACpB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CACzB,QAAgB,EAChB,MAAkB,EAClB,MAAkD;IAElD,MAAM,SAAS,GAAG,MAAM,CAAC,kBAAkB,IAAI,GAAG,CAAC;IAEnD,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,UAAU,CAAC;IAC5C,IAAI,MAAM,KAAK,OAAO;QAAE,OAAO,OAAO,CAAC;IACvC,IAAI,QAAQ,GAAG,SAAS;QAAE,OAAO,MAAM,CAAC;IACxC,OAAO,MAAM,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// flow-debugger — Health Monitor
|
|
3
|
+
// Tracks dependency health from step results
|
|
4
|
+
// ─────────────────────────────────────────────────────────────
|
|
5
|
+
export class HealthMonitor {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.records = new Map();
|
|
8
|
+
}
|
|
9
|
+
/** Record a step result to update health tracking */
|
|
10
|
+
recordStep(step) {
|
|
11
|
+
try {
|
|
12
|
+
const key = step.service;
|
|
13
|
+
if (key === 'internal' || key === 'unknown')
|
|
14
|
+
return;
|
|
15
|
+
if (!this.records.has(key)) {
|
|
16
|
+
this.records.set(key, {
|
|
17
|
+
service: step.service,
|
|
18
|
+
name: key,
|
|
19
|
+
successes: 0,
|
|
20
|
+
failures: 0,
|
|
21
|
+
lastCheck: new Date(),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
const record = this.records.get(key);
|
|
25
|
+
record.lastCheck = new Date();
|
|
26
|
+
if (step.status === 'success') {
|
|
27
|
+
record.successes++;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
record.failures++;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch (_) {
|
|
34
|
+
// never crash
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/** Get health status for a specific service */
|
|
38
|
+
getHealth(service) {
|
|
39
|
+
const record = this.records.get(service);
|
|
40
|
+
if (!record)
|
|
41
|
+
return null;
|
|
42
|
+
return this.toHealthStatus(record);
|
|
43
|
+
}
|
|
44
|
+
/** Get health status for all tracked services */
|
|
45
|
+
getAllHealth() {
|
|
46
|
+
return [...this.records.values()].map(r => this.toHealthStatus(r));
|
|
47
|
+
}
|
|
48
|
+
toHealthStatus(record) {
|
|
49
|
+
const total = record.successes + record.failures;
|
|
50
|
+
const successRate = total > 0 ? record.successes / total : 1;
|
|
51
|
+
let status;
|
|
52
|
+
if (successRate >= 0.95) {
|
|
53
|
+
status = 'healthy';
|
|
54
|
+
}
|
|
55
|
+
else if (successRate >= 0.7) {
|
|
56
|
+
status = 'degraded';
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
status = 'down';
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
service: record.service,
|
|
63
|
+
name: record.name,
|
|
64
|
+
status,
|
|
65
|
+
lastCheck: record.lastCheck,
|
|
66
|
+
successRate: Math.round(successRate * 100),
|
|
67
|
+
totalChecks: total,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/** Reset all health records */
|
|
71
|
+
reset() {
|
|
72
|
+
this.records.clear();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=HealthMonitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HealthMonitor.js","sourceRoot":"","sources":["../../../src/core/HealthMonitor.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,iCAAiC;AACjC,6CAA6C;AAC7C,gEAAgE;AAiBhE,MAAM,OAAO,aAAa;IAA1B;QACY,YAAO,GAAG,IAAI,GAAG,EAAwB,CAAC;IAsEtD,CAAC;IApEG,qDAAqD;IACrD,UAAU,CAAC,IAAe;QACtB,IAAI,CAAC;YACD,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC;YACzB,IAAI,GAAG,KAAK,UAAU,IAAI,GAAG,KAAK,SAAS;gBAAE,OAAO;YAEpD,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBACzB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE;oBAClB,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,IAAI,EAAE,GAAG;oBACT,SAAS,EAAE,CAAC;oBACZ,QAAQ,EAAE,CAAC;oBACX,SAAS,EAAE,IAAI,IAAI,EAAE;iBACxB,CAAC,CAAC;YACP,CAAC;YAED,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC;YACtC,MAAM,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;YAE9B,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBAC5B,MAAM,CAAC,SAAS,EAAE,CAAC;YACvB,CAAC;iBAAM,CAAC;gBACJ,MAAM,CAAC,QAAQ,EAAE,CAAC;YACtB,CAAC;QACL,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,cAAc;QAClB,CAAC;IACL,CAAC;IAED,+CAA+C;IAC/C,SAAS,CAAC,OAAmB;QACzB,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACzC,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACzB,OAAO,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IAED,iDAAiD;IACjD,YAAY;QACR,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,CAAC;IAEO,cAAc,CAAC,MAAoB;QACvC,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC;QACjD,MAAM,WAAW,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAE7D,IAAI,MAAmB,CAAC;QACxB,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;YACtB,MAAM,GAAG,SAAS,CAAC;QACvB,CAAC;aAAM,IAAI,WAAW,IAAI,GAAG,EAAE,CAAC;YAC5B,MAAM,GAAG,UAAU,CAAC;QACxB,CAAC;aAAM,CAAC;YACJ,MAAM,GAAG,MAAM,CAAC;QACpB,CAAC;QAED,OAAO;YACH,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,MAAM;YACN,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,GAAG,CAAC;YAC1C,WAAW,EAAE,KAAK;SACrB,CAAC;IACN,CAAC;IAED,+BAA+B;IAC/B,KAAK;QACD,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC;CACJ"}
|