@vzn/vx-otel 0.0.638 → 0.0.639

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
@@ -31,7 +31,7 @@ to declare in every environment:
31
31
 
32
32
  | Variable | Purpose |
33
33
  | ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
34
- | `OTEL_EXPORTER_OTLP_ENDPOINT` | base collector URL (e.g. `http://localhost:4318`) |
34
+ | `OTEL_EXPORTER_OTLP_ENDPOINT` | base collector URL (e.g. `http://localhost:4318`); one with no `http://` or `https://` is refused with a warning |
35
35
  | `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | full traces URL override |
36
36
  | `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` | full metrics URL override |
37
37
  | `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` | full logs URL override |
@@ -93,8 +93,7 @@ otel({
93
93
  `vcs.ref.head.name`, `vx.command` (the command line; what follows `--`
94
94
  is counted, `-- <N arguments>`, never quoted, and a secret value
95
95
  before it is `***`), CI provider,
96
- host/os/arch, vx version, `--tag k=v` →
97
- `vx.tag.<k>`, and `cicd.pipeline.result` (`success`, `failure`, or
96
+ host/os/arch, vx version, and `cicd.pipeline.result` (`success`, `failure`, or
98
97
  `cancellation` for a run stopped with nothing failed); a red run sets
99
98
  span status `ERROR`;
100
99
  - a child `vx.task` span per task — `cicd.pipeline.task.name`,
@@ -191,8 +190,7 @@ same CPU live as with one send at the end (median of 10: 815 ms both).
191
190
 
192
191
  No send ever holds a task: a task's end only queues its records. A collector
193
192
  that never answers holds nothing either: at the end of the run every send
194
- still in flight is cut at core's teardown deadline (3 s,
195
- `VX_RUN_TEARDOWN_TIMEOUT`), and the process exits.
193
+ still in flight is cut at core's teardown deadline (3 s), and the process exits.
196
194
 
197
195
  ## Behavior note
198
196
 
@@ -219,5 +217,5 @@ userinfo and query string replaced by `***`, so an endpoint that carries
219
217
  its credential in the URL does not leak it into a CI log. The run stays
220
218
  green either way; a collector that
221
219
  takes too long is cut off by core's end-of-run deadline
222
- (`VX_RUN_TEARDOWN_TIMEOUT`, 3 s by default) with a line saying the buffered
220
+ (3 s) with a line saying the buffered
223
221
  records were lost.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vzn/vx-otel",
3
- "version": "0.0.638",
3
+ "version": "0.0.639",
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.638"
37
+ "@vzn/vx": "^0.0.639"
38
38
  },
39
39
  "repository": {
40
40
  "type": "git",
package/src/otlp.ts CHANGED
@@ -109,7 +109,6 @@ const VX_ATTR = {
109
109
  version: 'vx.version',
110
110
  dirty: 'vx.dirty',
111
111
  defaultBranch: 'vx.default_branch',
112
- tagPrefix: 'vx.tag.',
113
112
  // run summary (root span only)
114
113
  runStartedAt: 'vx.run.started_at',
115
114
  runEndedAt: 'vx.run.ended_at',
@@ -395,7 +394,6 @@ export function runSpanAttributes(run: RunContextRecord, summary?: RunSummaryRec
395
394
  if (run.dirty !== null) attrs.push(boolAttr(VX_ATTR.dirty, run.dirty))
396
395
  if (run.ciProvider !== null) attrs.push(strAttr(VX_ATTR.ciProvider, run.ciProvider))
397
396
  if (run.host !== null) attrs.push(strAttr(VX_ATTR.host, run.host))
398
- for (const [k, v] of Object.entries(run.tags)) attrs.push(strAttr(`${VX_ATTR.tagPrefix}${k}`, v))
399
397
  if (summary !== undefined) {
400
398
  attrs.push(
401
399
  intAttr(VX_ATTR.runStartedAt, summary.startedAt),
package/src/plugin.ts CHANGED
@@ -174,19 +174,47 @@ export function resolveOtelConfig(
174
174
  env: Record<string, string | undefined>,
175
175
  warn?: (m: string) => void,
176
176
  ): OtelSinkConfig | undefined {
177
- const base = present(opts.endpoint) ?? present(env['OTEL_EXPORTER_OTLP_ENDPOINT'])
178
- const tracesUrl =
179
- present(opts.tracesEndpoint) ??
180
- present(env['OTEL_EXPORTER_OTLP_TRACES_ENDPOINT']) ??
181
- (base ? joinSignal(base, 'traces') : undefined)
182
- const metricsUrl =
183
- present(opts.metricsEndpoint) ??
184
- present(env['OTEL_EXPORTER_OTLP_METRICS_ENDPOINT']) ??
185
- (base ? joinSignal(base, 'metrics') : undefined)
186
- const logsUrl =
187
- present(opts.logsEndpoint) ??
188
- present(env['OTEL_EXPORTER_OTLP_LOGS_ENDPOINT']) ??
189
- (base ? joinSignal(base, 'logs') : undefined)
177
+ // Each URL with the setting it came from, so a refusal can name it
178
+ // without printing a value that may hold a credential.
179
+ const from = (option: keyof OtelPluginOptions, envName: string): [string, string] | undefined => {
180
+ const o = present(opts[option] as string | undefined)
181
+ if (o !== undefined) return [o, `the ${option} option`]
182
+ const e = present(env[envName])
183
+ return e === undefined ? undefined : [e, envName]
184
+ }
185
+ const base = from('endpoint', 'OTEL_EXPORTER_OTLP_ENDPOINT')
186
+ const pick = (
187
+ option: keyof OtelPluginOptions,
188
+ envName: string,
189
+ signal: string,
190
+ ): [string, string] | undefined =>
191
+ from(option, envName) ??
192
+ (base === undefined ? undefined : [joinSignal(base[0], signal), base[1]])
193
+ const picked = {
194
+ traces: pick('tracesEndpoint', 'OTEL_EXPORTER_OTLP_TRACES_ENDPOINT', 'traces'),
195
+ metrics: pick('metricsEndpoint', 'OTEL_EXPORTER_OTLP_METRICS_ENDPOINT', 'metrics'),
196
+ logs: pick('logsEndpoint', 'OTEL_EXPORTER_OTLP_LOGS_ENDPOINT', 'logs'),
197
+ }
198
+ // An endpoint with no http(s) scheme (`localhost:4318`) parses as a URL
199
+ // of scheme `localhost:`: every export failed "Unable to connect", once
200
+ // per signal, after a second of retries. It is refused here, once per
201
+ // setting, and its signals are not exported.
202
+ const refused = new Map<string, string[]>()
203
+ for (const signal of ['traces', 'metrics', 'logs'] as const) {
204
+ const p = picked[signal]
205
+ if (p === undefined) continue
206
+ const protocol = URL.parse(p[0])?.protocol
207
+ if (protocol === 'http:' || protocol === 'https:') continue
208
+ refused.set(p[1], [...(refused.get(p[1]) ?? []), signal])
209
+ picked[signal] = undefined
210
+ }
211
+ for (const [setting, signals] of refused) {
212
+ const names = signals.length === 3 ? 'traces, metrics and logs' : signals.join(' and ')
213
+ warn?.(`[vx-otel] ${setting} is not an http:// or https:// URL — ${names} are not exported`)
214
+ }
215
+ const tracesUrl = picked.traces?.[0]
216
+ const metricsUrl = picked.metrics?.[0]
217
+ const logsUrl = picked.logs?.[0]
190
218
  // Each signal has its own endpoint, as the spec has them: a pipeline that
191
219
  // set only a metrics (or logs) endpoint declined whole and exported
192
220
  // nothing (F-45). The plugin declines only when no signal has one.