Skip to content

Commit

Permalink
Telemetry: add time-to-first-byte signal (vercel#61238)
Browse files Browse the repository at this point in the history
  • Loading branch information
dvoytenko committed Jan 29, 2024
1 parent 5cf4f87 commit 6eb2c24
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -357,3 +357,9 @@ Attributes:
- `next.span_name`
- `next.span_type`
- `next.segment`

### `start response`

- `next.span_type`: `NextNodeServer.startResponse`.

This zero-length span represents the time when the first byte has been sent in the response.
2 changes: 2 additions & 0 deletions packages/next/src/server/lib/trace/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ enum NextNodeServerSpan {
renderError = 'NextNodeServer.renderError',
renderErrorToHTML = 'NextNodeServer.renderErrorToHTML',
render404 = 'NextNodeServer.render404',
startResponse = 'NextNodeServer.startResponse',

// nested inner span, does not require parent scope name
route = 'route',
Expand Down Expand Up @@ -132,6 +133,7 @@ export const NextVanillaSpanAllowlist = [
NextNodeServerSpan.createComponentTree,
NextNodeServerSpan.findPageComponents,
NextNodeServerSpan.getLayoutOrPageModule,
NextNodeServerSpan.startResponse,
]

export {
Expand Down
9 changes: 9 additions & 0 deletions packages/next/src/server/pipe-readable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
createAbortController,
} from './web/spec-extension/adapters/next-request'
import { DetachedPromise } from '../lib/detached-promise'
import { getTracer } from './lib/trace/tracer'
import { NextNodeServerSpan } from './lib/trace/constants'

export function isAbortError(e: any): e is Error & { name: 'AbortError' } {
return e?.name === 'AbortError' || e?.name === ResponseAbortedName
Expand Down Expand Up @@ -47,6 +49,13 @@ function createWriterFromResponse(
if (!started) {
started = true
res.flushHeaders()
getTracer().trace(
NextNodeServerSpan.startResponse,
{
spanName: 'start response',
},
() => undefined
)
}

try {
Expand Down
51 changes: 45 additions & 6 deletions test/e2e/opentelemetry/opentelemetry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ createNextDescribe(
const numberOfRootTraces =
env.span.rootParentId === undefined ? 1 : 0
const traces = await getSanitizedTraces(numberOfRootTraces)
if (traces.length < 9) {
return `not enough traces, expected 9, but got ${traces.length}`
if (traces.length < 10) {
return `not enough traces, expected 10, but got ${traces.length}`
}
expect(traces).toMatchInlineSnapshot(`
[
Expand Down Expand Up @@ -226,6 +226,19 @@ createNextDescribe(
},
"traceId": "${env.span.traceId}",
},
{
"attributes": {
"next.span_name": "start response",
"next.span_type": "NextNodeServer.startResponse",
},
"kind": 0,
"name": "start response",
"parentId": "[parent-id]",
"status": {
"code": 0,
},
"traceId": "${env.span.traceId}",
},
{
"attributes": {
"next.page": "/app/[param]/layout",
Expand Down Expand Up @@ -267,8 +280,8 @@ createNextDescribe(
const numberOfRootTraces =
env.span.rootParentId === undefined ? 1 : 0
const traces = await getSanitizedTraces(numberOfRootTraces)
if (traces.length < 3) {
return `not enough traces, expected 3, but got ${traces.length}`
if (traces.length < 4) {
return `not enough traces, expected 4, but got ${traces.length}`
}
expect(traces).toMatchInlineSnapshot(`
[
Expand Down Expand Up @@ -322,6 +335,19 @@ createNextDescribe(
},
"traceId": "${env.span.traceId}",
},
{
"attributes": {
"next.span_name": "start response",
"next.span_type": "NextNodeServer.startResponse",
},
"kind": 0,
"name": "start response",
"parentId": "[parent-id]",
"status": {
"code": 0,
},
"traceId": "${env.span.traceId}",
},
]
`)
return 'success'
Expand Down Expand Up @@ -638,8 +664,8 @@ createNextDescribe(

await check(async () => {
const traces = await getSanitizedTraces(1)
if (traces.length < 5) {
return `not enough traces, expected 5, but got ${traces.length}`
if (traces.length < 6) {
return `not enough traces, expected 6, but got ${traces.length}`
}
expect(traces).toMatchInlineSnapshot(`
[
Expand Down Expand Up @@ -730,6 +756,19 @@ createNextDescribe(
},
"traceId": "[trace-id]",
},
{
"attributes": {
"next.span_name": "start response",
"next.span_type": "NextNodeServer.startResponse",
},
"kind": 0,
"name": "start response",
"parentId": "[parent-id]",
"status": {
"code": 0,
},
"traceId": "[trace-id]",
},
{
"attributes": {
"next.page": "/app/[param]/layout",
Expand Down

0 comments on commit 6eb2c24

Please sign in to comment.