@growth-labs/monitoring 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.
- package/CHANGELOG.md +9 -0
- package/README.md +115 -0
- package/SPEC.md +19 -0
- package/dist/alerting/dedup.d.ts +6 -0
- package/dist/alerting/dedup.d.ts.map +1 -0
- package/dist/alerting/dedup.js +49 -0
- package/dist/alerting/dedup.js.map +1 -0
- package/dist/alerting/escalation.d.ts +4 -0
- package/dist/alerting/escalation.d.ts.map +1 -0
- package/dist/alerting/escalation.js +26 -0
- package/dist/alerting/escalation.js.map +1 -0
- package/dist/alerting/index.d.ts +31 -0
- package/dist/alerting/index.d.ts.map +1 -0
- package/dist/alerting/index.js +50 -0
- package/dist/alerting/index.js.map +1 -0
- package/dist/alerting/thresholds.d.ts +8 -0
- package/dist/alerting/thresholds.d.ts.map +1 -0
- package/dist/alerting/thresholds.js +105 -0
- package/dist/alerting/thresholds.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/prober/index.d.ts +19 -0
- package/dist/prober/index.d.ts.map +1 -0
- package/dist/prober/index.js +48 -0
- package/dist/prober/index.js.map +1 -0
- package/dist/prober/persist.d.ts +4 -0
- package/dist/prober/persist.d.ts.map +1 -0
- package/dist/prober/persist.js +21 -0
- package/dist/prober/persist.js.map +1 -0
- package/dist/prober/runners/get-runner.d.ts +4 -0
- package/dist/prober/runners/get-runner.d.ts.map +1 -0
- package/dist/prober/runners/get-runner.js +43 -0
- package/dist/prober/runners/get-runner.js.map +1 -0
- package/dist/prober/runners/happy-path-runner.d.ts +13 -0
- package/dist/prober/runners/happy-path-runner.d.ts.map +1 -0
- package/dist/prober/runners/happy-path-runner.js +183 -0
- package/dist/prober/runners/happy-path-runner.js.map +1 -0
- package/dist/prober/runners/post-runner.d.ts +4 -0
- package/dist/prober/runners/post-runner.d.ts.map +1 -0
- package/dist/prober/runners/post-runner.js +44 -0
- package/dist/prober/runners/post-runner.js.map +1 -0
- package/dist/prober/surfaces.d.ts +46 -0
- package/dist/prober/surfaces.d.ts.map +1 -0
- package/dist/prober/surfaces.js +2 -0
- package/dist/prober/surfaces.js.map +1 -0
- package/dist/schemas/drizzle/schema.d.ts +519 -0
- package/dist/schemas/drizzle/schema.d.ts.map +1 -0
- package/dist/schemas/drizzle/schema.js +45 -0
- package/dist/schemas/drizzle/schema.js.map +1 -0
- package/dist/schemas/index.d.ts +2 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +2 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/migrations/0001_uptime_checks.sql +12 -0
- package/dist/schemas/migrations/0002_uptime_incidents.sql +12 -0
- package/dist/schemas/migrations/0003_errors.sql +16 -0
- package/dist/schemas/migrations/README.md +15 -0
- package/dist/status-page/app.d.ts +10 -0
- package/dist/status-page/app.d.ts.map +1 -0
- package/dist/status-page/app.js +44 -0
- package/dist/status-page/app.js.map +1 -0
- package/dist/status-page/lib/queries.d.ts +6 -0
- package/dist/status-page/lib/queries.d.ts.map +1 -0
- package/dist/status-page/lib/queries.js +81 -0
- package/dist/status-page/lib/queries.js.map +1 -0
- package/dist/status-page/pages/api/status.json.d.ts +3 -0
- package/dist/status-page/pages/api/status.json.d.ts.map +1 -0
- package/dist/status-page/pages/api/status.json.js +19 -0
- package/dist/status-page/pages/api/status.json.js.map +1 -0
- package/dist/status-page/shell.d.ts +3 -0
- package/dist/status-page/shell.d.ts.map +1 -0
- package/dist/status-page/shell.js +18 -0
- package/dist/status-page/shell.js.map +1 -0
- package/dist/tail/categorize.d.ts +44 -0
- package/dist/tail/categorize.d.ts.map +1 -0
- package/dist/tail/categorize.js +113 -0
- package/dist/tail/categorize.js.map +1 -0
- package/dist/tail/fingerprint.d.ts +4 -0
- package/dist/tail/fingerprint.d.ts.map +1 -0
- package/dist/tail/fingerprint.js +18 -0
- package/dist/tail/fingerprint.js.map +1 -0
- package/dist/tail/index.d.ts +21 -0
- package/dist/tail/index.d.ts.map +1 -0
- package/dist/tail/index.js +50 -0
- package/dist/tail/index.js.map +1 -0
- package/dist/tail/persist.d.ts +15 -0
- package/dist/tail/persist.d.ts.map +1 -0
- package/dist/tail/persist.js +63 -0
- package/dist/tail/persist.js.map +1 -0
- package/dist/tail/redact.d.ts +5 -0
- package/dist/tail/redact.d.ts.map +1 -0
- package/dist/tail/redact.js +25 -0
- package/dist/tail/redact.js.map +1 -0
- package/dist/tail/sample.d.ts +9 -0
- package/dist/tail/sample.d.ts.map +1 -0
- package/dist/tail/sample.js +25 -0
- package/dist/tail/sample.js.map +1 -0
- package/dist/types.d.ts +87 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +11 -0
- package/dist/types.js.map +1 -0
- package/package.json +85 -0
- package/src/schemas/migrations/0001_uptime_checks.sql +12 -0
- package/src/schemas/migrations/0002_uptime_incidents.sql +12 -0
- package/src/schemas/migrations/0003_errors.sql +16 -0
- package/src/schemas/migrations/README.md +15 -0
- package/src/status-page/README.md +14 -0
- package/src/status-page/app.ts +58 -0
- package/src/status-page/components/ErrorRollup.astro +26 -0
- package/src/status-page/components/IncidentList.astro +25 -0
- package/src/status-page/components/SurfaceRow.astro +24 -0
- package/src/status-page/lib/queries.ts +114 -0
- package/src/status-page/pages/api/status.json.ts +24 -0
- package/src/status-page/pages/index.astro +45 -0
- package/src/status-page/shell.ts +17 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export function createStatusPageApp(config) {
|
|
2
|
+
const routePath = config.routePath ?? '/';
|
|
3
|
+
const jsonRoutePath = config.jsonRoutePath ?? '/api/status.json';
|
|
4
|
+
const serializable = {
|
|
5
|
+
realm: config.realm,
|
|
6
|
+
d1Binding: config.d1Binding ?? 'MONITORING_DB',
|
|
7
|
+
surfaces: config.surfaces,
|
|
8
|
+
};
|
|
9
|
+
return {
|
|
10
|
+
name: '@growth-labs/monitoring/status-page',
|
|
11
|
+
hooks: {
|
|
12
|
+
'astro:config:setup': ({ injectRoute, updateConfig }) => {
|
|
13
|
+
updateConfig({
|
|
14
|
+
vite: {
|
|
15
|
+
plugins: [
|
|
16
|
+
{
|
|
17
|
+
name: '@growth-labs/monitoring:status-page-config',
|
|
18
|
+
resolveId(id) {
|
|
19
|
+
if (id === 'virtual:growth-labs/monitoring/status-page/config')
|
|
20
|
+
return id;
|
|
21
|
+
},
|
|
22
|
+
load(id) {
|
|
23
|
+
if (id === 'virtual:growth-labs/monitoring/status-page/config') {
|
|
24
|
+
return `export const config = ${JSON.stringify(serializable)}`;
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
injectRoute({
|
|
32
|
+
pattern: routePath,
|
|
33
|
+
entrypoint: '@growth-labs/monitoring/status-page/pages/index',
|
|
34
|
+
});
|
|
35
|
+
injectRoute({
|
|
36
|
+
pattern: jsonRoutePath,
|
|
37
|
+
entrypoint: '@growth-labs/monitoring/status-page/pages/api/status.json',
|
|
38
|
+
});
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export { getCurrentSurfaceStatuses, getOpenIncidents, getSurfaceUptime, getTopErrors, } from './lib/queries.js';
|
|
44
|
+
//# sourceMappingURL=app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/status-page/app.ts"],"names":[],"mappings":"AAQA,MAAM,UAAU,mBAAmB,CAAC,MAA2B;IAC9D,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,GAAG,CAAA;IACzC,MAAM,aAAa,GAAG,MAAM,CAAC,aAAa,IAAI,kBAAkB,CAAA;IAChE,MAAM,YAAY,GAAG;QACpB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,eAAe;QAC9C,QAAQ,EAAE,MAAM,CAAC,QAAQ;KACzB,CAAA;IAED,OAAO;QACN,IAAI,EAAE,qCAAqC;QAC3C,KAAK,EAAE;YACN,oBAAoB,EAAE,CAAC,EAAE,WAAW,EAAE,YAAY,EAAE,EAAE,EAAE;gBACvD,YAAY,CAAC;oBACZ,IAAI,EAAE;wBACL,OAAO,EAAE;4BACR;gCACC,IAAI,EAAE,4CAA4C;gCAClD,SAAS,CAAC,EAAU;oCACnB,IAAI,EAAE,KAAK,mDAAmD;wCAAE,OAAO,EAAE,CAAA;gCAC1E,CAAC;gCACD,IAAI,CAAC,EAAU;oCACd,IAAI,EAAE,KAAK,mDAAmD,EAAE,CAAC;wCAChE,OAAO,yBAAyB,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,CAAA;oCAC/D,CAAC;gCACF,CAAC;6BACD;yBACD;qBACD;iBACD,CAAC,CAAA;gBACF,WAAW,CAAC;oBACX,OAAO,EAAE,SAAS;oBAClB,UAAU,EAAE,iDAAiD;iBAC7D,CAAC,CAAA;gBACF,WAAW,CAAC;oBACX,OAAO,EAAE,aAAa;oBACtB,UAAU,EAAE,2DAA2D;iBACvE,CAAC,CAAA;YACH,CAAC;SACD;KACD,CAAA;AACF,CAAC;AAGD,OAAO,EACN,yBAAyB,EACzB,gBAAgB,EAChB,gBAAgB,EAChB,YAAY,GACZ,MAAM,kBAAkB,CAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ErrorRollup, StatusPageSurfaceConfig, SurfaceStatus, UptimeIncident } from '../../types.js';
|
|
2
|
+
export declare function getCurrentSurfaceStatuses(db: D1Database, surfaces: StatusPageSurfaceConfig[]): Promise<SurfaceStatus[]>;
|
|
3
|
+
export declare function getOpenIncidents(db: D1Database): Promise<UptimeIncident[]>;
|
|
4
|
+
export declare function getTopErrors(db: D1Database, hoursBack: number): Promise<ErrorRollup[]>;
|
|
5
|
+
export declare function getSurfaceUptime(db: D1Database, surface: string, daysBack: number): Promise<number>;
|
|
6
|
+
//# sourceMappingURL=queries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../../src/status-page/lib/queries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,WAAW,EACX,uBAAuB,EACvB,aAAa,EACb,cAAc,EACd,MAAM,gBAAgB,CAAA;AAiBvB,wBAAsB,yBAAyB,CAC9C,EAAE,EAAE,UAAU,EACd,QAAQ,EAAE,uBAAuB,EAAE,GACjC,OAAO,CAAC,aAAa,EAAE,CAAC,CAwB1B;AAED,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAUhF;AAED,wBAAsB,YAAY,CAAC,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAoB5F;AAED,wBAAsB,gBAAgB,CACrC,EAAE,EAAE,UAAU,EACd,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CAcjB"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export async function getCurrentSurfaceStatuses(db, surfaces) {
|
|
2
|
+
const openIncidents = await getOpenIncidents(db);
|
|
3
|
+
return Promise.all(surfaces.map(async (surface) => {
|
|
4
|
+
const lastCheck = await db
|
|
5
|
+
.prepare(`
|
|
6
|
+
SELECT id, surface, status, checked_at
|
|
7
|
+
FROM gl_uptime_checks
|
|
8
|
+
WHERE surface = ?
|
|
9
|
+
ORDER BY checked_at DESC
|
|
10
|
+
LIMIT 1
|
|
11
|
+
`)
|
|
12
|
+
.bind(surface.name)
|
|
13
|
+
.first();
|
|
14
|
+
const incident = openIncidents.find((row) => row.surface === surface.name);
|
|
15
|
+
const uptime7d = await getSurfaceUptime(db, surface.name, 7);
|
|
16
|
+
return {
|
|
17
|
+
name: surface.name,
|
|
18
|
+
status: statusFor(lastCheck ?? null, incident),
|
|
19
|
+
lastCheckedAt: lastCheck?.checked_at ?? null,
|
|
20
|
+
uptime7d,
|
|
21
|
+
};
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
export async function getOpenIncidents(db) {
|
|
25
|
+
const { results } = await db
|
|
26
|
+
.prepare(`
|
|
27
|
+
SELECT id, surface, opened_at, closed_at, trigger_check_id, resolve_check_id, severity, notes
|
|
28
|
+
FROM gl_uptime_incidents
|
|
29
|
+
WHERE closed_at IS NULL
|
|
30
|
+
ORDER BY opened_at DESC
|
|
31
|
+
`)
|
|
32
|
+
.all();
|
|
33
|
+
return results ?? [];
|
|
34
|
+
}
|
|
35
|
+
export async function getTopErrors(db, hoursBack) {
|
|
36
|
+
const since = Math.floor(Date.now() / 1000) - hoursBack * 60 * 60;
|
|
37
|
+
const { results } = await db
|
|
38
|
+
.prepare(`
|
|
39
|
+
SELECT fingerprint, surface, message, COUNT(*) AS count, MAX(occurred_at) AS occurred_at
|
|
40
|
+
FROM gl_errors
|
|
41
|
+
WHERE occurred_at >= ?
|
|
42
|
+
GROUP BY fingerprint
|
|
43
|
+
ORDER BY count DESC, occurred_at DESC
|
|
44
|
+
LIMIT 5
|
|
45
|
+
`)
|
|
46
|
+
.bind(since)
|
|
47
|
+
.all();
|
|
48
|
+
return (results ?? []).map((row) => ({
|
|
49
|
+
fingerprint: row.fingerprint,
|
|
50
|
+
surface: row.surface,
|
|
51
|
+
message: row.message,
|
|
52
|
+
count: Number(row.count),
|
|
53
|
+
lastOccurredAt: Number(row.occurred_at),
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
export async function getSurfaceUptime(db, surface, daysBack) {
|
|
57
|
+
const since = Math.floor(Date.now() / 1000) - daysBack * 24 * 60 * 60;
|
|
58
|
+
const { results } = await db
|
|
59
|
+
.prepare(`
|
|
60
|
+
SELECT id, surface, status, checked_at
|
|
61
|
+
FROM gl_uptime_checks
|
|
62
|
+
WHERE surface = ? AND checked_at >= ?
|
|
63
|
+
ORDER BY checked_at DESC
|
|
64
|
+
`)
|
|
65
|
+
.bind(surface, since)
|
|
66
|
+
.all();
|
|
67
|
+
const rows = results ?? [];
|
|
68
|
+
if (rows.length === 0)
|
|
69
|
+
return 1;
|
|
70
|
+
return rows.filter((row) => row.status === 'pass').length / rows.length;
|
|
71
|
+
}
|
|
72
|
+
function statusFor(check, incident) {
|
|
73
|
+
if (incident?.severity === 'critical')
|
|
74
|
+
return 'red';
|
|
75
|
+
if (incident?.severity === 'warning')
|
|
76
|
+
return 'yellow';
|
|
77
|
+
if (!check)
|
|
78
|
+
return 'yellow';
|
|
79
|
+
return check.status === 'pass' ? 'green' : 'yellow';
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=queries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queries.js","sourceRoot":"","sources":["../../../src/status-page/lib/queries.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.json.d.ts","sourceRoot":"","sources":["../../../../src/status-page/pages/api/status.json.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAGrC,eAAO,MAAM,GAAG,EAAE,QAmBjB,CAAA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { config } from 'virtual:growth-labs/monitoring/status-page/config';
|
|
2
|
+
import { getCurrentSurfaceStatuses, getOpenIncidents, getTopErrors } from '../../lib/queries.js';
|
|
3
|
+
export const GET = async ({ locals }) => {
|
|
4
|
+
const env = locals.runtime?.env ?? {};
|
|
5
|
+
const db = env[config.d1Binding];
|
|
6
|
+
const [surfaces, openIncidents, topErrors] = await Promise.all([
|
|
7
|
+
getCurrentSurfaceStatuses(db, config.surfaces),
|
|
8
|
+
getOpenIncidents(db),
|
|
9
|
+
getTopErrors(db, 24),
|
|
10
|
+
]);
|
|
11
|
+
return new Response(JSON.stringify({
|
|
12
|
+
realm: config.realm,
|
|
13
|
+
generatedAt: Math.floor(Date.now() / 1000),
|
|
14
|
+
surfaces,
|
|
15
|
+
openIncidents,
|
|
16
|
+
topErrors,
|
|
17
|
+
}), { headers: { 'Content-Type': 'application/json' } });
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=status.json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.json.js","sourceRoot":"","sources":["../../../../src/status-page/pages/api/status.json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mDAAmD,CAAA;AAE1E,OAAO,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAEhG,MAAM,CAAC,MAAM,GAAG,GAAa,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;IACjD,MAAM,GAAG,GAAI,MAA0D,CAAC,OAAO,EAAE,GAAG,IAAI,EAAE,CAAA;IAC1F,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,SAAS,CAAe,CAAA;IAC9C,MAAM,CAAC,QAAQ,EAAE,aAAa,EAAE,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC9D,yBAAyB,CAAC,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC;QAC9C,gBAAgB,CAAC,EAAE,CAAC;QACpB,YAAY,CAAC,EAAE,EAAE,EAAE,CAAC;KACpB,CAAC,CAAA;IAEF,OAAO,IAAI,QAAQ,CAClB,IAAI,CAAC,SAAS,CAAC;QACd,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;QAC1C,QAAQ;QACR,aAAa;QACb,SAAS;KACT,CAAC,EACF,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,CACnD,CAAA;AACF,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../../src/status-page/shell.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAC3B,gBAAgB,EAAE,MAAM,GAAG,IAAI,EAC/B,UAAU,SAAgC,GACxC,MAAM,CASR;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function relativeTime(timestampSeconds, nowSeconds = Math.floor(Date.now() / 1000)) {
|
|
2
|
+
if (!timestampSeconds)
|
|
3
|
+
return 'never';
|
|
4
|
+
const diff = Math.max(0, nowSeconds - timestampSeconds);
|
|
5
|
+
if (diff < 60)
|
|
6
|
+
return `${diff}s ago`;
|
|
7
|
+
const minutes = Math.floor(diff / 60);
|
|
8
|
+
if (minutes < 60)
|
|
9
|
+
return `${minutes}m ago`;
|
|
10
|
+
const hours = Math.floor(minutes / 60);
|
|
11
|
+
if (hours < 24)
|
|
12
|
+
return `${hours}h ago`;
|
|
13
|
+
return `${Math.floor(hours / 24)}d ago`;
|
|
14
|
+
}
|
|
15
|
+
export function formatUptime(value) {
|
|
16
|
+
return `${(value * 100).toFixed(2)}%`;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=shell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell.js","sourceRoot":"","sources":["../../src/status-page/shell.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,YAAY,CAC3B,gBAA+B,EAC/B,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;IAE1C,IAAI,CAAC,gBAAgB;QAAE,OAAO,OAAO,CAAA;IACrC,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,GAAG,gBAAgB,CAAC,CAAA;IACvD,IAAI,IAAI,GAAG,EAAE;QAAE,OAAO,GAAG,IAAI,OAAO,CAAA;IACpC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,EAAE,CAAC,CAAA;IACrC,IAAI,OAAO,GAAG,EAAE;QAAE,OAAO,GAAG,OAAO,OAAO,CAAA;IAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAA;IACtC,IAAI,KAAK,GAAG,EAAE;QAAE,OAAO,GAAG,KAAK,OAAO,CAAA;IACtC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,CAAC,OAAO,CAAA;AACxC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAa;IACzC,OAAO,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAA;AACtC,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export type ErrorCategory = 'exception' | 'fivexx' | 'console-error' | 'console-warn' | 'slow-request';
|
|
2
|
+
export interface CategorizedEvent {
|
|
3
|
+
category: ErrorCategory;
|
|
4
|
+
surface: string;
|
|
5
|
+
message: string;
|
|
6
|
+
stack?: string;
|
|
7
|
+
statusCode?: number;
|
|
8
|
+
durationMs?: number;
|
|
9
|
+
requestId?: string;
|
|
10
|
+
occurredAt: number;
|
|
11
|
+
fingerprint?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface TraceItem {
|
|
14
|
+
scriptName?: string;
|
|
15
|
+
outcome?: string;
|
|
16
|
+
exceptions?: Array<{
|
|
17
|
+
name?: string;
|
|
18
|
+
message?: string;
|
|
19
|
+
stack?: string;
|
|
20
|
+
}>;
|
|
21
|
+
logs?: Array<{
|
|
22
|
+
level?: string;
|
|
23
|
+
message?: unknown;
|
|
24
|
+
args?: unknown[];
|
|
25
|
+
}>;
|
|
26
|
+
request?: {
|
|
27
|
+
method?: string;
|
|
28
|
+
url?: string;
|
|
29
|
+
headers?: Record<string, string>;
|
|
30
|
+
};
|
|
31
|
+
response?: {
|
|
32
|
+
status?: number;
|
|
33
|
+
};
|
|
34
|
+
eventTimestamp?: number | string;
|
|
35
|
+
wallTime?: number;
|
|
36
|
+
dispatchNamespace?: string;
|
|
37
|
+
}
|
|
38
|
+
interface CategorizeOptions {
|
|
39
|
+
slowRequestThresholdMs?: number;
|
|
40
|
+
}
|
|
41
|
+
export declare function categorize(traceEvent: TraceItem, options?: CategorizeOptions): CategorizedEvent[];
|
|
42
|
+
export declare function normalizePath(pathname: string): string;
|
|
43
|
+
export {};
|
|
44
|
+
//# sourceMappingURL=categorize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"categorize.d.ts","sourceRoot":"","sources":["../../src/tail/categorize.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GACtB,WAAW,GACX,QAAQ,GACR,eAAe,GACf,cAAc,GACd,cAAc,CAAA;AAEjB,MAAM,WAAW,gBAAgB;IAChC,QAAQ,EAAE,aAAa,CAAA;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,SAAS;IACzB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACvE,IAAI,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAA;KAAE,CAAC,CAAA;IACrE,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAA;IAC7E,QAAQ,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC9B,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED,UAAU,iBAAiB;IAC1B,sBAAsB,CAAC,EAAE,MAAM,CAAA;CAC/B;AAED,wBAAgB,UAAU,CACzB,UAAU,EAAE,SAAS,EACrB,OAAO,GAAE,iBAAsB,GAC7B,gBAAgB,EAAE,CAkEpB;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAStD"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
export function categorize(traceEvent, options = {}) {
|
|
2
|
+
const occurredAt = parseTimestamp(traceEvent.eventTimestamp);
|
|
3
|
+
const surface = surfaceForTrace(traceEvent);
|
|
4
|
+
const requestId = traceEvent.request?.headers?.['cf-ray'] ?? traceEvent.request?.headers?.['CF-Ray'];
|
|
5
|
+
const durationMs = typeof traceEvent.wallTime === 'number' ? traceEvent.wallTime : undefined;
|
|
6
|
+
const events = [];
|
|
7
|
+
for (const exception of traceEvent.exceptions ?? []) {
|
|
8
|
+
const prefix = exception.name ? `${exception.name}: ` : '';
|
|
9
|
+
events.push({
|
|
10
|
+
category: 'exception',
|
|
11
|
+
surface,
|
|
12
|
+
message: `${prefix}${exception.message ?? 'Unhandled exception'}`,
|
|
13
|
+
stack: exception.stack,
|
|
14
|
+
requestId,
|
|
15
|
+
durationMs,
|
|
16
|
+
occurredAt,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
const statusCode = traceEvent.response?.status;
|
|
20
|
+
if (typeof statusCode === 'number' && statusCode >= 500) {
|
|
21
|
+
events.push({
|
|
22
|
+
category: 'fivexx',
|
|
23
|
+
surface,
|
|
24
|
+
message: `HTTP ${statusCode} response`,
|
|
25
|
+
statusCode,
|
|
26
|
+
requestId,
|
|
27
|
+
durationMs,
|
|
28
|
+
occurredAt,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
for (const log of traceEvent.logs ?? []) {
|
|
32
|
+
if (log.level !== 'error' && log.level !== 'warn')
|
|
33
|
+
continue;
|
|
34
|
+
events.push({
|
|
35
|
+
category: log.level === 'error' ? 'console-error' : 'console-warn',
|
|
36
|
+
surface,
|
|
37
|
+
message: logMessage(log),
|
|
38
|
+
statusCode,
|
|
39
|
+
requestId,
|
|
40
|
+
durationMs,
|
|
41
|
+
occurredAt,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
const slowThreshold = options.slowRequestThresholdMs;
|
|
45
|
+
if (events.length === 0 &&
|
|
46
|
+
typeof durationMs === 'number' &&
|
|
47
|
+
typeof slowThreshold === 'number' &&
|
|
48
|
+
durationMs > slowThreshold) {
|
|
49
|
+
events.push({
|
|
50
|
+
category: 'slow-request',
|
|
51
|
+
surface,
|
|
52
|
+
message: `Slow request took ${durationMs}ms`,
|
|
53
|
+
statusCode,
|
|
54
|
+
requestId,
|
|
55
|
+
durationMs,
|
|
56
|
+
occurredAt,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return events;
|
|
60
|
+
}
|
|
61
|
+
export function normalizePath(pathname) {
|
|
62
|
+
return ('/' +
|
|
63
|
+
pathname
|
|
64
|
+
.split('/')
|
|
65
|
+
.filter(Boolean)
|
|
66
|
+
.map((segment) => (/^[A-Za-z]+$/.test(segment) ? segment : '[slug]'))
|
|
67
|
+
.join('/'));
|
|
68
|
+
}
|
|
69
|
+
function surfaceForTrace(traceEvent) {
|
|
70
|
+
const scriptName = traceEvent.scriptName ?? traceEvent.dispatchNamespace ?? 'unknown';
|
|
71
|
+
const method = traceEvent.request?.method ?? 'GET';
|
|
72
|
+
const url = traceEvent.request?.url ?? 'https://unknown/';
|
|
73
|
+
let pathname = '/';
|
|
74
|
+
try {
|
|
75
|
+
pathname = new URL(url).pathname;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
pathname = url.split('?')[0] || '/';
|
|
79
|
+
}
|
|
80
|
+
return `${scriptName}:${method} ${normalizePath(pathname)}`;
|
|
81
|
+
}
|
|
82
|
+
function parseTimestamp(value) {
|
|
83
|
+
if (typeof value === 'number')
|
|
84
|
+
return value < 10_000_000_000 ? value * 1000 : value;
|
|
85
|
+
if (typeof value === 'string') {
|
|
86
|
+
const numeric = Number(value);
|
|
87
|
+
if (Number.isFinite(numeric))
|
|
88
|
+
return parseTimestamp(numeric);
|
|
89
|
+
const parsed = Date.parse(value);
|
|
90
|
+
if (Number.isFinite(parsed))
|
|
91
|
+
return parsed;
|
|
92
|
+
}
|
|
93
|
+
return Date.now();
|
|
94
|
+
}
|
|
95
|
+
function logMessage(log) {
|
|
96
|
+
const value = log.message ?? log.args ?? '';
|
|
97
|
+
if (Array.isArray(value))
|
|
98
|
+
return value.map(formatLogPart).join(' ');
|
|
99
|
+
return formatLogPart(value);
|
|
100
|
+
}
|
|
101
|
+
function formatLogPart(value) {
|
|
102
|
+
if (typeof value === 'string')
|
|
103
|
+
return value;
|
|
104
|
+
if (value instanceof Error)
|
|
105
|
+
return value.message;
|
|
106
|
+
try {
|
|
107
|
+
return JSON.stringify(value);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return String(value);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=categorize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"categorize.js","sourceRoot":"","sources":["../../src/tail/categorize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fingerprint.d.ts","sourceRoot":"","sources":["../../src/tail/fingerprint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAIvD,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CASjF;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAKxD"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const encoder = new TextEncoder();
|
|
2
|
+
export async function computeFingerprint(event) {
|
|
3
|
+
const firstStackFrame = event.stack?.split('\n')[1]?.trim() ?? '';
|
|
4
|
+
const normalizedMessage = normalizeMessage(event.message);
|
|
5
|
+
const input = `${event.category}|${normalizedMessage}|${firstStackFrame}`;
|
|
6
|
+
const digest = await crypto.subtle.digest('SHA-256', encoder.encode(input));
|
|
7
|
+
return [...new Uint8Array(digest)]
|
|
8
|
+
.map((byte) => byte.toString(16).padStart(2, '0'))
|
|
9
|
+
.join('')
|
|
10
|
+
.slice(0, 16);
|
|
11
|
+
}
|
|
12
|
+
export function normalizeMessage(message) {
|
|
13
|
+
return message
|
|
14
|
+
.replace(/\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi, '[uuid]')
|
|
15
|
+
.replace(/\/[a-z0-9_-]+(?=\b)/gi, '/[seg]')
|
|
16
|
+
.replace(/\b\d{4,}\b/g, '[n]');
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=fingerprint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fingerprint.js","sourceRoot":"","sources":["../../src/tail/fingerprint.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAA;AAEjC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,KAAuB;IAC/D,MAAM,eAAe,GAAG,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;IACjE,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACzD,MAAM,KAAK,GAAG,GAAG,KAAK,CAAC,QAAQ,IAAI,iBAAiB,IAAI,eAAe,EAAE,CAAA;IACzE,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;IAC3E,OAAO,CAAC,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;SAChC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SACjD,IAAI,CAAC,EAAE,CAAC;SACR,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;AACf,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC/C,OAAO,OAAO;SACZ,OAAO,CAAC,oEAAoE,EAAE,QAAQ,CAAC;SACvF,OAAO,CAAC,uBAAuB,EAAE,QAAQ,CAAC;SAC1C,OAAO,CAAC,aAAa,EAAE,KAAK,CAAC,CAAA;AAChC,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type NotifyConfig, type SamplingConfig, type TailAlertingConfig } from '../types.js';
|
|
2
|
+
import { type TraceItem } from './categorize.js';
|
|
3
|
+
export interface TailWorkerConfig {
|
|
4
|
+
realmId: string;
|
|
5
|
+
d1Binding: string;
|
|
6
|
+
waeBinding: string;
|
|
7
|
+
notifyConfig: NotifyConfig;
|
|
8
|
+
alertingConfig?: TailAlertingConfig;
|
|
9
|
+
sampling: SamplingConfig;
|
|
10
|
+
}
|
|
11
|
+
export declare function createTailWorker(config: TailWorkerConfig): {
|
|
12
|
+
tail: (events: TraceItem[], env: Record<string, unknown>) => Promise<void>;
|
|
13
|
+
tailHandler: (events: TraceItem[], env: Record<string, unknown>) => Promise<void>;
|
|
14
|
+
};
|
|
15
|
+
export type { CategorizedEvent, ErrorCategory, TraceItem } from './categorize.js';
|
|
16
|
+
export { categorize, normalizePath } from './categorize.js';
|
|
17
|
+
export { computeFingerprint, normalizeMessage } from './fingerprint.js';
|
|
18
|
+
export { persistErrorEvent, severityFromCategory } from './persist.js';
|
|
19
|
+
export { redact, redactString, redactSurface } from './redact.js';
|
|
20
|
+
export { shouldKeep } from './sample.js';
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tail/index.ts"],"names":[],"mappings":"AACA,OAAO,EAEN,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAc,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAM5D,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,YAAY,CAAA;IAC1B,cAAc,CAAC,EAAE,kBAAkB,CAAA;IACnC,QAAQ,EAAE,cAAc,CAAA;CACxB;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB;mBAMrB,SAAS,EAAE,OAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,OAAO,CAAC,IAAI,CAAC;0BAAzD,SAAS,EAAE,OAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,OAAO,CAAC,IAAI,CAAC;EAmC5F;AAED,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AACjF,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC3D,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AACvE,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACtE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createAlerter } from '../alerting/index.js';
|
|
2
|
+
import { errorMessage, } from '../types.js';
|
|
3
|
+
import { categorize } from './categorize.js';
|
|
4
|
+
import { computeFingerprint } from './fingerprint.js';
|
|
5
|
+
import { persistErrorEvent } from './persist.js';
|
|
6
|
+
import { redact } from './redact.js';
|
|
7
|
+
import { shouldKeep } from './sample.js';
|
|
8
|
+
export function createTailWorker(config) {
|
|
9
|
+
const alerter = createAlerter({
|
|
10
|
+
notifyConfig: config.notifyConfig,
|
|
11
|
+
alertingConfig: config.alertingConfig,
|
|
12
|
+
});
|
|
13
|
+
async function tailHandler(events, env) {
|
|
14
|
+
const db = env[config.d1Binding];
|
|
15
|
+
const wae = env[config.waeBinding];
|
|
16
|
+
if (!db) {
|
|
17
|
+
console.error(`[monitoring] missing D1 binding env.${config.d1Binding}`);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
for (const traceEvent of events) {
|
|
21
|
+
for (const rawEvent of categorize(traceEvent, {
|
|
22
|
+
slowRequestThresholdMs: config.sampling.slowRequestThresholdMs,
|
|
23
|
+
})) {
|
|
24
|
+
try {
|
|
25
|
+
const fingerprint = await computeFingerprint(rawEvent);
|
|
26
|
+
const withFingerprint = { ...rawEvent, fingerprint };
|
|
27
|
+
if (!shouldKeep(withFingerprint.category, config.sampling, {
|
|
28
|
+
surface: withFingerprint.surface,
|
|
29
|
+
activeIncidentSurfaces: alerter.activeIncidentSurfaces,
|
|
30
|
+
})) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const redacted = redact(withFingerprint);
|
|
34
|
+
await persistErrorEvent(db, wae, { realmId: config.realmId }, redacted);
|
|
35
|
+
await alerter.handleErrorEvent(withFingerprint, db, env);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
console.error(`[monitoring] failed to process tail event: ${errorMessage(error)}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return { tail: tailHandler, tailHandler };
|
|
44
|
+
}
|
|
45
|
+
export { categorize, normalizePath } from './categorize.js';
|
|
46
|
+
export { computeFingerprint, normalizeMessage } from './fingerprint.js';
|
|
47
|
+
export { persistErrorEvent, severityFromCategory } from './persist.js';
|
|
48
|
+
export { redact, redactString, redactSurface } from './redact.js';
|
|
49
|
+
export { shouldKeep } from './sample.js';
|
|
50
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tail/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EACN,YAAY,GAIZ,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,UAAU,EAAkB,MAAM,iBAAiB,CAAA;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAWxC,MAAM,UAAU,gBAAgB,CAAC,MAAwB;IACxD,MAAM,OAAO,GAAG,aAAa,CAAC;QAC7B,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,cAAc,EAAE,MAAM,CAAC,cAAc;KACrC,CAAC,CAAA;IAEF,KAAK,UAAU,WAAW,CAAC,MAAmB,EAAE,GAA4B;QAC3E,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,SAAS,CAA2B,CAAA;QAC1D,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,UAAU,CAErB,CAAA;QACZ,IAAI,CAAC,EAAE,EAAE,CAAC;YACT,OAAO,CAAC,KAAK,CAAC,uCAAuC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAA;YACxE,OAAM;QACP,CAAC;QACD,KAAK,MAAM,UAAU,IAAI,MAAM,EAAE,CAAC;YACjC,KAAK,MAAM,QAAQ,IAAI,UAAU,CAAC,UAAU,EAAE;gBAC7C,sBAAsB,EAAE,MAAM,CAAC,QAAQ,CAAC,sBAAsB;aAC9D,CAAC,EAAE,CAAC;gBACJ,IAAI,CAAC;oBACJ,MAAM,WAAW,GAAG,MAAM,kBAAkB,CAAC,QAAQ,CAAC,CAAA;oBACtD,MAAM,eAAe,GAAG,EAAE,GAAG,QAAQ,EAAE,WAAW,EAAE,CAAA;oBACpD,IACC,CAAC,UAAU,CAAC,eAAe,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE;wBACtD,OAAO,EAAE,eAAe,CAAC,OAAO;wBAChC,sBAAsB,EAAE,OAAO,CAAC,sBAAsB;qBACtD,CAAC,EACD,CAAC;wBACF,SAAQ;oBACT,CAAC;oBACD,MAAM,QAAQ,GAAG,MAAM,CAAC,eAAe,CAA2B,CAAA;oBAClE,MAAM,iBAAiB,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAA;oBACvE,MAAM,OAAO,CAAC,gBAAgB,CAAC,eAAe,EAAE,EAAE,EAAE,GAAG,CAAC,CAAA;gBACzD,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBAChB,OAAO,CAAC,KAAK,CAAC,8CAA8C,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;gBACnF,CAAC;YACF,CAAC;QACF,CAAC;IACF,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,CAAA;AAC1C,CAAC;AAGD,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC3D,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AACvE,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACtE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ErrorSeverity, type IdFactory } from '../types.js';
|
|
2
|
+
import type { CategorizedEvent, ErrorCategory } from './categorize.js';
|
|
3
|
+
interface PersistConfig {
|
|
4
|
+
realmId: string;
|
|
5
|
+
}
|
|
6
|
+
interface AnalyticsEngineBinding {
|
|
7
|
+
writeDataPoint(dataPoint: unknown): Promise<unknown> | unknown;
|
|
8
|
+
}
|
|
9
|
+
export interface FingerprintedEvent extends CategorizedEvent {
|
|
10
|
+
fingerprint: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function persistErrorEvent(db: D1Database, wae: AnalyticsEngineBinding | undefined, config: PersistConfig, event: FingerprintedEvent, idFactory?: IdFactory): Promise<void>;
|
|
13
|
+
export declare function severityFromCategory(category: ErrorCategory): ErrorSeverity;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=persist.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persist.d.ts","sourceRoot":"","sources":["../../src/tail/persist.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAA4B,KAAK,SAAS,EAAE,MAAM,aAAa,CAAA;AAC1F,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEtE,UAAU,aAAa;IACtB,OAAO,EAAE,MAAM,CAAA;CACf;AAED,UAAU,sBAAsB;IAC/B,cAAc,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAA;CAC9D;AAED,MAAM,WAAW,kBAAmB,SAAQ,gBAAgB;IAC3D,WAAW,EAAE,MAAM,CAAA;CACnB;AAED,wBAAsB,iBAAiB,CACtC,EAAE,EAAE,UAAU,EACd,GAAG,EAAE,sBAAsB,GAAG,SAAS,EACvC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,kBAAkB,EACzB,SAAS,GAAE,SAAsB,GAC/B,OAAO,CAAC,IAAI,CAAC,CA0Df;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,aAAa,GAAG,aAAa,CAW3E"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { errorMessage, generateId } from '../types.js';
|
|
2
|
+
export async function persistErrorEvent(db, wae, config, event, idFactory = generateId) {
|
|
3
|
+
const severity = severityFromCategory(event.category);
|
|
4
|
+
try {
|
|
5
|
+
await db
|
|
6
|
+
.prepare(`
|
|
7
|
+
INSERT INTO gl_errors
|
|
8
|
+
(id, realm_key, surface, severity, message, stack, request_id, status_code, duration_ms, occurred_at, fingerprint)
|
|
9
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
10
|
+
`)
|
|
11
|
+
.bind(idFactory(), config.realmId, event.surface, severity, event.message, event.stack ?? null, event.requestId ?? null, event.statusCode ?? null, event.durationMs ?? null, Math.floor(event.occurredAt / 1000), event.fingerprint)
|
|
12
|
+
.run();
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
console.error(`[monitoring] failed to persist error event: ${errorMessage(error)}`);
|
|
16
|
+
}
|
|
17
|
+
if (!wae)
|
|
18
|
+
return;
|
|
19
|
+
try {
|
|
20
|
+
await wae.writeDataPoint({
|
|
21
|
+
blobs: [
|
|
22
|
+
severity,
|
|
23
|
+
config.realmId,
|
|
24
|
+
'',
|
|
25
|
+
'',
|
|
26
|
+
event.surface,
|
|
27
|
+
'',
|
|
28
|
+
'',
|
|
29
|
+
'',
|
|
30
|
+
'',
|
|
31
|
+
'',
|
|
32
|
+
'',
|
|
33
|
+
'',
|
|
34
|
+
'',
|
|
35
|
+
'',
|
|
36
|
+
'',
|
|
37
|
+
'',
|
|
38
|
+
'',
|
|
39
|
+
event.category,
|
|
40
|
+
event.fingerprint,
|
|
41
|
+
'',
|
|
42
|
+
],
|
|
43
|
+
doubles: [event.occurredAt, event.statusCode ?? 0, event.durationMs ?? 0],
|
|
44
|
+
indexes: [severity],
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
console.error(`[monitoring] failed to write WAE error event: ${errorMessage(error)}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export function severityFromCategory(category) {
|
|
52
|
+
switch (category) {
|
|
53
|
+
case 'exception':
|
|
54
|
+
return 'exception';
|
|
55
|
+
case 'console-warn':
|
|
56
|
+
case 'slow-request':
|
|
57
|
+
return 'warning';
|
|
58
|
+
case 'fivexx':
|
|
59
|
+
case 'console-error':
|
|
60
|
+
return 'error';
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=persist.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persist.js","sourceRoot":"","sources":["../../src/tail/persist.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,YAAY,EAAE,UAAU,EAAkB,MAAM,aAAa,CAAA;AAe1F,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACtC,EAAc,EACd,GAAuC,EACvC,MAAqB,EACrB,KAAyB,EACzB,YAAuB,UAAU;IAEjC,MAAM,QAAQ,GAAG,oBAAoB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;IACrD,IAAI,CAAC;QACJ,MAAM,EAAE;aACN,OAAO,CAAC;;;;IAIR,CAAC;aACD,IAAI,CACJ,SAAS,EAAE,EACX,MAAM,CAAC,OAAO,EACd,KAAK,CAAC,OAAO,EACb,QAAQ,EACR,KAAK,CAAC,OAAO,EACb,KAAK,CAAC,KAAK,IAAI,IAAI,EACnB,KAAK,CAAC,SAAS,IAAI,IAAI,EACvB,KAAK,CAAC,UAAU,IAAI,IAAI,EACxB,KAAK,CAAC,UAAU,IAAI,IAAI,EACxB,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,EACnC,KAAK,CAAC,WAAW,CACjB;aACA,GAAG,EAAE,CAAA;IACR,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO,CAAC,KAAK,CAAC,+CAA+C,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IACpF,CAAC;IAED,IAAI,CAAC,GAAG;QAAE,OAAM;IAChB,IAAI,CAAC;QACJ,MAAM,GAAG,CAAC,cAAc,CAAC;YACxB,KAAK,EAAE;gBACN,QAAQ;gBACR,MAAM,CAAC,OAAO;gBACd,EAAE;gBACF,EAAE;gBACF,KAAK,CAAC,OAAO;gBACb,EAAE;gBACF,EAAE;gBACF,EAAE;gBACF,EAAE;gBACF,EAAE;gBACF,EAAE;gBACF,EAAE;gBACF,EAAE;gBACF,EAAE;gBACF,EAAE;gBACF,EAAE;gBACF,EAAE;gBACF,KAAK,CAAC,QAAQ;gBACd,KAAK,CAAC,WAAW;gBACjB,EAAE;aACF;YACD,OAAO,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,CAAC,EAAE,KAAK,CAAC,UAAU,IAAI,CAAC,CAAC;YACzE,OAAO,EAAE,CAAC,QAAQ,CAAC;SACnB,CAAC,CAAA;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO,CAAC,KAAK,CAAC,iDAAiD,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IACtF,CAAC;AACF,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,QAAuB;IAC3D,QAAQ,QAAQ,EAAE,CAAC;QAClB,KAAK,WAAW;YACf,OAAO,WAAW,CAAA;QACnB,KAAK,cAAc,CAAC;QACpB,KAAK,cAAc;YAClB,OAAO,SAAS,CAAA;QACjB,KAAK,QAAQ,CAAC;QACd,KAAK,eAAe;YACnB,OAAO,OAAO,CAAA;IAChB,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CategorizedEvent } from './categorize.js';
|
|
2
|
+
export declare function redact(event: CategorizedEvent): CategorizedEvent;
|
|
3
|
+
export declare function redactString(value: string): string;
|
|
4
|
+
export declare function redactSurface(surface: string): string;
|
|
5
|
+
//# sourceMappingURL=redact.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/tail/redact.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAKvD,wBAAgB,MAAM,CAAC,KAAK,EAAE,gBAAgB,GAAG,gBAAgB,CAOhE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOlD;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAErD"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const MAX_MESSAGE_LENGTH = 2000;
|
|
2
|
+
const MAX_STACK_LENGTH = 4000;
|
|
3
|
+
export function redact(event) {
|
|
4
|
+
return {
|
|
5
|
+
...event,
|
|
6
|
+
message: truncate(redactString(event.message), MAX_MESSAGE_LENGTH),
|
|
7
|
+
stack: event.stack ? truncate(redactString(event.stack), MAX_STACK_LENGTH) : undefined,
|
|
8
|
+
surface: redactSurface(event.surface),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function redactString(value) {
|
|
12
|
+
return value
|
|
13
|
+
.replace(/\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\b/g, '[redacted-email]')
|
|
14
|
+
.replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g, '[redacted-jwt]')
|
|
15
|
+
.replace(/\bcfut_[A-Za-z0-9_-]+\b/g, '[redacted-cf-token]')
|
|
16
|
+
.replace(/\bsk-[A-Za-z0-9_-]{20,}\b/g, '[redacted-api-key]')
|
|
17
|
+
.replace(/\b(?=[A-Za-z0-9]{32,}\b)(?=[A-Za-z0-9]*\d)[A-Za-z0-9]+\b/g, '[redacted-token]');
|
|
18
|
+
}
|
|
19
|
+
export function redactSurface(surface) {
|
|
20
|
+
return surface.replace(/\?[^\s]+/, '');
|
|
21
|
+
}
|
|
22
|
+
function truncate(value, maxLength) {
|
|
23
|
+
return value.length > maxLength ? value.slice(0, maxLength) : value;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=redact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.js","sourceRoot":"","sources":["../../src/tail/redact.ts"],"names":[],"mappings":"AAEA,MAAM,kBAAkB,GAAG,IAAI,CAAA;AAC/B,MAAM,gBAAgB,GAAG,IAAI,CAAA;AAE7B,MAAM,UAAU,MAAM,CAAC,KAAuB;IAC7C,OAAO;QACN,GAAG,KAAK;QACR,OAAO,EAAE,QAAQ,CAAC,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,kBAAkB,CAAC;QAClE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,SAAS;QACtF,OAAO,EAAE,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC;KACrC,CAAA;AACF,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAa;IACzC,OAAO,KAAK;SACV,OAAO,CAAC,qDAAqD,EAAE,kBAAkB,CAAC;SAClF,OAAO,CAAC,wDAAwD,EAAE,gBAAgB,CAAC;SACnF,OAAO,CAAC,0BAA0B,EAAE,qBAAqB,CAAC;SAC1D,OAAO,CAAC,4BAA4B,EAAE,oBAAoB,CAAC;SAC3D,OAAO,CAAC,2DAA2D,EAAE,kBAAkB,CAAC,CAAA;AAC3F,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAe;IAC5C,OAAO,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;AACvC,CAAC;AAED,SAAS,QAAQ,CAAC,KAAa,EAAE,SAAiB;IACjD,OAAO,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;AACpE,CAAC"}
|