@downtrace/protocol 0.2.1 → 0.4.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/README.md CHANGED
@@ -4,7 +4,7 @@ The ingestion contract between Downtrace agents and the Downtrace cloud, as a JS
4
4
 
5
5
  - `schema/v0/aggregates.schema.json` — the contract. Everything else derives from it.
6
6
  - `schema/v0/fixtures/{valid,invalid}/` — examples every implementation must accept and reject.
7
- - Exports: `PROTOCOL_VERSION`, `AGGREGATES_PATH`, `AGGREGATES_SCHEMA_V0`, `LATENCY_BOUNDARIES_V0`, `LATENCY_BUCKETS_V0`, `latencyBucket()`, `QUERIES_PER_REQUEST_BOUNDARIES_V0`, `QUERIES_PER_REQUEST_BUCKETS_V0`, `queriesPerRequestBucket()`, and the types `AggregatesBatch`, `Interval`, `Endpoint`, `LatencyHistogram`, `PostgresStats`, …
7
+ - Exports: `PROTOCOL_VERSION`, `AGGREGATES_PATH`, `AGGREGATES_SCHEMA_V0`, `LATENCY_BOUNDARIES_V0`, `LATENCY_BUCKETS_V0`, `latencyBucket()`, `CALLS_PER_REQUEST_BOUNDARIES_V0`, `CALLS_PER_REQUEST_BUCKETS_V0`, `callsPerRequestBucket()`, and the types `AggregatesBatch`, `Interval`, `Endpoint`, `LatencyHistogram`, `Dependency`, `PostgresStats`, …
8
8
 
9
9
  ## v0 in one sentence
10
10
 
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@ interface AggregatesBatch {
6
6
  /**
7
7
  * Protocol version this batch conforms to. Every published minor of v0 stays acceptable.
8
8
  */
9
- protocol: "0.1.0" | "0.2.0";
9
+ protocol: "0.1.0" | "0.2.0" | "0.3.0" | "0.4.0" | "0.5.0";
10
10
  agent: AgentInfo;
11
11
  instance: InstanceInfo;
12
12
  deploy: DeployInfo;
@@ -47,6 +47,7 @@ interface Interval {
47
47
  * @maxItems 4096
48
48
  */
49
49
  endpoints: Endpoint[];
50
+ runtime?: RuntimeHealth;
50
51
  }
51
52
  interface Endpoint {
52
53
  method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "HEAD" | "OPTIONS" | "OTHER";
@@ -62,6 +63,12 @@ interface Endpoint {
62
63
  status: StatusClasses;
63
64
  latency: LatencyHistogram;
64
65
  postgres?: PostgresStats;
66
+ /**
67
+ * One entry per dependency this route touched. Replaces `postgres` (ADR 0011).
68
+ *
69
+ * @maxItems 64
70
+ */
71
+ dependencies?: Dependency[];
65
72
  }
66
73
  interface StatusClasses {
67
74
  /**
@@ -100,7 +107,7 @@ interface LatencyHistogram {
100
107
  max: number;
101
108
  }
102
109
  /**
103
- * Postgres work done per request on this route during the interval. Optional: agents that do not observe queries omit it.
110
+ * DEPRECATED since 0.4.0, use `dependencies` with kind `postgres` (ADR 0011). Still accepted, and translated at ingest, so agents on 0.2.x and 0.3.x keep working. Will be removed in v1. Its buckets are the same as Dependency's, declared there.
104
111
  */
105
112
  interface PostgresStats {
106
113
  /**
@@ -119,23 +126,101 @@ interface PostgresStats {
119
126
  */
120
127
  max: number;
121
128
  }
129
+ /**
130
+ * What one request did against one dependency, aggregated over the interval for this route. The same shape for every kind, so the cloud stores and compares them uniformly (ADR 0011).
131
+ */
132
+ interface Dependency {
133
+ /**
134
+ * What kind of dependency this is.
135
+ */
136
+ kind: "postgres" | "mysql" | "redis" | "http";
137
+ /**
138
+ * Host for outgoing HTTP (`api.stripe.com`); empty when the dependency is a single one, like the application's own database.
139
+ */
140
+ target: string;
141
+ /**
142
+ * Requests per bucket of calls-per-request; bounds in x-calls-per-request-boundaries, last bucket open-ended.
143
+ *
144
+ * @minItems 8
145
+ * @maxItems 8
146
+ */
147
+ callsPerRequest: [number, number, number, number, number, number, number, number];
148
+ /**
149
+ * Sum of call durations (ms) across every request on this route.
150
+ */
151
+ totalMs: number;
152
+ /**
153
+ * Slowest single call (ms).
154
+ */
155
+ max: number;
156
+ /**
157
+ * Calls that failed.
158
+ */
159
+ errors: number;
160
+ /**
161
+ * Time requests spent waiting to be able to talk to this dependency, on top of the time spent talking to it: a saturated connection pool shows up here and nowhere else. Optional; agents that cannot see it omit it.
162
+ */
163
+ waitMs?: number;
164
+ }
165
+ /**
166
+ * Health of the agent's own process during the interval. Optional: an agent that does not measure it omits it. Per interval, not per route, because it belongs to the process.
167
+ */
168
+ interface RuntimeHealth {
169
+ /**
170
+ * How long the event loop was late, in ms. The signal that says the process itself is the bottleneck.
171
+ */
172
+ eventLoopDelayMs: {
173
+ /**
174
+ * Median delay.
175
+ */
176
+ p50: number;
177
+ /**
178
+ * 99th percentile delay.
179
+ */
180
+ p99: number;
181
+ /**
182
+ * Worst delay.
183
+ */
184
+ max: number;
185
+ };
186
+ /**
187
+ * Total time spent in garbage collection during the interval.
188
+ */
189
+ gcPauseMs: number;
190
+ /**
191
+ * Garbage collections during the interval.
192
+ */
193
+ gcCount: number;
194
+ /**
195
+ * Heap in use at the end of the interval.
196
+ */
197
+ heapUsedMb: number;
198
+ /**
199
+ * Resident set size at the end of the interval.
200
+ */
201
+ rssMb: number;
202
+ /**
203
+ * Peak concurrent in-flight requests during the interval.
204
+ */
205
+ inFlightMax: number;
206
+ }
122
207
  //#endregion
123
208
  //#region src/generated/boundaries.d.ts
124
209
  /** Upper bounds (ms) of the fixed latency buckets; the last bucket is open-ended. */
125
210
  declare const LATENCY_BOUNDARIES_V0: readonly number[];
126
211
  /** Number of buckets: boundaries + 1 open-ended bucket. */
127
212
  declare const LATENCY_BUCKETS_V0 = 35;
128
- /** Upper bounds of the fixed queries-per-request buckets; the last bucket is open-ended. */
129
- declare const QUERIES_PER_REQUEST_BOUNDARIES_V0: readonly number[];
130
- /** Number of queries-per-request buckets: boundaries + 1 open-ended bucket. */
131
- declare const QUERIES_PER_REQUEST_BUCKETS_V0 = 8;
213
+ /** Upper bounds of the fixed calls-per-request buckets; the last bucket is open-ended. */
214
+ declare const CALLS_PER_REQUEST_BOUNDARIES_V0: readonly number[];
215
+ /** Number of calls-per-request buckets: boundaries + 1 open-ended bucket. */
216
+ declare const CALLS_PER_REQUEST_BUCKETS_V0 = 8;
132
217
  //#endregion
133
218
  //#region src/index.d.ts
134
219
  /**
135
220
  * Version of the ingestion protocol this package speaks. Every published minor of v0 stays acceptable to the
136
221
  * cloud, so an agent on an older minor keeps working: fields are only ever added, and always optional (ADR 0008).
137
222
  */
138
- declare const PROTOCOL_VERSION = "0.2.0";
223
+ declare const PROTOCOL_VERSION = "0.5.0";
139
224
  /** Path, relative to the ingest URL, that receives AggregatesBatch payloads. */
140
225
  declare const AGGREGATES_PATH = "/v0/aggregates";
141
226
  /** The JSON Schema (draft 2020-12) for AggregatesBatch, for validators on either side. */
@@ -263,6 +348,9 @@ declare const AGGREGATES_SCHEMA_V0: {
263
348
  $ref: string;
264
349
  };
265
350
  };
351
+ runtime: {
352
+ $ref: string;
353
+ };
266
354
  };
267
355
  };
268
356
  Endpoint: {
@@ -298,6 +386,14 @@ declare const AGGREGATES_SCHEMA_V0: {
298
386
  postgres: {
299
387
  $ref: string;
300
388
  };
389
+ dependencies: {
390
+ type: string;
391
+ description: string;
392
+ maxItems: number;
393
+ items: {
394
+ $ref: string;
395
+ };
396
+ };
301
397
  };
302
398
  };
303
399
  StatusClasses: {
@@ -363,7 +459,6 @@ declare const AGGREGATES_SCHEMA_V0: {
363
459
  type: string;
364
460
  additionalProperties: boolean;
365
461
  required: string[];
366
- "x-queries-per-request-boundaries": number[];
367
462
  properties: {
368
463
  queriesPerRequest: {
369
464
  type: string;
@@ -387,11 +482,117 @@ declare const AGGREGATES_SCHEMA_V0: {
387
482
  };
388
483
  };
389
484
  };
485
+ RuntimeHealth: {
486
+ title: string;
487
+ description: string;
488
+ type: string;
489
+ additionalProperties: boolean;
490
+ required: string[];
491
+ properties: {
492
+ eventLoopDelayMs: {
493
+ type: string;
494
+ additionalProperties: boolean;
495
+ required: string[];
496
+ description: string;
497
+ properties: {
498
+ p50: {
499
+ type: string;
500
+ minimum: number;
501
+ description: string;
502
+ };
503
+ p99: {
504
+ type: string;
505
+ minimum: number;
506
+ description: string;
507
+ };
508
+ max: {
509
+ type: string;
510
+ minimum: number;
511
+ description: string;
512
+ };
513
+ };
514
+ };
515
+ gcPauseMs: {
516
+ type: string;
517
+ minimum: number;
518
+ description: string;
519
+ };
520
+ gcCount: {
521
+ type: string;
522
+ minimum: number;
523
+ description: string;
524
+ };
525
+ heapUsedMb: {
526
+ type: string;
527
+ minimum: number;
528
+ description: string;
529
+ };
530
+ rssMb: {
531
+ type: string;
532
+ minimum: number;
533
+ description: string;
534
+ };
535
+ inFlightMax: {
536
+ type: string;
537
+ minimum: number;
538
+ description: string;
539
+ };
540
+ };
541
+ };
542
+ Dependency: {
543
+ title: string;
544
+ description: string;
545
+ type: string;
546
+ additionalProperties: boolean;
547
+ required: string[];
548
+ "x-calls-per-request-boundaries": number[];
549
+ properties: {
550
+ kind: {
551
+ enum: string[];
552
+ description: string;
553
+ };
554
+ target: {
555
+ type: string;
556
+ maxLength: number;
557
+ description: string;
558
+ };
559
+ callsPerRequest: {
560
+ type: string;
561
+ description: string;
562
+ minItems: number;
563
+ maxItems: number;
564
+ items: {
565
+ type: string;
566
+ minimum: number;
567
+ };
568
+ };
569
+ totalMs: {
570
+ type: string;
571
+ minimum: number;
572
+ description: string;
573
+ };
574
+ max: {
575
+ type: string;
576
+ minimum: number;
577
+ description: string;
578
+ };
579
+ errors: {
580
+ type: string;
581
+ minimum: number;
582
+ description: string;
583
+ };
584
+ waitMs: {
585
+ type: string;
586
+ minimum: number;
587
+ description: string;
588
+ };
589
+ };
590
+ };
390
591
  };
391
592
  };
392
593
  /** Index of the bucket a latency (ms) falls into: first boundary >= latency, else the open-ended last bucket. */
393
594
  declare function latencyBucket(ms: number): number;
394
- /** Index of the bucket a query count falls into: first boundary >= count, else the open-ended last bucket. */
395
- declare function queriesPerRequestBucket(queries: number): number;
595
+ /** Index of the bucket a call count falls into: first boundary >= count, else the open-ended last bucket. */
596
+ declare function callsPerRequestBucket(calls: number): number;
396
597
  //#endregion
397
- export { AGGREGATES_PATH, AGGREGATES_SCHEMA_V0, type AgentInfo, type AggregatesBatch, type DeployInfo, type Endpoint, type InstanceInfo, type Interval, LATENCY_BOUNDARIES_V0, LATENCY_BUCKETS_V0, type LatencyHistogram, PROTOCOL_VERSION, type PostgresStats, QUERIES_PER_REQUEST_BOUNDARIES_V0, QUERIES_PER_REQUEST_BUCKETS_V0, type StatusClasses, latencyBucket, queriesPerRequestBucket };
598
+ export { AGGREGATES_PATH, AGGREGATES_SCHEMA_V0, type AgentInfo, type AggregatesBatch, CALLS_PER_REQUEST_BOUNDARIES_V0, CALLS_PER_REQUEST_BUCKETS_V0, type Dependency, type DeployInfo, type Endpoint, type InstanceInfo, type Interval, LATENCY_BOUNDARIES_V0, LATENCY_BUCKETS_V0, type LatencyHistogram, PROTOCOL_VERSION, type PostgresStats, type RuntimeHealth, type StatusClasses, callsPerRequestBucket, latencyBucket };
package/dist/index.js CHANGED
@@ -16,7 +16,13 @@ var aggregates_schema_default = {
16
16
  properties: {
17
17
  "protocol": {
18
18
  "type": "string",
19
- "enum": ["0.1.0", "0.2.0"],
19
+ "enum": [
20
+ "0.1.0",
21
+ "0.2.0",
22
+ "0.3.0",
23
+ "0.4.0",
24
+ "0.5.0"
25
+ ],
20
26
  "description": "Protocol version this batch conforms to. Every published minor of v0 stays acceptable."
21
27
  },
22
28
  "agent": { "$ref": "#/$defs/AgentInfo" },
@@ -129,7 +135,8 @@ var aggregates_schema_default = {
129
135
  "type": "array",
130
136
  "maxItems": 4096,
131
137
  "items": { "$ref": "#/$defs/Endpoint" }
132
- }
138
+ },
139
+ "runtime": { "$ref": "#/$defs/RuntimeHealth" }
133
140
  }
134
141
  },
135
142
  "Endpoint": {
@@ -172,7 +179,13 @@ var aggregates_schema_default = {
172
179
  },
173
180
  "status": { "$ref": "#/$defs/StatusClasses" },
174
181
  "latency": { "$ref": "#/$defs/LatencyHistogram" },
175
- "postgres": { "$ref": "#/$defs/PostgresStats" }
182
+ "postgres": { "$ref": "#/$defs/PostgresStats" },
183
+ "dependencies": {
184
+ "type": "array",
185
+ "description": "One entry per dependency this route touched. Replaces `postgres` (ADR 0011).",
186
+ "maxItems": 64,
187
+ "items": { "$ref": "#/$defs/Dependency" }
188
+ }
176
189
  }
177
190
  },
178
191
  "StatusClasses": {
@@ -278,7 +291,7 @@ var aggregates_schema_default = {
278
291
  },
279
292
  "PostgresStats": {
280
293
  "title": "PostgresStats",
281
- "description": "Postgres work done per request on this route during the interval. Optional: agents that do not observe queries omit it.",
294
+ "description": "DEPRECATED since 0.4.0, use `dependencies` with kind `postgres` (ADR 0011). Still accepted, and translated at ingest, so agents on 0.2.x and 0.3.x keep working. Will be removed in v1. Its buckets are the same as Dependency's, declared there.",
282
295
  "type": "object",
283
296
  "additionalProperties": false,
284
297
  "required": [
@@ -286,7 +299,111 @@ var aggregates_schema_default = {
286
299
  "totalMs",
287
300
  "max"
288
301
  ],
289
- "x-queries-per-request-boundaries": [
302
+ "properties": {
303
+ "queriesPerRequest": {
304
+ "type": "array",
305
+ "description": "Requests per bucket of queries-per-request; bounds in x-queries-per-request-boundaries, last bucket open-ended.",
306
+ "minItems": 8,
307
+ "maxItems": 8,
308
+ "items": {
309
+ "type": "integer",
310
+ "minimum": 0
311
+ }
312
+ },
313
+ "totalMs": {
314
+ "type": "number",
315
+ "minimum": 0,
316
+ "description": "Sum of query durations (ms) across every request on this route."
317
+ },
318
+ "max": {
319
+ "type": "number",
320
+ "minimum": 0,
321
+ "description": "Slowest single query (ms)."
322
+ }
323
+ }
324
+ },
325
+ "RuntimeHealth": {
326
+ "title": "RuntimeHealth",
327
+ "description": "Health of the agent's own process during the interval. Optional: an agent that does not measure it omits it. Per interval, not per route, because it belongs to the process.",
328
+ "type": "object",
329
+ "additionalProperties": false,
330
+ "required": [
331
+ "eventLoopDelayMs",
332
+ "gcPauseMs",
333
+ "gcCount",
334
+ "heapUsedMb",
335
+ "rssMb",
336
+ "inFlightMax"
337
+ ],
338
+ "properties": {
339
+ "eventLoopDelayMs": {
340
+ "type": "object",
341
+ "additionalProperties": false,
342
+ "required": [
343
+ "p50",
344
+ "p99",
345
+ "max"
346
+ ],
347
+ "description": "How long the event loop was late, in ms. The signal that says the process itself is the bottleneck.",
348
+ "properties": {
349
+ "p50": {
350
+ "type": "number",
351
+ "minimum": 0,
352
+ "description": "Median delay."
353
+ },
354
+ "p99": {
355
+ "type": "number",
356
+ "minimum": 0,
357
+ "description": "99th percentile delay."
358
+ },
359
+ "max": {
360
+ "type": "number",
361
+ "minimum": 0,
362
+ "description": "Worst delay."
363
+ }
364
+ }
365
+ },
366
+ "gcPauseMs": {
367
+ "type": "number",
368
+ "minimum": 0,
369
+ "description": "Total time spent in garbage collection during the interval."
370
+ },
371
+ "gcCount": {
372
+ "type": "integer",
373
+ "minimum": 0,
374
+ "description": "Garbage collections during the interval."
375
+ },
376
+ "heapUsedMb": {
377
+ "type": "number",
378
+ "minimum": 0,
379
+ "description": "Heap in use at the end of the interval."
380
+ },
381
+ "rssMb": {
382
+ "type": "number",
383
+ "minimum": 0,
384
+ "description": "Resident set size at the end of the interval."
385
+ },
386
+ "inFlightMax": {
387
+ "type": "integer",
388
+ "minimum": 0,
389
+ "description": "Peak concurrent in-flight requests during the interval."
390
+ }
391
+ }
392
+ },
393
+ "Dependency": {
394
+ "title": "Dependency",
395
+ "description": "What one request did against one dependency, aggregated over the interval for this route. The same shape for every kind, so the cloud stores and compares them uniformly (ADR 0011).",
396
+ "type": "object",
397
+ "additionalProperties": false,
398
+ "required": [
399
+ "kind",
400
+ "target",
401
+ "callsPerRequest",
402
+ "totalMs",
403
+ "max",
404
+ "errors"
405
+ ],
406
+ "x-calls-per-request-boundaries": [
290
407
  0,
291
408
  1,
292
409
  2,
@@ -296,9 +413,23 @@ var aggregates_schema_default = {
296
413
  50
297
414
  ],
298
415
  "properties": {
299
- "queriesPerRequest": {
416
+ "kind": {
417
+ "enum": [
418
+ "postgres",
419
+ "mysql",
420
+ "redis",
421
+ "http"
422
+ ],
423
+ "description": "What kind of dependency this is."
424
+ },
425
+ "target": {
426
+ "type": "string",
427
+ "maxLength": 256,
428
+ "description": "Host for outgoing HTTP (`api.stripe.com`); empty when the dependency is a single one, like the application's own database."
429
+ },
430
+ "callsPerRequest": {
300
431
  "type": "array",
301
- "description": "Requests per bucket of queries-per-request; bounds in x-queries-per-request-boundaries, last bucket open-ended.",
432
+ "description": "Requests per bucket of calls-per-request; bounds in x-calls-per-request-boundaries, last bucket open-ended.",
302
433
  "minItems": 8,
303
434
  "maxItems": 8,
304
435
  "items": {
@@ -309,12 +440,22 @@ var aggregates_schema_default = {
309
440
  "totalMs": {
310
441
  "type": "number",
311
442
  "minimum": 0,
312
- "description": "Sum of query durations (ms) across every request on this route."
443
+ "description": "Sum of call durations (ms) across every request on this route."
313
444
  },
314
445
  "max": {
315
446
  "type": "number",
316
447
  "minimum": 0,
317
- "description": "Slowest single query (ms)."
448
+ "description": "Slowest single call (ms)."
449
+ },
450
+ "errors": {
451
+ "type": "integer",
452
+ "minimum": 0,
453
+ "description": "Calls that failed."
454
+ },
455
+ "waitMs": {
456
+ "type": "number",
457
+ "minimum": 0,
458
+ "description": "Time requests spent waiting to be able to talk to this dependency, on top of the time spent talking to it: a saturated connection pool shows up here and nowhere else. Optional; agents that cannot see it omit it."
318
459
  }
319
460
  }
320
461
  }
@@ -361,8 +502,8 @@ const LATENCY_BOUNDARIES_V0 = [
361
502
  ];
362
503
  /** Number of buckets: boundaries + 1 open-ended bucket. */
363
504
  const LATENCY_BUCKETS_V0 = 35;
364
- /** Upper bounds of the fixed queries-per-request buckets; the last bucket is open-ended. */
365
- const QUERIES_PER_REQUEST_BOUNDARIES_V0 = [
505
+ /** Upper bounds of the fixed calls-per-request buckets; the last bucket is open-ended. */
506
+ const CALLS_PER_REQUEST_BOUNDARIES_V0 = [
366
507
  0,
367
508
  1,
368
509
  2,
@@ -371,15 +512,15 @@ const QUERIES_PER_REQUEST_BOUNDARIES_V0 = [
371
512
  20,
372
513
  50
373
514
  ];
374
- /** Number of queries-per-request buckets: boundaries + 1 open-ended bucket. */
375
- const QUERIES_PER_REQUEST_BUCKETS_V0 = 8;
515
+ /** Number of calls-per-request buckets: boundaries + 1 open-ended bucket. */
516
+ const CALLS_PER_REQUEST_BUCKETS_V0 = 8;
376
517
  //#endregion
377
518
  //#region src/index.ts
378
519
  /**
379
520
  * Version of the ingestion protocol this package speaks. Every published minor of v0 stays acceptable to the
380
521
  * cloud, so an agent on an older minor keeps working: fields are only ever added, and always optional (ADR 0008).
381
522
  */
382
- const PROTOCOL_VERSION = "0.2.0";
523
+ const PROTOCOL_VERSION = "0.5.0";
383
524
  /** Path, relative to the ingest URL, that receives AggregatesBatch payloads. */
384
525
  const AGGREGATES_PATH = "/v0/aggregates";
385
526
  /** The JSON Schema (draft 2020-12) for AggregatesBatch, for validators on either side. */
@@ -393,14 +534,14 @@ function latencyBucket(ms) {
393
534
  }
394
535
  return n;
395
536
  }
396
- /** Index of the bucket a query count falls into: first boundary >= count, else the open-ended last bucket. */
397
- function queriesPerRequestBucket(queries) {
398
- const n = QUERIES_PER_REQUEST_BOUNDARIES_V0.length;
537
+ /** Index of the bucket a call count falls into: first boundary >= count, else the open-ended last bucket. */
538
+ function callsPerRequestBucket(calls) {
539
+ const n = CALLS_PER_REQUEST_BOUNDARIES_V0.length;
399
540
  for (let i = 0; i < n; i++) {
400
- const bound = QUERIES_PER_REQUEST_BOUNDARIES_V0[i];
401
- if (bound !== void 0 && queries <= bound) return i;
541
+ const bound = CALLS_PER_REQUEST_BOUNDARIES_V0[i];
542
+ if (bound !== void 0 && calls <= bound) return i;
402
543
  }
403
544
  return n;
404
545
  }
405
546
  //#endregion
406
- export { AGGREGATES_PATH, AGGREGATES_SCHEMA_V0, LATENCY_BOUNDARIES_V0, LATENCY_BUCKETS_V0, PROTOCOL_VERSION, QUERIES_PER_REQUEST_BOUNDARIES_V0, QUERIES_PER_REQUEST_BUCKETS_V0, latencyBucket, queriesPerRequestBucket };
547
+ export { AGGREGATES_PATH, AGGREGATES_SCHEMA_V0, CALLS_PER_REQUEST_BOUNDARIES_V0, CALLS_PER_REQUEST_BUCKETS_V0, LATENCY_BOUNDARIES_V0, LATENCY_BUCKETS_V0, PROTOCOL_VERSION, callsPerRequestBucket, latencyBucket };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@downtrace/protocol",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "description": "Downtrace ingestion protocol: JSON Schema, fixtures and generated TypeScript types",
5
5
  "type": "module",
6
6
  "exports": {
@@ -9,7 +9,7 @@
9
9
  "properties": {
10
10
  "protocol": {
11
11
  "type": "string",
12
- "enum": ["0.1.0", "0.2.0"],
12
+ "enum": ["0.1.0", "0.2.0", "0.3.0", "0.4.0", "0.5.0"],
13
13
  "description": "Protocol version this batch conforms to. Every published minor of v0 stays acceptable."
14
14
  },
15
15
  "agent": {
@@ -121,6 +121,9 @@
121
121
  "items": {
122
122
  "$ref": "#/$defs/Endpoint"
123
123
  }
124
+ },
125
+ "runtime": {
126
+ "$ref": "#/$defs/RuntimeHealth"
124
127
  }
125
128
  }
126
129
  },
@@ -156,6 +159,14 @@
156
159
  },
157
160
  "postgres": {
158
161
  "$ref": "#/$defs/PostgresStats"
162
+ },
163
+ "dependencies": {
164
+ "type": "array",
165
+ "description": "One entry per dependency this route touched. Replaces `postgres` (ADR 0011).",
166
+ "maxItems": 64,
167
+ "items": {
168
+ "$ref": "#/$defs/Dependency"
169
+ }
159
170
  }
160
171
  }
161
172
  },
@@ -221,11 +232,10 @@
221
232
  },
222
233
  "PostgresStats": {
223
234
  "title": "PostgresStats",
224
- "description": "Postgres work done per request on this route during the interval. Optional: agents that do not observe queries omit it.",
235
+ "description": "DEPRECATED since 0.4.0, use `dependencies` with kind `postgres` (ADR 0011). Still accepted, and translated at ingest, so agents on 0.2.x and 0.3.x keep working. Will be removed in v1. Its buckets are the same as Dependency's, declared there.",
225
236
  "type": "object",
226
237
  "additionalProperties": false,
227
238
  "required": ["queriesPerRequest", "totalMs", "max"],
228
- "x-queries-per-request-boundaries": [0, 1, 2, 5, 10, 20, 50],
229
239
  "properties": {
230
240
  "queriesPerRequest": {
231
241
  "type": "array",
@@ -248,6 +258,112 @@
248
258
  "description": "Slowest single query (ms)."
249
259
  }
250
260
  }
261
+ },
262
+ "RuntimeHealth": {
263
+ "title": "RuntimeHealth",
264
+ "description": "Health of the agent's own process during the interval. Optional: an agent that does not measure it omits it. Per interval, not per route, because it belongs to the process.",
265
+ "type": "object",
266
+ "additionalProperties": false,
267
+ "required": ["eventLoopDelayMs", "gcPauseMs", "gcCount", "heapUsedMb", "rssMb", "inFlightMax"],
268
+ "properties": {
269
+ "eventLoopDelayMs": {
270
+ "type": "object",
271
+ "additionalProperties": false,
272
+ "required": ["p50", "p99", "max"],
273
+ "description": "How long the event loop was late, in ms. The signal that says the process itself is the bottleneck.",
274
+ "properties": {
275
+ "p50": {
276
+ "type": "number",
277
+ "minimum": 0,
278
+ "description": "Median delay."
279
+ },
280
+ "p99": {
281
+ "type": "number",
282
+ "minimum": 0,
283
+ "description": "99th percentile delay."
284
+ },
285
+ "max": {
286
+ "type": "number",
287
+ "minimum": 0,
288
+ "description": "Worst delay."
289
+ }
290
+ }
291
+ },
292
+ "gcPauseMs": {
293
+ "type": "number",
294
+ "minimum": 0,
295
+ "description": "Total time spent in garbage collection during the interval."
296
+ },
297
+ "gcCount": {
298
+ "type": "integer",
299
+ "minimum": 0,
300
+ "description": "Garbage collections during the interval."
301
+ },
302
+ "heapUsedMb": {
303
+ "type": "number",
304
+ "minimum": 0,
305
+ "description": "Heap in use at the end of the interval."
306
+ },
307
+ "rssMb": {
308
+ "type": "number",
309
+ "minimum": 0,
310
+ "description": "Resident set size at the end of the interval."
311
+ },
312
+ "inFlightMax": {
313
+ "type": "integer",
314
+ "minimum": 0,
315
+ "description": "Peak concurrent in-flight requests during the interval."
316
+ }
317
+ }
318
+ },
319
+ "Dependency": {
320
+ "title": "Dependency",
321
+ "description": "What one request did against one dependency, aggregated over the interval for this route. The same shape for every kind, so the cloud stores and compares them uniformly (ADR 0011).",
322
+ "type": "object",
323
+ "additionalProperties": false,
324
+ "required": ["kind", "target", "callsPerRequest", "totalMs", "max", "errors"],
325
+ "x-calls-per-request-boundaries": [0, 1, 2, 5, 10, 20, 50],
326
+ "properties": {
327
+ "kind": {
328
+ "enum": ["postgres", "mysql", "redis", "http"],
329
+ "description": "What kind of dependency this is."
330
+ },
331
+ "target": {
332
+ "type": "string",
333
+ "maxLength": 256,
334
+ "description": "Host for outgoing HTTP (`api.stripe.com`); empty when the dependency is a single one, like the application's own database."
335
+ },
336
+ "callsPerRequest": {
337
+ "type": "array",
338
+ "description": "Requests per bucket of calls-per-request; bounds in x-calls-per-request-boundaries, last bucket open-ended.",
339
+ "minItems": 8,
340
+ "maxItems": 8,
341
+ "items": {
342
+ "type": "integer",
343
+ "minimum": 0
344
+ }
345
+ },
346
+ "totalMs": {
347
+ "type": "number",
348
+ "minimum": 0,
349
+ "description": "Sum of call durations (ms) across every request on this route."
350
+ },
351
+ "max": {
352
+ "type": "number",
353
+ "minimum": 0,
354
+ "description": "Slowest single call (ms)."
355
+ },
356
+ "errors": {
357
+ "type": "integer",
358
+ "minimum": 0,
359
+ "description": "Calls that failed."
360
+ },
361
+ "waitMs": {
362
+ "type": "number",
363
+ "minimum": 0,
364
+ "description": "Time requests spent waiting to be able to talk to this dependency, on top of the time spent talking to it: a saturated connection pool shows up here and nowhere else. Optional; agents that cannot see it omit it."
365
+ }
366
+ }
251
367
  }
252
368
  }
253
369
  }
@@ -0,0 +1,83 @@
1
+ {
2
+ "protocol": "0.4.0",
3
+ "agent": {
4
+ "name": "@downtrace/agent",
5
+ "version": "0.0.0",
6
+ "runtime": "node",
7
+ "runtimeVersion": "v24.0.0"
8
+ },
9
+ "instance": {
10
+ "id": "7d1e9c6a-4f2b-4c1e-9b0a-2f6d3e8a1c55",
11
+ "hostname": "web-1",
12
+ "pid": 4242
13
+ },
14
+ "deploy": {
15
+ "version": "8f71ac2",
16
+ "environment": "production"
17
+ },
18
+ "intervals": [
19
+ {
20
+ "start": 1756900000000,
21
+ "durationMs": 10000,
22
+ "endpoints": [
23
+ {
24
+ "method": "POST",
25
+ "route": "/checkout",
26
+ "count": 3,
27
+ "errors": 0,
28
+ "status": {
29
+ "success": 3,
30
+ "redirect": 0,
31
+ "clientError": 0,
32
+ "serverError": 0
33
+ },
34
+ "latency": {
35
+ "counts": [
36
+ 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
37
+ ],
38
+ "sum": 2.4,
39
+ "max": 0.9
40
+ },
41
+ "dependencies": [
42
+ {
43
+ "kind": "cassandra",
44
+ "target": "",
45
+ "callsPerRequest": [0, 0, 0, 0, 0, 3, 0, 0],
46
+ "totalMs": 41.25,
47
+ "max": 9.5,
48
+ "errors": 0
49
+ },
50
+ {
51
+ "kind": "http",
52
+ "target": "api.stripe.com",
53
+ "callsPerRequest": [0, 0, 3, 0, 0, 0, 0, 0],
54
+ "totalMs": 612.4,
55
+ "max": 240.1,
56
+ "errors": 1
57
+ },
58
+ {
59
+ "kind": "redis",
60
+ "target": "",
61
+ "callsPerRequest": [0, 0, 0, 3, 0, 0, 0, 0],
62
+ "totalMs": 4.2,
63
+ "max": 1.1,
64
+ "errors": 0
65
+ }
66
+ ]
67
+ }
68
+ ],
69
+ "runtime": {
70
+ "eventLoopDelayMs": {
71
+ "p50": 0.9,
72
+ "p99": 14.2,
73
+ "max": 61.5
74
+ },
75
+ "gcPauseMs": 22.4,
76
+ "gcCount": 7,
77
+ "heapUsedMb": 48.6,
78
+ "rssMb": 131.2,
79
+ "inFlightMax": 9
80
+ }
81
+ }
82
+ ]
83
+ }
@@ -0,0 +1,61 @@
1
+ {
2
+ "protocol": "0.3.0",
3
+ "agent": {
4
+ "name": "@downtrace/agent",
5
+ "version": "0.0.0",
6
+ "runtime": "node",
7
+ "runtimeVersion": "v24.0.0"
8
+ },
9
+ "instance": {
10
+ "id": "7d1e9c6a-4f2b-4c1e-9b0a-2f6d3e8a1c55",
11
+ "hostname": "web-1",
12
+ "pid": 4242
13
+ },
14
+ "deploy": {
15
+ "version": "8f71ac2",
16
+ "environment": "production"
17
+ },
18
+ "intervals": [
19
+ {
20
+ "start": 1756900000000,
21
+ "durationMs": 10000,
22
+ "endpoints": [
23
+ {
24
+ "method": "POST",
25
+ "route": "/checkout",
26
+ "count": 3,
27
+ "errors": 0,
28
+ "status": {
29
+ "success": 3,
30
+ "redirect": 0,
31
+ "clientError": 0,
32
+ "serverError": 0
33
+ },
34
+ "latency": {
35
+ "counts": [
36
+ 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
37
+ ],
38
+ "sum": 2.4,
39
+ "max": 0.9
40
+ },
41
+ "postgres": {
42
+ "queriesPerRequest": [0, 0, 0, 0, 0, 3, 0, 0],
43
+ "totalMs": 41.25,
44
+ "max": 9.5
45
+ }
46
+ }
47
+ ],
48
+ "runtime": {
49
+ "eventLoopDelayMs": {
50
+ "p50": 0.9,
51
+ "p99": 14.2,
52
+ "max": 61.5
53
+ },
54
+ "gcPauseMs": 22.4,
55
+ "gcCount": 7,
56
+ "heapUsedMb": 48.6,
57
+ "inFlightMax": 9
58
+ }
59
+ }
60
+ ]
61
+ }
@@ -0,0 +1,83 @@
1
+ {
2
+ "protocol": "0.4.0",
3
+ "agent": {
4
+ "name": "@downtrace/agent",
5
+ "version": "0.0.0",
6
+ "runtime": "node",
7
+ "runtimeVersion": "v24.0.0"
8
+ },
9
+ "instance": {
10
+ "id": "7d1e9c6a-4f2b-4c1e-9b0a-2f6d3e8a1c55",
11
+ "hostname": "web-1",
12
+ "pid": 4242
13
+ },
14
+ "deploy": {
15
+ "version": "8f71ac2",
16
+ "environment": "production"
17
+ },
18
+ "intervals": [
19
+ {
20
+ "start": 1756900000000,
21
+ "durationMs": 10000,
22
+ "endpoints": [
23
+ {
24
+ "method": "POST",
25
+ "route": "/checkout",
26
+ "count": 3,
27
+ "errors": 0,
28
+ "status": {
29
+ "success": 3,
30
+ "redirect": 0,
31
+ "clientError": 0,
32
+ "serverError": 0
33
+ },
34
+ "latency": {
35
+ "counts": [
36
+ 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
37
+ ],
38
+ "sum": 2.4,
39
+ "max": 0.9
40
+ },
41
+ "dependencies": [
42
+ {
43
+ "kind": "postgres",
44
+ "target": "",
45
+ "callsPerRequest": [0, 0, 0, 0, 0, 3, 0, 0],
46
+ "totalMs": 41.25,
47
+ "max": 9.5,
48
+ "errors": 0
49
+ },
50
+ {
51
+ "kind": "http",
52
+ "target": "api.stripe.com",
53
+ "callsPerRequest": [0, 0, 3, 0, 0, 0, 0, 0],
54
+ "totalMs": 612.4,
55
+ "max": 240.1,
56
+ "errors": 1
57
+ },
58
+ {
59
+ "kind": "redis",
60
+ "target": "",
61
+ "callsPerRequest": [0, 0, 0, 3, 0, 0, 0, 0],
62
+ "totalMs": 4.2,
63
+ "max": 1.1,
64
+ "errors": 0
65
+ }
66
+ ]
67
+ }
68
+ ],
69
+ "runtime": {
70
+ "eventLoopDelayMs": {
71
+ "p50": 0.9,
72
+ "p99": 14.2,
73
+ "max": 61.5
74
+ },
75
+ "gcPauseMs": 22.4,
76
+ "gcCount": 7,
77
+ "heapUsedMb": 48.6,
78
+ "rssMb": 131.2,
79
+ "inFlightMax": 9
80
+ }
81
+ }
82
+ ]
83
+ }
@@ -0,0 +1,84 @@
1
+ {
2
+ "protocol": "0.5.0",
3
+ "agent": {
4
+ "name": "@downtrace/agent",
5
+ "version": "0.0.0",
6
+ "runtime": "node",
7
+ "runtimeVersion": "v24.0.0"
8
+ },
9
+ "instance": {
10
+ "id": "7d1e9c6a-4f2b-4c1e-9b0a-2f6d3e8a1c55",
11
+ "hostname": "web-1",
12
+ "pid": 4242
13
+ },
14
+ "deploy": {
15
+ "version": "8f71ac2",
16
+ "environment": "production"
17
+ },
18
+ "intervals": [
19
+ {
20
+ "start": 1756900000000,
21
+ "durationMs": 10000,
22
+ "endpoints": [
23
+ {
24
+ "method": "POST",
25
+ "route": "/checkout",
26
+ "count": 3,
27
+ "errors": 0,
28
+ "status": {
29
+ "success": 3,
30
+ "redirect": 0,
31
+ "clientError": 0,
32
+ "serverError": 0
33
+ },
34
+ "latency": {
35
+ "counts": [
36
+ 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
37
+ ],
38
+ "sum": 2.4,
39
+ "max": 0.9
40
+ },
41
+ "dependencies": [
42
+ {
43
+ "kind": "postgres",
44
+ "target": "",
45
+ "callsPerRequest": [0, 0, 0, 0, 0, 3, 0, 0],
46
+ "totalMs": 41.25,
47
+ "max": 9.5,
48
+ "errors": 0,
49
+ "waitMs": 30.5
50
+ },
51
+ {
52
+ "kind": "http",
53
+ "target": "api.stripe.com",
54
+ "callsPerRequest": [0, 0, 3, 0, 0, 0, 0, 0],
55
+ "totalMs": 612.4,
56
+ "max": 240.1,
57
+ "errors": 1
58
+ },
59
+ {
60
+ "kind": "redis",
61
+ "target": "",
62
+ "callsPerRequest": [0, 0, 0, 3, 0, 0, 0, 0],
63
+ "totalMs": 4.2,
64
+ "max": 1.1,
65
+ "errors": 0
66
+ }
67
+ ]
68
+ }
69
+ ],
70
+ "runtime": {
71
+ "eventLoopDelayMs": {
72
+ "p50": 0.9,
73
+ "p99": 14.2,
74
+ "max": 61.5
75
+ },
76
+ "gcPauseMs": 22.4,
77
+ "gcCount": 7,
78
+ "heapUsedMb": 48.6,
79
+ "rssMb": 131.2,
80
+ "inFlightMax": 9
81
+ }
82
+ }
83
+ ]
84
+ }
@@ -0,0 +1,62 @@
1
+ {
2
+ "protocol": "0.3.0",
3
+ "agent": {
4
+ "name": "@downtrace/agent",
5
+ "version": "0.0.0",
6
+ "runtime": "node",
7
+ "runtimeVersion": "v24.0.0"
8
+ },
9
+ "instance": {
10
+ "id": "7d1e9c6a-4f2b-4c1e-9b0a-2f6d3e8a1c55",
11
+ "hostname": "web-1",
12
+ "pid": 4242
13
+ },
14
+ "deploy": {
15
+ "version": "8f71ac2",
16
+ "environment": "production"
17
+ },
18
+ "intervals": [
19
+ {
20
+ "start": 1756900000000,
21
+ "durationMs": 10000,
22
+ "endpoints": [
23
+ {
24
+ "method": "POST",
25
+ "route": "/checkout",
26
+ "count": 3,
27
+ "errors": 0,
28
+ "status": {
29
+ "success": 3,
30
+ "redirect": 0,
31
+ "clientError": 0,
32
+ "serverError": 0
33
+ },
34
+ "latency": {
35
+ "counts": [
36
+ 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
37
+ ],
38
+ "sum": 2.4,
39
+ "max": 0.9
40
+ },
41
+ "postgres": {
42
+ "queriesPerRequest": [0, 0, 0, 0, 0, 3, 0, 0],
43
+ "totalMs": 41.25,
44
+ "max": 9.5
45
+ }
46
+ }
47
+ ],
48
+ "runtime": {
49
+ "eventLoopDelayMs": {
50
+ "p50": 0.9,
51
+ "p99": 14.2,
52
+ "max": 61.5
53
+ },
54
+ "gcPauseMs": 22.4,
55
+ "gcCount": 7,
56
+ "heapUsedMb": 48.6,
57
+ "rssMb": 131.2,
58
+ "inFlightMax": 9
59
+ }
60
+ }
61
+ ]
62
+ }