@vzn/vx-otel 0.0.526 → 0.0.528
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 +14 -5
- package/package.json +2 -2
- package/src/otlp.ts +37 -5
package/README.md
CHANGED
|
@@ -107,8 +107,13 @@ otel({
|
|
|
107
107
|
command (`vx.task.command`, secrets masked), on a flaky one its record
|
|
108
108
|
(`vx.task.flaky.passes`, `vx.task.flaky.failures`), on a hit what the
|
|
109
109
|
stored run took (`vx.cache.stored_duration_ms`, `vx.cache.stored_cpu_ms`,
|
|
110
|
-
`vx.cache.stored_peak_rss_bytes`),
|
|
111
|
-
(`vx.task.admission_held_ms`)
|
|
110
|
+
`vx.cache.stored_peak_rss_bytes`), how long an `admit` policy held it
|
|
111
|
+
(`vx.task.admission_held_ms`), how long it waited ready for a worker
|
|
112
|
+
(`vx.task.queued_ms`, the hold included), and on a cacheable task that
|
|
113
|
+
ran how many files its key read (`vx.task.input_files`) and what its key
|
|
114
|
+
changed since the last entry the cache saved for it
|
|
115
|
+
(`vx.cache.miss.change_count`, and `vx.cache.miss.changes`, the first ten
|
|
116
|
+
as `changed file packages/a/src/x.ts`). A
|
|
112
117
|
failed task sets span status
|
|
113
118
|
`ERROR`. A task span links to the spans of the tasks it waited on (a
|
|
114
119
|
group seen through to the tasks behind it), and carries an event per
|
|
@@ -138,8 +143,9 @@ backend adds runs rather than reading each as the series' new total.
|
|
|
138
143
|
`vx.task.project` and `vx.task.task`: at each task's end
|
|
139
144
|
`vx.task.duration` (ms, with `vx.cache.source`), and for a task the runner
|
|
140
145
|
measured `vx.task.cpu_time` (ms) and `vx.task.peak_memory` (bytes), on a
|
|
141
|
-
hit `vx.task.time_saved` (ms),
|
|
142
|
-
`admit` policy held it
|
|
146
|
+
hit `vx.task.time_saved` (ms), `vx.task.admission_held` (ms) when an
|
|
147
|
+
`admit` policy held it, and `vx.task.queued` (ms) when it waited for a
|
|
148
|
+
worker; a skipped task sends none. While a task runs, its process tree is sampled
|
|
143
149
|
each second: `vx.task.cpu_usage` (cores busy since the last sample, 1 =
|
|
144
150
|
one core) and `vx.task.memory` (resident bytes). Sampling runs only while
|
|
145
151
|
metrics export; a remote task, or a task under 1 s, has no samples.
|
|
@@ -155,7 +161,10 @@ secrets, and this sends it to the collector; `logs: false` or
|
|
|
155
161
|
(`host.name`, `host.arch`, `os.type`), the commit (`vcs.ref.head.*`) and
|
|
156
162
|
the repository (`vcs.repository.url.full`, `vcs.repository.name`,
|
|
157
163
|
`vcs.owner.name`, `vcs.provider.name`, from the `origin` remote) with
|
|
158
|
-
`vx.workspace.path`, the workspace root inside it (`.` at the top),
|
|
164
|
+
`vx.workspace.path`, the workspace root inside it (`.` at the top), and on
|
|
165
|
+
CI the run (`cicd.pipeline.run.url.full`, `cicd.pipeline.name`,
|
|
166
|
+
`vx.ci.job`, `vx.ci.attempt`) and the pull request it builds
|
|
167
|
+
(`vcs.change.id`; GitHub Actions, GitLab, Buildkite, CircleCI), under `OTEL_RESOURCE_ATTRIBUTES`, which wins. Each task metric point carries
|
|
159
168
|
its task's span as an exemplar, and each log record its span, so a chart
|
|
160
169
|
opens the trace and a span opens its output.
|
|
161
170
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx-otel",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.528",
|
|
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.528"
|
|
38
38
|
},
|
|
39
39
|
"repository": {
|
|
40
40
|
"type": "git",
|
package/src/otlp.ts
CHANGED
|
@@ -24,6 +24,7 @@ type AnyValue =
|
|
|
24
24
|
| { intValue: string }
|
|
25
25
|
| { boolValue: boolean }
|
|
26
26
|
| { doubleValue: number }
|
|
27
|
+
| { arrayValue: { values: AnyValue[] } }
|
|
27
28
|
|
|
28
29
|
export interface KeyValue {
|
|
29
30
|
key: string
|
|
@@ -33,6 +34,9 @@ export interface KeyValue {
|
|
|
33
34
|
function strAttr(key: string, v: string): KeyValue {
|
|
34
35
|
return { key, value: { stringValue: v } }
|
|
35
36
|
}
|
|
37
|
+
function strArrayAttr(key: string, v: readonly string[]): KeyValue {
|
|
38
|
+
return { key, value: { arrayValue: { values: v.map((stringValue) => ({ stringValue })) } } }
|
|
39
|
+
}
|
|
36
40
|
function intAttr(key: string, v: number): KeyValue {
|
|
37
41
|
return { key, value: { intValue: String(Math.trunc(v)) } }
|
|
38
42
|
}
|
|
@@ -50,6 +54,9 @@ function int64Attr(key: string, v: string): KeyValue {
|
|
|
50
54
|
|
|
51
55
|
const SEMCONV = {
|
|
52
56
|
pipelineRunId: 'cicd.pipeline.run.id',
|
|
57
|
+
pipelineRunUrl: 'cicd.pipeline.run.url.full',
|
|
58
|
+
pipelineName: 'cicd.pipeline.name',
|
|
59
|
+
vcsChangeId: 'vcs.change.id',
|
|
53
60
|
pipelineResult: 'cicd.pipeline.result',
|
|
54
61
|
taskName: 'cicd.pipeline.task.name',
|
|
55
62
|
taskRunResult: 'cicd.pipeline.task.run.result',
|
|
@@ -141,6 +148,12 @@ const VX_ATTR = {
|
|
|
141
148
|
taskFlakyPasses: 'vx.task.flaky.passes',
|
|
142
149
|
taskFlakyFailures: 'vx.task.flaky.failures',
|
|
143
150
|
taskAdmissionHeldMs: 'vx.task.admission_held_ms',
|
|
151
|
+
taskQueuedMs: 'vx.task.queued_ms',
|
|
152
|
+
taskInputFiles: 'vx.task.input_files',
|
|
153
|
+
missChangeCount: 'vx.cache.miss.change_count',
|
|
154
|
+
missChanges: 'vx.cache.miss.changes',
|
|
155
|
+
ciJob: 'vx.ci.job',
|
|
156
|
+
ciAttempt: 'vx.ci.attempt',
|
|
144
157
|
storedDurationMs: 'vx.cache.stored_duration_ms',
|
|
145
158
|
storedCpuMs: 'vx.cache.stored_cpu_ms',
|
|
146
159
|
storedPeakRssBytes: 'vx.cache.stored_peak_rss_bytes',
|
|
@@ -299,7 +312,7 @@ export function runResource(run: RunContextRecord): Record<string, string> {
|
|
|
299
312
|
[SEMCONV.hostArch]: run.arch === 'x64' ? 'amd64' : run.arch,
|
|
300
313
|
}
|
|
301
314
|
if (run.host !== null) r[SEMCONV.hostName] = run.host
|
|
302
|
-
for (const [k, v] of
|
|
315
|
+
for (const [k, v] of originFields(run)) r[k] = v
|
|
303
316
|
if (run.commitSha !== null) r[SEMCONV.vcsHeadRevision] = run.commitSha
|
|
304
317
|
if (run.branch !== null) r[SEMCONV.vcsHeadName] = run.branch
|
|
305
318
|
return r
|
|
@@ -313,10 +326,11 @@ const VCS_PROVIDERS: Readonly<Record<string, string>> = {
|
|
|
313
326
|
}
|
|
314
327
|
|
|
315
328
|
/**
|
|
316
|
-
*
|
|
317
|
-
* the VCS conventions name it,
|
|
329
|
+
* Where the run came from: the repository (from the normalized origin remote,
|
|
330
|
+
* `host/owner/name`) as the VCS conventions name it, where in it the
|
|
331
|
+
* workspace sits, and the CI run, pull request, workflow and job.
|
|
318
332
|
*/
|
|
319
|
-
function
|
|
333
|
+
function originFields(run: RunContextRecord): [key: string, value: string][] {
|
|
320
334
|
const fields: [string, string][] = []
|
|
321
335
|
if (run.repository !== undefined) {
|
|
322
336
|
const parts = run.repository.split('/')
|
|
@@ -329,6 +343,11 @@ function repositoryFields(run: RunContextRecord): [key: string, value: string][]
|
|
|
329
343
|
if (provider !== undefined) fields.push([SEMCONV.vcsProviderName, provider])
|
|
330
344
|
}
|
|
331
345
|
if (run.workspacePath !== undefined) fields.push([VX_ATTR.workspacePath, run.workspacePath])
|
|
346
|
+
if (run.ciRunUrl !== undefined) fields.push([SEMCONV.pipelineRunUrl, run.ciRunUrl])
|
|
347
|
+
if (run.ciChange !== undefined) fields.push([SEMCONV.vcsChangeId, run.ciChange])
|
|
348
|
+
if (run.ciPipeline !== undefined) fields.push([SEMCONV.pipelineName, run.ciPipeline])
|
|
349
|
+
if (run.ciJob !== undefined) fields.push([VX_ATTR.ciJob, run.ciJob])
|
|
350
|
+
if (run.ciAttempt !== undefined) fields.push([VX_ATTR.ciAttempt, String(run.ciAttempt)])
|
|
332
351
|
return fields
|
|
333
352
|
}
|
|
334
353
|
|
|
@@ -352,7 +371,7 @@ export function runSpanAttributes(run: RunContextRecord, summary?: RunSummaryRec
|
|
|
352
371
|
intAttr(VX_ATTR.schema, TELEMETRY_SCHEMA_VERSION),
|
|
353
372
|
strAttr(VX_ATTR.workspaceId, run.workspaceId),
|
|
354
373
|
strAttr(VX_ATTR.workspaceName, run.workspaceName),
|
|
355
|
-
...
|
|
374
|
+
...originFields(run).map(([k, v]) => strAttr(k, v)),
|
|
356
375
|
strAttr(VX_ATTR.command, run.command),
|
|
357
376
|
strAttr(VX_ATTR.requestedTasks, run.requestedTasks.join(',')),
|
|
358
377
|
strAttr(VX_ATTR.cachePolicy, run.cachePolicy),
|
|
@@ -499,6 +518,18 @@ export function taskSpanAttributes(t: TaskTelemetry, run: TaskSpanRunContext): K
|
|
|
499
518
|
attrs.push(intAttr(VX_ATTR.storedPeakRssBytes, t.storedPeakRssBytes))
|
|
500
519
|
if (t.admissionHeldMs !== undefined)
|
|
501
520
|
attrs.push(intAttr(VX_ATTR.taskAdmissionHeldMs, t.admissionHeldMs))
|
|
521
|
+
if (t.queuedMs !== undefined) attrs.push(intAttr(VX_ATTR.taskQueuedMs, t.queuedMs))
|
|
522
|
+
if (t.inputFiles !== undefined) attrs.push(intAttr(VX_ATTR.taskInputFiles, t.inputFiles))
|
|
523
|
+
if (t.inputChanges !== undefined) {
|
|
524
|
+
// `changed file src/a.ts`: what moved the key since the last saved entry.
|
|
525
|
+
attrs.push(
|
|
526
|
+
intAttr(VX_ATTR.missChangeCount, t.inputChanges.count),
|
|
527
|
+
strArrayAttr(
|
|
528
|
+
VX_ATTR.missChanges,
|
|
529
|
+
t.inputChanges.first.map((c) => `${c.change} ${c.kind} ${c.name}`),
|
|
530
|
+
),
|
|
531
|
+
)
|
|
532
|
+
}
|
|
502
533
|
if (t.wallclockStartNs !== undefined)
|
|
503
534
|
attrs.push(int64Attr(VX_ATTR.wallclockStartNs, t.wallclockStartNs))
|
|
504
535
|
if (t.wallclockEndNs !== undefined)
|
|
@@ -693,6 +724,7 @@ function taskMetrics(points: readonly TaskMetricPoint[]): unknown[] {
|
|
|
693
724
|
if (t.peakRssBytes !== undefined) add('vx.task.peak_memory', 'By', at(t.peakRssBytes))
|
|
694
725
|
if (t.storedDurationMs !== undefined) add('vx.task.time_saved', 'ms', at(t.storedDurationMs))
|
|
695
726
|
if (t.admissionHeldMs !== undefined) add('vx.task.admission_held', 'ms', at(t.admissionHeldMs))
|
|
727
|
+
if (t.queuedMs !== undefined) add('vx.task.queued', 'ms', at(t.queuedMs))
|
|
696
728
|
}
|
|
697
729
|
return [...series].map(([name, { unit, dataPoints }]) => ({ name, unit, gauge: { dataPoints } }))
|
|
698
730
|
}
|