Telemetry-compatible tracing (#22713)
A number of changes here. I recommend viewing the diff with the <a href="?w=1">whitespace flag enabled</a>.
- OpenTelemetry is replaced with a custom and lightweight tracing solution.
- Three trace targets are currently supported: console, Zipkin, and NextJS.
- Tracing is now governed by environment variables rather than `--require instrument.js`.
+ `TRACE_TARGET`: one of `CONSOLE`, `ZIPKIN`, or `TELEMETRY`; defaults to `TELEMETRY` if unset or invalid.
+ `TRACE_ID`: an 8-byte hex-encoded value used as the Zipkin trace ID; if not provided, this value will be randomly generated and passed down to subprocesses.
Other sundry:
- I'm missing something, probably a setup step, with the Zipkin target. Traces are captured successfully, but you have to manually enter the Trace ID in order to view the trace - it doesn't show up in queries.
- I'm generally unhappy with [this commit](https://github.com/vercel/next.js/pull/22713/commits/235cedcb3ead76b630b4c8aa695f904489da2831). It is... untidy to provide a telemetry object via `setGlobal`, but I don't have a ready alternative. Is `distDir` strictly required when creating a new Telemetry object? I didn't dig too deep here.
As noted, there are a lot of changes, so it'd be great if a reviewer could:
- [ ] pull down the branch and try to break it
- [ ] check the Zipkin traces and identify possible regressions in the functionality
Closes #22570
Fixes #22574
2021-03-10 22:00:20 +01:00
|
|
|
import { randomBytes } from 'crypto'
|
|
|
|
import { SpanId } from './shared'
|
|
|
|
import { report } from './report'
|
|
|
|
|
|
|
|
const NUM_OF_MICROSEC_IN_SEC = BigInt('1000')
|
|
|
|
|
|
|
|
const getId = () => randomBytes(8).toString('hex')
|
|
|
|
|
2021-04-25 20:34:36 +02:00
|
|
|
// eslint typescript has a bug with TS enums
|
|
|
|
/* eslint-disable no-shadow */
|
Telemetry-compatible tracing (#22713)
A number of changes here. I recommend viewing the diff with the <a href="?w=1">whitespace flag enabled</a>.
- OpenTelemetry is replaced with a custom and lightweight tracing solution.
- Three trace targets are currently supported: console, Zipkin, and NextJS.
- Tracing is now governed by environment variables rather than `--require instrument.js`.
+ `TRACE_TARGET`: one of `CONSOLE`, `ZIPKIN`, or `TELEMETRY`; defaults to `TELEMETRY` if unset or invalid.
+ `TRACE_ID`: an 8-byte hex-encoded value used as the Zipkin trace ID; if not provided, this value will be randomly generated and passed down to subprocesses.
Other sundry:
- I'm missing something, probably a setup step, with the Zipkin target. Traces are captured successfully, but you have to manually enter the Trace ID in order to view the trace - it doesn't show up in queries.
- I'm generally unhappy with [this commit](https://github.com/vercel/next.js/pull/22713/commits/235cedcb3ead76b630b4c8aa695f904489da2831). It is... untidy to provide a telemetry object via `setGlobal`, but I don't have a ready alternative. Is `distDir` strictly required when creating a new Telemetry object? I didn't dig too deep here.
As noted, there are a lot of changes, so it'd be great if a reviewer could:
- [ ] pull down the branch and try to break it
- [ ] check the Zipkin traces and identify possible regressions in the functionality
Closes #22570
Fixes #22574
2021-03-10 22:00:20 +01:00
|
|
|
export enum SpanStatus {
|
|
|
|
Started,
|
|
|
|
Stopped,
|
|
|
|
}
|
|
|
|
|
|
|
|
export class Span {
|
|
|
|
name: string
|
|
|
|
id: SpanId
|
|
|
|
parentId?: SpanId
|
|
|
|
duration: number | null
|
|
|
|
attrs: { [key: string]: any }
|
|
|
|
status: SpanStatus
|
|
|
|
|
|
|
|
_start: bigint
|
|
|
|
|
|
|
|
constructor(name: string, parentId?: SpanId, attrs?: Object) {
|
|
|
|
this.name = name
|
|
|
|
this.parentId = parentId
|
|
|
|
this.duration = null
|
|
|
|
this.attrs = attrs ? { ...attrs } : {}
|
|
|
|
this.status = SpanStatus.Started
|
|
|
|
this.id = getId()
|
|
|
|
this._start = process.hrtime.bigint()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Durations are reported as microseconds. This gives 1000x the precision
|
|
|
|
// of something like Date.now(), which reports in milliseconds.
|
|
|
|
// Additionally, ~285 years can be safely represented as microseconds as
|
|
|
|
// a float64 in both JSON and JavaScript.
|
|
|
|
stop() {
|
|
|
|
const end: bigint = process.hrtime.bigint()
|
|
|
|
const duration = (end - this._start) / NUM_OF_MICROSEC_IN_SEC
|
|
|
|
this.status = SpanStatus.Stopped
|
|
|
|
if (duration > Number.MAX_SAFE_INTEGER) {
|
|
|
|
throw new Error(`Duration is too long to express as float64: ${duration}`)
|
|
|
|
}
|
|
|
|
const timestamp = this._start / NUM_OF_MICROSEC_IN_SEC
|
|
|
|
report(
|
|
|
|
this.name,
|
|
|
|
Number(duration),
|
|
|
|
Number(timestamp),
|
|
|
|
this.id,
|
|
|
|
this.parentId,
|
|
|
|
this.attrs
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
traceChild(name: string, attrs?: Object) {
|
|
|
|
return new Span(name, this.id, attrs)
|
|
|
|
}
|
|
|
|
|
|
|
|
setAttribute(key: string, value: any) {
|
|
|
|
this.attrs[key] = value
|
|
|
|
}
|
|
|
|
|
|
|
|
traceFn(fn: any) {
|
|
|
|
try {
|
|
|
|
return fn()
|
|
|
|
} finally {
|
|
|
|
this.stop()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-04 10:41:01 +02:00
|
|
|
async traceAsyncFn<T>(fn: () => T | Promise<T>): Promise<T> {
|
Telemetry-compatible tracing (#22713)
A number of changes here. I recommend viewing the diff with the <a href="?w=1">whitespace flag enabled</a>.
- OpenTelemetry is replaced with a custom and lightweight tracing solution.
- Three trace targets are currently supported: console, Zipkin, and NextJS.
- Tracing is now governed by environment variables rather than `--require instrument.js`.
+ `TRACE_TARGET`: one of `CONSOLE`, `ZIPKIN`, or `TELEMETRY`; defaults to `TELEMETRY` if unset or invalid.
+ `TRACE_ID`: an 8-byte hex-encoded value used as the Zipkin trace ID; if not provided, this value will be randomly generated and passed down to subprocesses.
Other sundry:
- I'm missing something, probably a setup step, with the Zipkin target. Traces are captured successfully, but you have to manually enter the Trace ID in order to view the trace - it doesn't show up in queries.
- I'm generally unhappy with [this commit](https://github.com/vercel/next.js/pull/22713/commits/235cedcb3ead76b630b4c8aa695f904489da2831). It is... untidy to provide a telemetry object via `setGlobal`, but I don't have a ready alternative. Is `distDir` strictly required when creating a new Telemetry object? I didn't dig too deep here.
As noted, there are a lot of changes, so it'd be great if a reviewer could:
- [ ] pull down the branch and try to break it
- [ ] check the Zipkin traces and identify possible regressions in the functionality
Closes #22570
Fixes #22574
2021-03-10 22:00:20 +01:00
|
|
|
try {
|
|
|
|
return await fn()
|
|
|
|
} finally {
|
|
|
|
this.stop()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export const trace = (name: string, parentId?: SpanId, attrs?: Object) => {
|
|
|
|
return new Span(name, parentId, attrs)
|
|
|
|
}
|