Skip to content

Commit d432f7d

Browse files
authored
fix: log span_id next to trace_id (#1225)
Signed-off-by: ferhat elmas <elmas.ferhat@gmail.com>
1 parent d01632d commit d432f7d

7 files changed

Lines changed: 36 additions & 18 deletions

File tree

src/http/plugins/log-request.test.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ describe('log-request plugin', () => {
111111
expect(requestLogLine).not.toContain('"request_id"')
112112
})
113113

114-
it('threads traceId from a valid traceparent header into the request log data', async () => {
114+
it('threads traceId and spanId from a valid traceparent header into the request log data', async () => {
115115
const response = await app.inject({
116116
method: 'GET',
117117
url: '/request-log',
@@ -127,10 +127,12 @@ describe('log-request plugin', () => {
127127

128128
const requestLog = JSON.parse(requestLogLine ?? '{}')
129129
expect(requestLog.traceId).toBe('4bf92f3577b34da6a3ce929d0e0e4736')
130+
expect(requestLog.spanId).toBe('00f067aa0ba902b7')
130131
expect(requestLog).not.toHaveProperty('trace_id')
132+
expect(requestLog).not.toHaveProperty('span_id')
131133
})
132134

133-
it('logs an empty traceId when traceparent is malformed', async () => {
135+
it('logs empty traceId and spanId values when traceparent is malformed', async () => {
134136
const response = await app.inject({
135137
method: 'GET',
136138
url: '/request-log',
@@ -146,10 +148,12 @@ describe('log-request plugin', () => {
146148

147149
const requestLog = JSON.parse(requestLogLine ?? '{}')
148150
expect(requestLog.traceId).toBe('')
151+
expect(requestLog.spanId).toBe('')
149152
expect(requestLog).not.toHaveProperty('trace_id')
153+
expect(requestLog).not.toHaveProperty('span_id')
150154
})
151155

152-
it('logs an empty traceId when traceparent is missing', async () => {
156+
it('logs empty traceId and spanId values when traceparent is missing', async () => {
153157
const response = await app.inject({
154158
method: 'GET',
155159
url: '/request-log',
@@ -162,7 +166,9 @@ describe('log-request plugin', () => {
162166

163167
const requestLog = JSON.parse(requestLogLine ?? '{}')
164168
expect(requestLog.traceId).toBe('')
169+
expect(requestLog.spanId).toBe('')
165170
expect(requestLog).not.toHaveProperty('trace_id')
171+
expect(requestLog).not.toHaveProperty('span_id')
166172
})
167173

168174
it('threads tenant context into the request log data', async () => {

src/http/plugins/log-request.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {
2-
getTraceIdFromTraceparent,
2+
getValidTraceparent,
33
logSchema,
44
serializeReplyLog,
55
serializeRequestLog,
@@ -11,6 +11,12 @@ interface RequestLoggerOptions {
1111
excludeUrls?: Set<string>
1212
}
1313

14+
// Fixed positions in a validated W3C traceparent value.
15+
const TRACE_ID_START = 3
16+
const TRACE_ID_END = 35
17+
const SPAN_ID_START = 36
18+
const SPAN_ID_END = 52
19+
1420
type BivariantHandler<Args extends unknown[], Return> = {
1521
bivarianceHack(...args: Args): Return
1622
}['bivarianceHack']
@@ -152,7 +158,9 @@ function doRequestLog(req: FastifyRequest, options: LogRequestOptions) {
152158
const statusCode = options.statusCode
153159
const error = req.raw.executionError || req.executionError
154160
const tenantId = req.tenantId
155-
const traceId = getTraceIdFromTraceparent(req.headers) ?? ''
161+
const traceparent = getValidTraceparent(req.headers)
162+
const traceId = traceparent?.slice(TRACE_ID_START, TRACE_ID_END) ?? ''
163+
const spanId = traceparent?.slice(SPAN_ID_START, SPAN_ID_END) ?? ''
156164

157165
let reqMetadata = '{}'
158166

@@ -200,6 +208,7 @@ function doRequestLog(req: FastifyRequest, options: LogRequestOptions) {
200208
reqId: rId,
201209
sbReqId: req.sbReqId,
202210
traceId,
211+
spanId,
203212
req: requestLog,
204213
reqMetadata,
205214
res: replyLog,

src/internal/monitoring/logflare.test.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ describe('logflare helpers', () => {
2020
vi.resetModules()
2121
})
2222

23-
it('promotes project, sbReqId, and traceId onto the prepared payload', async () => {
23+
it('promotes project, sbReqId, traceId, and spanId onto the prepared payload', async () => {
2424
defaultPreparePayloadMock.mockReturnValue({
2525
log_entry: 'hello',
2626
metadata: { level: 'info' },
@@ -32,6 +32,7 @@ describe('logflare helpers', () => {
3232
project: 'tenant-a',
3333
sbReqId: 'sb-req-123',
3434
traceId: '4bf92f3577b34da6a3ce929d0e0e4736',
35+
spanId: '00f067aa0ba902b7',
3536
msg: { text: 'hello' },
3637
}
3738

@@ -41,11 +42,12 @@ describe('logflare helpers', () => {
4142
project: 'tenant-a',
4243
request_id: 'sb-req-123',
4344
trace_id: '4bf92f3577b34da6a3ce929d0e0e4736',
45+
span_id: '00f067aa0ba902b7',
4446
})
4547
expect(defaultPreparePayloadMock).toHaveBeenCalledWith(payload, meta)
4648
})
4749

48-
it('leaves project, request_id, and trace_id undefined when they are missing', async () => {
50+
it('leaves project, request_id, trace_id, and span_id undefined when they are missing', async () => {
4951
defaultPreparePayloadMock.mockReturnValue({
5052
log_entry: 'hello',
5153
metadata: {},
@@ -62,6 +64,7 @@ describe('logflare helpers', () => {
6264
project: undefined,
6365
request_id: undefined,
6466
trace_id: undefined,
67+
span_id: undefined,
6568
})
6669
})
6770

src/internal/monitoring/logflare.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export function onPreparePayload(payload: Record<string, unknown>, meta: Payload
1414
item.project = payload.project
1515
item.request_id = payload.sbReqId
1616
item.trace_id = payload.traceId
17+
item.span_id = payload.spanId
1718
return item
1819
}
1920

src/internal/monitoring/logger.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ export interface RequestLogContext {
139139
export interface RequestLog extends RequestLogContext {
140140
type: 'request'
141141
traceId: string
142+
spanId: string
142143
req: SerializedRequestLog
143144
res?: SerializedReplyLog
144145
reqMetadata: string

src/internal/monitoring/request-context.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {
22
getSbReqId,
33
getSbReqIdFromPayload,
4-
getTraceIdFromTraceparent,
4+
getValidTraceparent,
55
SUPABASE_REQUEST_ID_HEADER,
66
TRACEPARENT_HEADER,
77
} from './request-context'
@@ -49,19 +49,19 @@ describe('request log context helpers', () => {
4949
expect(getSbReqIdFromPayload(undefined)).toBeUndefined()
5050
})
5151

52-
it('extracts trace ids from valid traceparent headers', () => {
52+
it('returns valid traceparent headers', () => {
5353
expect(
54-
getTraceIdFromTraceparent({
54+
getValidTraceparent({
5555
[TRACEPARENT_HEADER]: '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01',
5656
})
57-
).toBe('4bf92f3577b34da6a3ce929d0e0e4736')
57+
).toBe('00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01')
5858

5959
expect(
60-
getTraceIdFromTraceparent({
60+
getValidTraceparent({
6161
[TRACEPARENT_HEADER]:
6262
'01-fbf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00-future-field',
6363
})
64-
).toBe('fbf92f3577b34da6a3ce929d0e0e4736')
64+
).toBe('01-fbf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00-future-field')
6565
})
6666

6767
it.each([
@@ -88,7 +88,7 @@ describe('request log context helpers', () => {
8888
['all-zero parent id', '00-4bf92f3577b34da6a3ce929d0e0e4736-0000000000000000-01'],
8989
])('ignores %s traceparent headers', (_name, traceparent) => {
9090
expect(
91-
getTraceIdFromTraceparent({
91+
getValidTraceparent({
9292
[TRACEPARENT_HEADER]: traceparent,
9393
})
9494
).toBeUndefined()

src/internal/monitoring/request-context.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,14 @@ export const TRACEPARENT_HEADER = 'traceparent'
88
const TRACEPARENT_PATTERN =
99
/^(?!ff)[\da-f]{2}-(?!0{32})[\da-f]{32}-(?!0{16})[\da-f]{16}-[\da-f]{2}(?:-.*)?$/
1010
const TRACEPARENT_V0_LENGTH = 55
11-
const TRACE_ID_START = 3
12-
const TRACE_ID_END = 35
1311

1412
export function getSbReqId(headers: IncomingHttpHeaders): string | undefined {
1513
const sbReqId = headers[SUPABASE_REQUEST_ID_HEADER]
1614

1715
return getNonEmptyString(sbReqId)
1816
}
1917

20-
export function getTraceIdFromTraceparent(headers: IncomingHttpHeaders): string | undefined {
18+
export function getValidTraceparent(headers: IncomingHttpHeaders): string | undefined {
2119
const traceparent = headers[TRACEPARENT_HEADER]
2220

2321
if (
@@ -28,7 +26,7 @@ export function getTraceIdFromTraceparent(headers: IncomingHttpHeaders): string
2826
return undefined
2927
}
3028

31-
return traceparent.slice(TRACE_ID_START, TRACE_ID_END)
29+
return traceparent
3230
}
3331

3432
export function getSbReqIdFromPayload(payload: unknown): string | undefined {

0 commit comments

Comments
 (0)