@tclohm/yoban 1.0.0 → 1.0.2
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/README.MD +84 -12
- package/package.json +1 -1
- package/src/yoban.js +59 -21
package/README.MD
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
# @tclohm/yoban
|
|
2
|
+
> Lightweight, universal middleware for real-time API performance monitoring and SLA alerting.
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
[](https://www.npmjs.com/package/@tclohm/yoban)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
---
|
|
4
8
|
|
|
5
9
|
## Install
|
|
6
|
-
```
|
|
7
|
-
npm install @tclohm/yoban
|
|
10
|
+
```bash
|
|
11
|
+
npm install @tclohm/yoban
|
|
8
12
|
```
|
|
9
13
|
|
|
10
|
-
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
11
17
|
```javascript
|
|
12
|
-
import {
|
|
18
|
+
import { Yoban } from '@tclohm/yoban';
|
|
13
19
|
|
|
14
|
-
const
|
|
20
|
+
const yoban = new Yoban({
|
|
15
21
|
service: "payments",
|
|
16
|
-
flushInterval: 10000,
|
|
17
|
-
violationThreshold: 0.5,
|
|
22
|
+
flushInterval: 10000,
|
|
23
|
+
violationThreshold: 0.5,
|
|
18
24
|
sla: { premium: 400, standard: 800 },
|
|
19
25
|
notify: {
|
|
20
26
|
slack: process.env.SLACK_WEBHOOK,
|
|
@@ -24,20 +30,86 @@ const metrics = new yoban({
|
|
|
24
30
|
tier: req.user?.tier ?? 'standard'
|
|
25
31
|
})
|
|
26
32
|
});
|
|
33
|
+
```
|
|
27
34
|
|
|
28
|
-
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Framework Support
|
|
38
|
+
|
|
39
|
+
### Express
|
|
40
|
+
```javascript
|
|
41
|
+
app.use(yoban.middleware());
|
|
29
42
|
```
|
|
30
43
|
|
|
44
|
+
### Fastify
|
|
45
|
+
```javascript
|
|
46
|
+
fastify.addHook('onRequest', yoban.fastify().onRequest);
|
|
47
|
+
fastify.addHook('onResponse', yoban.fastify().onResponse);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Koa
|
|
51
|
+
```javascript
|
|
52
|
+
app.use(yoban.koa());
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Hapi
|
|
56
|
+
```javascript
|
|
57
|
+
await server.register(yoban.hapi());
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
31
62
|
## Config
|
|
63
|
+
|
|
32
64
|
| Option | Type | Default | Description |
|
|
33
65
|
|---|---|---|---|
|
|
34
66
|
| `service` | string | required | Name of your service |
|
|
35
67
|
| `flushInterval` | number | 10000 | How often to flush in ms |
|
|
36
|
-
| `violationThreshold` | number | 0.5 | Violation rate to trigger alert |
|
|
68
|
+
| `violationThreshold` | number | 0.5 | Violation rate to trigger alert (0-1) |
|
|
37
69
|
| `sla` | object | — | SLA thresholds per tier in ms |
|
|
38
70
|
| `notify.slack` | string | — | Slack webhook URL |
|
|
39
71
|
| `notify.pagerduty` | string | — | PagerDuty routing key |
|
|
40
|
-
| `enrichEvent` | function | — |
|
|
72
|
+
| `enrichEvent` | function | — | Attach custom metadata per request |
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## How It Works
|
|
77
|
+
```
|
|
78
|
+
Request → Adapter (Express/Fastify/Koa/Hapi)
|
|
79
|
+
→ Buffer
|
|
80
|
+
→ Flush every N seconds
|
|
81
|
+
→ Aggregate (mean, median, violation rate)
|
|
82
|
+
→ Alert if violation rate exceeds threshold
|
|
83
|
+
→ Notify (Slack, PagerDuty)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Roadmap
|
|
89
|
+
|
|
90
|
+
### v1.1
|
|
91
|
+
- [ ] TypeScript support
|
|
92
|
+
- [ ] Config file support (`.yobanrc`)
|
|
93
|
+
|
|
94
|
+
### v1.2
|
|
95
|
+
- [ ] p50 / p90 / p95 percentiles in output
|
|
96
|
+
- [ ] Email notifications via SendGrid
|
|
97
|
+
- [ ] SMS notifications via Twilio
|
|
98
|
+
- [ ] Discord webhook support
|
|
99
|
+
|
|
100
|
+
### v2.0
|
|
101
|
+
- [ ] Persistent storage — keep metrics across restarts
|
|
102
|
+
- [ ] Dashboard UI to visualize metrics and violation rates
|
|
103
|
+
- [ ] Anomaly detection — alert when something is unusually slow
|
|
104
|
+
- [ ] Trend detection — "this endpoint has been getting slower over 24 hours"
|
|
105
|
+
- [ ] Auto-tune SLA thresholds based on historical baselines
|
|
106
|
+
|
|
107
|
+
### v2.1
|
|
108
|
+
- [ ] Export to Datadog, Prometheus, Grafana
|
|
109
|
+
- [ ] Kubernetes health check endpoint
|
|
110
|
+
- [ ] CLI tool to replay and analyze log files
|
|
111
|
+
|
|
112
|
+
---
|
|
41
113
|
|
|
42
114
|
## License
|
|
43
|
-
MIT © tclohm
|
|
115
|
+
MIT © [tclohm](https://github.com/tclohm)
|
package/package.json
CHANGED
package/src/yoban.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { notifySlack } from './notifiers/slack.js';
|
|
2
2
|
import { notifyPagerDuty } from './notifiers/pagerduty.js';
|
|
3
3
|
|
|
4
|
-
export class
|
|
4
|
+
export class Yoban {
|
|
5
5
|
#buffer = [];
|
|
6
6
|
#aggregated = [];
|
|
7
7
|
|
|
@@ -9,25 +9,17 @@ export class yoban {
|
|
|
9
9
|
this.config = config;
|
|
10
10
|
this.#startFlushing();
|
|
11
11
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
status: res.statusCode,
|
|
24
|
-
duration,
|
|
25
|
-
timestamp: Date.now(),
|
|
26
|
-
...extra
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
next();
|
|
30
|
-
};
|
|
12
|
+
// --- Core ---
|
|
13
|
+
#record({ route, method, status, duration, extra = {} }) {
|
|
14
|
+
this.#buffer.push({
|
|
15
|
+
server: this.config.service,
|
|
16
|
+
method,
|
|
17
|
+
route,
|
|
18
|
+
status,
|
|
19
|
+
duration,
|
|
20
|
+
timestamp: Date.now(),
|
|
21
|
+
...extra
|
|
22
|
+
});
|
|
31
23
|
}
|
|
32
24
|
|
|
33
25
|
#startFlushing() {
|
|
@@ -68,7 +60,7 @@ export class yoban {
|
|
|
68
60
|
const violationRate = violations / entry.durations.length;
|
|
69
61
|
|
|
70
62
|
if (violationRate > (this.config.violationThreshold ?? 0.5)) {
|
|
71
|
-
console.log(`[
|
|
63
|
+
console.log(`[ YOBAN ALERT ]: ${entry.service} ${entry.route} (${entry.tier}) violation rate ${(violationRate * 100).toFixed(2)}%`);
|
|
72
64
|
|
|
73
65
|
if (this.config.notify?.slack) {
|
|
74
66
|
await notifySlack(this.config.notify.slack, entry, violationRate);
|
|
@@ -79,4 +71,50 @@ export class yoban {
|
|
|
79
71
|
}
|
|
80
72
|
}
|
|
81
73
|
}
|
|
74
|
+
|
|
75
|
+
// --- Framework Adapters ---
|
|
76
|
+
middleware() {
|
|
77
|
+
return (req, res, next) => {
|
|
78
|
+
const start = Date.now();
|
|
79
|
+
res.on('finish', () => {
|
|
80
|
+
const extra = this.config.enrichEvent ? this.config.enrichEvent(req) : {};
|
|
81
|
+
this.#record({ route: req.route?.path ?? req.path, method: req.method, status: res.statusCode, duration: Date.now() - start, extra });
|
|
82
|
+
});
|
|
83
|
+
next();
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
fastify() {
|
|
88
|
+
return {
|
|
89
|
+
onRequest: (request, reply, done) => { requet.startTime = Date.now(); done(); },
|
|
90
|
+
onResponse: (request, reply, done) => {
|
|
91
|
+
const extra = this.config.enrichEvent ? this.config.enrichEvent(request) : {};
|
|
92
|
+
this.#record({ route: request.routerPath, method: request.method, status: reply.statusCode, duration: Date.now() - request.startTime, extra });
|
|
93
|
+
done();
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
koa() {
|
|
99
|
+
return async (ctx, next) => {
|
|
100
|
+
const start = Date.now();
|
|
101
|
+
await next();
|
|
102
|
+
const extra = this.config.enrichEvent ? this.config.enrichEvent(ctx) : {};
|
|
103
|
+
this.#record({ route: ctx.path , method: ctx.method, status: ctx.status, duration: Date.now() - start, extra });
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
hapi() {
|
|
108
|
+
return {
|
|
109
|
+
name: 'yoban',
|
|
110
|
+
register: (server) => {
|
|
111
|
+
server.ext('onPreResponse', (request, h) => {
|
|
112
|
+
const extra = this.config.enrichEvent > this.config.enrichEvent(request) : {};
|
|
113
|
+
this.#record({ route: request.method, status: request.response.statusCode, duration: Date.now() - request.infor.received, extra });
|
|
114
|
+
return h.continue;
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
82
120
|
}
|