@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 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); a red run sets
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`, `vx.tasks.failed`,
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.652",
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.652"
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 > 0 ? 'cancellation' : 'failure'
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.text().catch(() => '')
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.