Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Projects have Observability: their requests, logs, traces, metrics and errors, from apps on g1t.page with nothing to set up, and from software anywhere else over OpenTelemetry. | 1 | import assert from "node:assert/strict"; |
| 2 | import test from "node:test"; | |
| 3 | ||
| 4 | import { fingerprint, messageShape, topFrame } from "./errors.ts"; | |
| 5 | import { bucketOf, decode, encode, percentiles, quantile } from "./histogram.ts"; | |
| 6 | import { hexId, nanosToMicros, route, severityNumber, severityOf } from "./shape.ts"; | |
| 7 | ||
| 8 | test("routes lose their ids, keep their words", () => { | |
| 9 | assert.equal(route("/users/42/posts"), "/users/:id/posts"); | |
| 10 | assert.equal(route("/o/3f2504e0-4f89-11d3-9a0c-0305e82c3301?x=1"), "/o/:id"); | |
| 11 | assert.equal(route("/blob/4bf92f3577b34da6a3ce/readme"), "/blob/:id/readme"); | |
| 12 | assert.equal(route("/assets/app.css"), "/assets/app.css"); | |
| 13 | assert.equal(route("/"), "/"); | |
| 14 | assert.equal(route("/settings/profile"), "/settings/profile"); | |
| 15 | }); | |
| 16 | ||
| 17 | test("severities", () => { | |
| 18 | assert.equal(severityOf(severityNumber("warn")), "warn"); | |
| 19 | assert.equal(severityOf(severityNumber("log")), "info"); | |
| 20 | assert.equal(severityOf(severityNumber("CRITICAL")), "fatal"); | |
| 21 | assert.equal(severityOf(0), "info"); | |
| 22 | assert.equal(severityOf(3), "trace"); | |
| 23 | }); | |
| 24 | ||
| 25 | test("ids and times", () => { | |
| 26 | assert.equal(hexId("AAAAAAAAAAAAAAAAAAAAAA==", 32), null); | |
| 27 | assert.equal(hexId("S/kvNXezTaajzpKdDg5HNg==", 32), "4bf92f3577b34da6a3ce929d0e0e4736"); | |
| 28 | assert.equal(hexId("ABCDEF0123456789", 16), "abcdef0123456789"); | |
| 29 | assert.equal(nanosToMicros("1760000000123456789"), 1_760_000_000_123_456); | |
| 30 | assert.equal(nanosToMicros("0"), null); | |
| 31 | }); | |
| 32 | ||
| 33 | test("percentiles come from bucket counts, within a bucket of the truth", () => { | |
| 34 | const counts = decode(""); | |
| 35 | for (let i = 1; i <= 100; i++) counts[bucketOf(i)]++; | |
| 36 | const p = percentiles(counts); | |
| 37 | assert.ok(p.p50! >= 30 && p.p50! <= 75, `p50 ${p.p50}`); | |
| 38 | assert.ok(p.p99! >= 75 && p.p99! <= 100, `p99 ${p.p99}`); | |
| 39 | assert.deepEqual(decode(encode(counts)), counts); | |
| 40 | assert.equal(quantile(decode(""), 0.5), null); | |
| 41 | }); | |
| 42 | ||
| 43 | test("the same error thrown with different ids is one group", async () => { | |
| 44 | const a = await fingerprint("TypeError", "Cannot read 'name' of user 41", "TypeError\n at load (src/user.ts:10:5)", "api"); | |
| 45 | const b = await fingerprint("TypeError", "Cannot read 'email' of user 977", "TypeError\n at load (src/user.ts:12:9)", "api"); | |
| 46 | const c = await fingerprint("TypeError", "Cannot read 'name' of user 41", "TypeError\n at save (src/user.ts:10:5)", "api"); | |
| 47 | assert.equal(a, b); | |
| 48 | assert.notEqual(a, c); | |
| 49 | assert.equal(messageShape('fetch https://x.y/z failed with 503 for "abc"'), 'fetch <url> failed with # for "…"'); | |
| 50 | assert.equal(topFrame("Error\n at f (node_modules/lib/index.js:1:1)\n at g (src/app.ts:4:2)"), "g src/app.ts"); | |
| 51 | }); |