@vzn/vx-otel 0.0.652 → 0.0.653
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 +6 -4
- package/package.json +2 -2
- package/src/otlp.ts +6 -2
- package/src/sink.ts +31 -2
package/README.md
CHANGED
|
@@ -96,12 +96,14 @@ otel({
|
|
|
96
96
|
is counted, `-- <N arguments>`, never quoted, and a secret value
|
|
97
97
|
before it is `***`), CI provider,
|
|
98
98
|
host/os/arch, vx version, and `cicd.pipeline.result` (`success`, `failure`, or
|
|
99
|
-
`cancellation` for a run stopped with nothing failed)
|
|
99
|
+
`cancellation` for a run stopped with nothing failed), and the counts:
|
|
100
|
+
`vx.run.aborted_count` the tasks the stop cut short, `vx.run.not_run_count`
|
|
101
|
+
the ones it reached first; a red run sets
|
|
100
102
|
span status `ERROR`;
|
|
101
|
-
- a child `vx.task` span per task — `cicd.pipeline.task.name`,
|
|
103
|
+
- a child `vx.task` span per task (none for one the stop reached before it started) — `cicd.pipeline.task.name`,
|
|
102
104
|
`cicd.pipeline.task.run.result` (the convention's enum: `success` for a
|
|
103
105
|
run or a hit, `failure`, `timeout` (vx's own `timeout`, or a server's readiness wait), `skip`, `cancellation`), vx's own
|
|
104
|
-
status as `vx.task.status`, `vx.cache.source` (miss/local/remote, or none for a task that never ran),
|
|
106
|
+
status as `vx.task.status`, `vx.cache.source` (miss/local/remote, or none for a task that never ran or a failed hit whose restore threw),
|
|
105
107
|
on a hit `vx.cache.restored` (`true` when it restored outputs, `false` when they were already up to date),
|
|
106
108
|
`vx.task.hash`, duration, CPU ms, peak RSS, and on a skipped task its root
|
|
107
109
|
blocker (`vx.task.blocked_by`) or, skipped for its `exec.platforms`, what it
|
|
@@ -140,7 +142,7 @@ otel({
|
|
|
140
142
|
stage does, before the run lock, and that start is the
|
|
141
143
|
`vx.run.started_at` every task's `vx.task.run_started_at` names.
|
|
142
144
|
|
|
143
|
-
**Metrics per run**: `vx.tasks.total
|
|
145
|
+
**Metrics per run**: `vx.tasks.total` (the terminal's total, not-run tasks included), `vx.tasks.failed`,
|
|
144
146
|
`vx.tasks.cache_hits{source=local|remote}`, what those hits did to the disk as
|
|
145
147
|
`vx.tasks.cache_restored{source=local|remote}` and `vx.tasks.cache_up_to_date`,
|
|
146
148
|
and the `vx.run.duration_ms` and `vx.run.time_saved_ms` (what the hits'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx-otel",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.653",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "OpenTelemetry exporter for vx — a telemetry plugin mapping runs to OTLP traces + metrics (CI/CD + VCS semantic conventions).",
|
|
6
6
|
"keywords": [
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"@vzn/vx": "^0.0.
|
|
37
|
+
"@vzn/vx": "^0.0.653"
|
|
38
38
|
},
|
|
39
39
|
"repository": {
|
|
40
40
|
"type": "git",
|
package/src/otlp.ts
CHANGED
|
@@ -116,6 +116,7 @@ const VX_ATTR = {
|
|
|
116
116
|
runTaskCount: 'vx.run.task_count',
|
|
117
117
|
runFailedCount: 'vx.run.failed_count',
|
|
118
118
|
runAbortedCount: 'vx.run.aborted_count',
|
|
119
|
+
runNotRunCount: 'vx.run.not_run_count',
|
|
119
120
|
runHitCount: 'vx.run.hit_count',
|
|
120
121
|
runHitLocalCount: 'vx.run.hit_local_count',
|
|
121
122
|
runHitRemoteCount: 'vx.run.hit_remote_count',
|
|
@@ -405,6 +406,7 @@ export function runSpanAttributes(run: RunContextRecord, summary?: RunSummaryRec
|
|
|
405
406
|
intAttr(VX_ATTR.runTaskCount, summary.taskCount),
|
|
406
407
|
intAttr(VX_ATTR.runFailedCount, summary.failedCount),
|
|
407
408
|
intAttr(VX_ATTR.runAbortedCount, summary.abortedCount),
|
|
409
|
+
intAttr(VX_ATTR.runNotRunCount, summary.notRunCount ?? 0),
|
|
408
410
|
intAttr(VX_ATTR.runHitCount, summary.hitCount),
|
|
409
411
|
intAttr(VX_ATTR.runHitLocalCount, summary.hitLocalCount),
|
|
410
412
|
intAttr(VX_ATTR.runHitRemoteCount, summary.hitRemoteCount),
|
|
@@ -428,11 +430,13 @@ export function runSpanAttributes(run: RunContextRecord, summary?: RunSummaryRec
|
|
|
428
430
|
|
|
429
431
|
/**
|
|
430
432
|
* `cicd.pipeline.result`, the task enum's run-level twin. A red run with
|
|
431
|
-
* nothing failed and something aborted is a stopped one.
|
|
433
|
+
* nothing failed and something aborted or not run is a stopped one.
|
|
432
434
|
*/
|
|
433
435
|
function pipelineResult(s: RunSummaryRecord): string {
|
|
434
436
|
if (s.exitOk) return 'success'
|
|
435
|
-
return s.failedCount === 0 && s.abortedCount
|
|
437
|
+
return s.failedCount === 0 && s.abortedCount + (s.notRunCount ?? 0) > 0
|
|
438
|
+
? 'cancellation'
|
|
439
|
+
: 'failure'
|
|
436
440
|
}
|
|
437
441
|
|
|
438
442
|
/** The root span is ERROR when the run is red: UNSET read as a clean run. */
|
package/src/sink.ts
CHANGED
|
@@ -175,7 +175,7 @@ async function postOnce(
|
|
|
175
175
|
)
|
|
176
176
|
// Core's flush deadline ends the POST too: `timeoutMs` (15 s by
|
|
177
177
|
// default) alone held the process that long after the run (item 1055).
|
|
178
|
-
const onDeadline = (): void => controller.abort()
|
|
178
|
+
const onDeadline = (): void => controller.abort(deadline?.reason)
|
|
179
179
|
deadline?.addEventListener('abort', onDeadline, { once: true })
|
|
180
180
|
try {
|
|
181
181
|
let res: Response
|
|
@@ -210,7 +210,7 @@ async function postOnce(
|
|
|
210
210
|
// (walked the adopter's path, 2026-09-20). The body is the
|
|
211
211
|
// collector's own explanation, so a line of it rides the message.
|
|
212
212
|
// quiet: the status alone still names the refusal
|
|
213
|
-
const text = await res
|
|
213
|
+
const text = await upTo(res, REPLY_BYTES).catch(() => '')
|
|
214
214
|
if (res.status >= 300 && res.status < 400) {
|
|
215
215
|
const location = res.headers.get('location')
|
|
216
216
|
const to = location === null ? null : URL.parse(location, url)
|
|
@@ -234,6 +234,35 @@ async function postOnce(
|
|
|
234
234
|
}
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
+
/**
|
|
238
|
+
* Room for a collector's error line or partial-success JSON: read whole,
|
|
239
|
+
* an endless 200 grew vx to 2.1 GB RSS within the flush deadline.
|
|
240
|
+
*/
|
|
241
|
+
const REPLY_BYTES = 64 * 1024
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* A body's first `max` bytes as text, the rest cancelled unread
|
|
245
|
+
* (vx-history's and vx-cache's `upTo`).
|
|
246
|
+
*/
|
|
247
|
+
async function upTo(res: Response, max: number): Promise<string> {
|
|
248
|
+
const reader = res.body?.getReader()
|
|
249
|
+
if (reader === undefined) return ''
|
|
250
|
+
const parts: Uint8Array[] = []
|
|
251
|
+
let n = 0
|
|
252
|
+
for (;;) {
|
|
253
|
+
const { done, value } = await reader.read()
|
|
254
|
+
if (done) break
|
|
255
|
+
parts.push(value)
|
|
256
|
+
n += value.byteLength
|
|
257
|
+
if (n > max) {
|
|
258
|
+
// quiet: the rest is unwanted; its cancel adds nothing
|
|
259
|
+
await reader.cancel().catch(() => {})
|
|
260
|
+
return Buffer.concat(parts).subarray(0, max).toString('utf8')
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return Buffer.concat(parts).toString('utf8')
|
|
264
|
+
}
|
|
265
|
+
|
|
237
266
|
/**
|
|
238
267
|
* A `Retry-After` in ms, or 0: seconds, or an HTTP date (RFC 9110), which
|
|
239
268
|
* `Number()` read as NaN and the retry came at the default delay.
|