What each telemetry event means, column by column: the one place the
Worker's writer (events.ts) and the reader (nix run .#events,
tools/events/) both take it from. Plain data with no imports, so Node
runs this file as it is.
Every event goes to one Workers Analytics Engine dataset (the EVENTS binding, wrangler.json), which is a single table of positional columns:
index1 the event's name ('jev.relay'): what a query filters on, and
the key Analytics Engine samples by, so a busy event is
thinned without touching a rare one (weigh rows by
_sample_interval when counting or averaging)
blob1…20 strings, per event below; null when not known
double1 which of the event's doubles are known: bit (k - 2) is set
when double k holds a value, since a double cannot be null
(bitAnd(toUInt32(double1), 1) > 0 for double2)
double2…20 numbers, per event below; 0 when not known (see double1)
A column's meaning is fixed once written: old rows keep it for the dataset's three months. So a field is only ever added, at the next free column; never renamed onto another column, moved, or reused. A new meaning is a new field (or a new event). events.test.ts checks every event's columns run from blob1 and double2 with no gap or repeat.
What an event may hold: timing, sizes, counts, statuses and bounded labels. Never an address, a visitor or rate-limit key, a request body, or a header as sent (worker/CLAUDE.md, "Logs carry no visitor"). A blob is a label to group by, not a place for text.
30export const EVENTS = { 31 'jev.relay': { 32 about: 'One per call to the Jev relay (relay.ts), with the same fields as its jev.relay log line.', 33 blobs: { 34 outcome: { 35 column: 'blob1', 36 about: 37 'refused (the relay answered itself: method, key, rate limit, size or shape), forwarded (TypeSafe answered, whatever its status), cached (the same request was answered within the hour; TypeSafe not asked) or unreachable (TypeSafe could not be reached)', 38 }, 39 colo: { column: 'blob2', about: 'the Cloudflare data centre that ran the call' }, 40 }, 41 doubles: { 42 status: { column: 'double2', about: 'the HTTP status the page got' }, 43 upstreamMs: { 44 column: 'double3', 45 about: "ms until TypeSafe's response headers; unknown when TypeSafe was not asked (refused, cached)", 46 }, 47 requestBytes: { column: 'double4', about: 'the body as forwarded; unknown when refused before reading it' }, 48 sectionInS: { 49 column: 'double5', 50 about: 'seconds before its section began that the page sent the call (negative once begun); unknown outside a song', 51 }, 52 attempt: { column: 'double6', about: "the page's attempt: 0, then its retries" }, 53 }, 54 }, 55 'jev.budget.exhausted': { 56 about: 57 "One per signed-in user and UTC day, on the relay call that spent the last of that user's daily Jev budget (budget.ts). The user is not recorded; counting these by day is how many listeners ran out.", 58 blobs: { 59 colo: { column: 'blob1', about: 'the Cloudflare data centre that ran the call' }, 60 }, 61 doubles: { 62 limit: { column: 'double2', about: 'the daily budget that was spent (JEV_DAILY_PER_USER at the time)' }, 63 secondsToReset: { column: 'double3', about: 'seconds from then until the budget resets at UTC midnight' }, 64 }, 65 }, 66 'jev.screen': { 67 about: 68 "One per attempt to screen a listener's item before it is public (content-jobs.ts): Jev's verdict, or why it is still pending. The item and its author are not recorded.", 69 blobs: { 70 kind: { column: 'blob1', about: 'what was screened: revision (a song), cover (its alt text), comment or pitch' }, 71 outcome: { column: 'blob2', about: "fine (now public), spam or abuse (held), or pending (not screened this time; see why)" }, 72 why: { 73 column: 'blob3', 74 about: 75 "for pending: budget (the author's daily Jev budget is spent), budget-unavailable, unreachable (TypeSafe, or no key), upstream (TypeSafe answered an error) or unanswered (no verdict in the answer); null otherwise", 76 }, 77 }, 78 doubles: { 79 status: { column: 'double2', about: "TypeSafe's HTTP status; unknown when it was not asked or not reached" }, 80 upstreamMs: { column: 'double3', about: "ms until TypeSafe's response headers; unknown when not asked" }, 81 attempt: { column: 'double4', about: 'which attempt this was: 0, then each retry' }, 82 confidence: { column: 'double5', about: "Jev's confidence in the verdict, 0 to 1; unknown when pending or not given" }, 83 requestBytes: { column: 'double6', about: 'the screening request as sent; unknown when not sent' }, 84 }, 85 }, 86 'jev.score': { 87 about: 88 "One per attempt to score a listener's song revision with the art critic (content-jobs.ts), after it was screened fine. The song and its author are not recorded.", 89 blobs: { 90 outcome: { column: 'blob1', about: 'scored, or pending (not scored this time; see why)' }, 91 why: { column: 'blob2', about: "for pending, as jev.screen's why; null when scored" }, 92 }, 93 doubles: { 94 status: { column: 'double2', about: "the worst HTTP status of the runs' TypeSafe calls; unknown when not asked or not reached" }, 95 upstreamMs: { column: 'double3', about: 'ms until the slowest run had its response headers; unknown when not asked' }, 96 attempt: { column: 'double4', about: 'which attempt this was: 0, then each retry' }, 97 art: { column: 'double5', about: 'the mean art of the runs, 0 to 1; unknown when pending' }, 98 runs: { column: 'double6', about: 'how many runs were asked (and charged)' }, 99 }, 100 }, 101} as const;