Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/admin-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const build = (opts: buildOpts = {}): FastifyInstance => {
})
}

app.register(plugins.requestContext)
app.register(plugins.signals)
app.register(plugins.adminTenantId)
app.register(plugins.logRequest({ excludeUrls: ['/status', '/metrics', '/health', '/version'] }))
Expand Down
1 change: 1 addition & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ const build = (opts: buildOpts = {}): FastifyInstance => {
app.addSchema(schemas.authSchema)
app.addSchema(schemas.errorSchema)

app.register(plugins.requestContext)
app.register(plugins.signals)
app.register(plugins.tenantId)
app.register(
Expand Down
6 changes: 6 additions & 0 deletions src/http/plugins/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export const db = fastifyPlugin(
logSchema.error(request.log, 'Error disposing db connection', {
type: 'db-connection',
error: e,
sbReqId: request.sbReqId,
})
})
}
Expand All @@ -80,6 +81,7 @@ export const db = fastifyPlugin(
logSchema.error(request.log, 'Error disposing db connection', {
type: 'db-connection',
error: e,
sbReqId: request.sbReqId,
})
})
}
Expand All @@ -91,6 +93,7 @@ export const db = fastifyPlugin(
logSchema.error(request.log, 'Error disposing db connection', {
type: 'db-connection',
error: e,
sbReqId: request.sbReqId,
})
})
}
Expand Down Expand Up @@ -137,6 +140,7 @@ export const dbSuperUser = fastifyPlugin<DbSuperUserPluginOptions>(
logSchema.error(request.log, 'Error disposing db connection', {
type: 'db-connection',
error: e,
sbReqId: request.sbReqId,
})
})
}
Expand All @@ -150,6 +154,7 @@ export const dbSuperUser = fastifyPlugin<DbSuperUserPluginOptions>(
logSchema.error(request.log, 'Error disposing db connection', {
type: 'db-connection',
error: e,
sbReqId: request.sbReqId,
})
})
}
Expand All @@ -161,6 +166,7 @@ export const dbSuperUser = fastifyPlugin<DbSuperUserPluginOptions>(
logSchema.error(request.log, 'Error disposing db connection', {
type: 'db-connection',
error: e,
sbReqId: request.sbReqId,
})
})
}
Expand Down
1 change: 1 addition & 0 deletions src/http/plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export * from './iceberg'
export * from './jwt'
export * from './log-request'
export * from './metrics'
export * from './request-context'
export * from './signals'
export * from './signature-v4'
export * from './storage'
Expand Down
50 changes: 47 additions & 3 deletions src/http/plugins/log-request.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,38 @@
import Fastify, { FastifyInstance } from 'fastify'
import { Writable } from 'node:stream'
import Fastify from 'fastify'
import pino from 'pino'
import { logRequest } from './log-request'
import { requestContext } from './request-context'

function createApp(lines: string[]) {
return Fastify({
disableRequestLogging: true,
loggerInstance: pino(
{ level: 'info' },
new Writable({
write(chunk, _encoding, callback) {
lines.push(chunk.toString())
callback()
},
})
),
})
}

describe('log-request plugin', () => {
let app: FastifyInstance
let app: ReturnType<typeof createApp>
let lines: string[]

beforeEach(async () => {
app = Fastify()
lines = []
app = createApp(lines)

await app.register(requestContext)
await app.register(logRequest({}))

app.get('/request-log', async () => {
return { ok: true }
})
})

afterEach(async () => {
Expand Down Expand Up @@ -56,4 +82,22 @@ describe('log-request plugin', () => {
resources: ['/bucket/demo', '/object/demo'],
})
})

it('threads sbReqId into the request log data', async () => {
const response = await app.inject({
method: 'GET',
url: '/request-log',
headers: {
'sb-request-id': 'sb-req-123',
},
})

expect(response.statusCode).toBe(200)

const requestLogLine = lines.find((line) => line.includes('"type":"request"'))

expect(requestLogLine).toBeDefined()
expect(requestLogLine).toContain('"sbReqId":"sb-req-123"')
expect(requestLogLine).not.toContain('"request_id"')
})
})
31 changes: 22 additions & 9 deletions src/http/plugins/log-request.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import { logger, logSchema, redactQueryParamFromRequest } from '@internal/monitoring'
import { FastifyReply } from 'fastify/types/reply'
import { FastifyRequest } from 'fastify/types/request'
import { logSchema, redactQueryParamFromRequest } from '@internal/monitoring'
import type { FastifyReply, FastifyRequest } from 'fastify'
import fastifyPlugin from 'fastify-plugin'

interface RequestLoggerOptions {
excludeUrls?: string[]
}

type BivariantHandler<Args extends unknown[], Return> = {
bivarianceHack(...args: Args): Return
}['bivarianceHack']

declare module 'http' {
interface IncomingMessage {
executionError?: Error
resources?: string[]
}
}

declare module 'fastify' {
interface FastifyRequest {
executionError?: Error
Expand All @@ -18,8 +28,8 @@ declare module 'fastify' {

interface FastifyContextConfig {
operation?: { type: string }
resources?: (req: FastifyRequest<any>) => string[]
logMetadata?: (req: FastifyRequest<any>) => Record<string, unknown>
resources?: BivariantHandler<[req: FastifyRequest], string[]>
logMetadata?: BivariantHandler<[req: FastifyRequest], Record<string, unknown>>
}
}

Expand Down Expand Up @@ -64,7 +74,7 @@ export const logRequest = (options: RequestLoggerOptions) =>
}

if (resources === undefined) {
resources = (req.raw as any).resources
resources = req.raw.resources
}

if (resources === undefined) {
Expand Down Expand Up @@ -150,7 +160,7 @@ function doRequestLog(req: FastifyRequest, options: LogRequestOptions) {
const rId = req.id
const cIP = req.ip
const statusCode = options.statusCode
const error = (req.raw as any).executionError || req.executionError
const error = req.raw.executionError || req.executionError
const tenantId = req.tenantId

let reqMetadata: Record<string, unknown> = {}
Expand All @@ -166,16 +176,18 @@ function doRequestLog(req: FastifyRequest, options: LogRequestOptions) {
}
} catch (e) {
// do nothing
logSchema.warning(logger, 'Failed to serialize log metadata', {
logSchema.warning(req.log, 'Failed to serialize log metadata', {
type: 'otel',
error: e,
sbReqId: req.sbReqId,
})
}
}
} catch (e) {
logSchema.error(logger, 'Failed to get log metadata', {
logSchema.error(req.log, 'Failed to get log metadata', {
type: 'request',
error: e,
sbReqId: req.sbReqId,
})
}
}
Expand All @@ -195,5 +207,6 @@ function doRequestLog(req: FastifyRequest, options: LogRequestOptions) {
resources: req.resources,
operation: req.operation?.type ?? req.routeOptions.config.operation?.type,
serverTimes: req.serverTimings,
sbReqId: req.sbReqId,
})
}
37 changes: 37 additions & 0 deletions src/http/plugins/request-context.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import Fastify, { FastifyInstance } from 'fastify'
import { requestContext } from './request-context'

describe('request-context plugin', () => {
let app: FastifyInstance

beforeEach(async () => {
app = Fastify()
await app.register(requestContext)
})

afterEach(async () => {
await app.close()
})

it('extracts sb-request-id from the header onto the request', async () => {
app.get('/context', async (request) => ({ sbReqId: request.sbReqId }))

const response = await app.inject({
method: 'GET',
url: '/context',
headers: { 'sb-request-id': 'sb-req-123' },
})

expect(response.statusCode).toBe(200)
expect(response.json()).toEqual({ sbReqId: 'sb-req-123' })
})

it('leaves sbReqId undefined when the header is absent', async () => {
app.get('/context', async (request) => ({ sbReqId: request.sbReqId ?? null }))

const response = await app.inject({ method: 'GET', url: '/context' })

expect(response.statusCode).toBe(200)
expect(response.json()).toEqual({ sbReqId: null })
})
})
18 changes: 18 additions & 0 deletions src/http/plugins/request-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { getSbReqId } from '@internal/monitoring'
import fastifyPlugin from 'fastify-plugin'

declare module 'fastify' {
interface FastifyRequest {
sbReqId?: string
}
}

export const requestContext = fastifyPlugin(
async (fastify) => {
fastify.decorateRequest('sbReqId', undefined)
fastify.addHook('onRequest', async (request) => {
request.sbReqId = getSbReqId(request.headers)
})
Comment thread
ferhatelmas marked this conversation as resolved.
},
{ name: 'request-context' }
)
1 change: 1 addition & 0 deletions src/http/plugins/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const storage = fastifyPlugin(
tenantId: request.tenantId,
host: request.headers['x-forwarded-host'] as string,
reqId: request.id,
sbReqId: request.sbReqId,
latestMigration: request.latestMigration,
})

Expand Down
6 changes: 5 additions & 1 deletion src/http/plugins/tracing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ export const tracing = fastifyPlugin(
}
}
} catch (e) {
logSchema.error(request.log, 'failed setting tracing mode', { error: e, type: 'tracing' })
logSchema.error(request.log, 'failed setting tracing mode', {
error: e,
type: 'tracing',
sbReqId: request.sbReqId,
})
}
})
},
Expand Down
10 changes: 7 additions & 3 deletions src/http/routes/admin/jwks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,9 @@ export default async function routes(fastify: FastifyInstance) {
tenantId,
tenant: {
ref: tenantId,
Comment thread
ferhatelmas marked this conversation as resolved.
host: '',
},
sbReqId: request.sbReqId,
})

return reply.send({ started: true })
Expand All @@ -162,12 +164,14 @@ export default async function routes(fastify: FastifyInstance) {
.send(`Generate missing jwks is already running, and has sent ${sent} items so far`)
}

UrlSigningJwkGenerator.generateUrlSigningJwksOnAllTenants(
request.signals.disconnect.signal
).catch((e) => {
UrlSigningJwkGenerator.generateUrlSigningJwksOnAllTenants({
sbReqId: request.sbReqId,
signal: request.signals.disconnect.signal,
}).catch((e) => {
logSchema.error(request.log, 'Error generating url signing jwks for all tenants', {
type: 'jwk-generator',
error: e,
sbReqId: request.sbReqId,
})
})
return reply.send({ started: true })
Expand Down
Loading