• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

supabase / storage / 29402487719

15 Jul 2026 08:54AM UTC coverage: 50.02% (-29.4%) from 79.38%
29402487719

Pull #1235

github

web-flow
Merge 379605bf4 into 9434eb79c
Pull Request #1235: fix: hoist regex and num parser in x-robots-tag

3416 of 7349 branches covered (46.48%)

Branch coverage included in aggregate %.

6 of 6 new or added lines in 1 file covered. (100.0%)

3851 existing lines in 164 files now uncovered.

6535 of 12545 relevant lines covered (52.09%)

63.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.59
/src/http/plugins/log-request.ts
1
import {
2
  getValidTraceparent,
3
  logSchema,
4
  serializeReplyLog,
5
  serializeRequestLog,
6
} from '@internal/monitoring'
7
import type { FastifyReply, FastifyRequest } from 'fastify'
8
import fastifyPlugin from 'fastify-plugin'
9

10
interface RequestLoggerOptions {
11
  excludeUrls?: Set<string>
12
}
13

14
// Fixed positions in a validated W3C traceparent value.
15
const TRACE_ID_START = 3
7✔
16
const TRACE_ID_END = 35
7✔
17
const SPAN_ID_START = 36
7✔
18
const SPAN_ID_END = 52
7✔
19

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

24
declare module 'http' {
25
  interface IncomingMessage {
26
    executionError?: Error
27
    resources?: string[]
28
  }
29
}
30

31
declare module 'fastify' {
32
  interface FastifyRequest {
33
    executionError?: Error
34
    operation?: string
35
    resources?: string[]
36
    startTime: number
37
    executionTime?: number
38
  }
39

40
  interface FastifyContextConfig {
41
    operation?: string
42
    resources?: BivariantHandler<[req: FastifyRequest], string[]>
43
    logMetadata?: BivariantHandler<[req: FastifyRequest], Record<string, unknown>>
44
  }
45
}
46

47
/**
48
 * Request logger plugin
49
 * @param options
50
 */
51
export const logRequest = (options: RequestLoggerOptions) =>
17✔
52
  fastifyPlugin(
53
    async (fastify) => {
54
      const excludeUrls = options.excludeUrls?.size ? options.excludeUrls : undefined
17✔
55

56
      fastify.addHook('onRequest', (req, res, done) => {
17✔
57
        req.startTime = performance.now()
16✔
58

59
        if (excludeUrls?.has(req.url)) {
16✔
60
          done()
2✔
61
          return
2✔
62
        }
63

64
        res.raw.once('close', () => {
14✔
65
          if (req.raw.aborted) {
14!
UNCOV
66
            doRequestLog(req, {
×
67
              statusCode: 'ABORTED REQ',
68
              responseTime: performance.now() - req.startTime,
69
            })
UNCOV
70
            return
×
71
          }
72

73
          if (!res.raw.writableFinished) {
14!
74
            doRequestLog(req, {
14✔
75
              statusCode: 'ABORTED RES',
76
              responseTime: performance.now() - req.startTime,
77
            })
78
          }
79
        })
80
        done()
14✔
81
      })
82

83
      /**
84
       * Adds req.resources and req.operation to the request object
85
       */
86
      fastify.addHook('preHandler', (req, _reply, done) => {
17✔
87
        let resources = req.resources
14✔
88

89
        if (resources === undefined) {
14✔
90
          resources = req.routeOptions.config.resources?.(req)
13✔
91
        }
92

93
        if (resources === undefined) {
14✔
94
          resources = req.raw.resources
12✔
95
        }
96

97
        if (resources === undefined) {
14✔
98
          const resourceFromParams = getResourceFromParams(req.params)
12✔
99
          resources = resourceFromParams ? [resourceFromParams] : []
12✔
100
        }
101

102
        if (resources && resources.length > 0) {
14✔
103
          for (let index = 0; index < resources.length; index++) {
4✔
104
            const resource = resources[index]
5✔
105
            if (!resource.startsWith('/')) {
5✔
106
              resources[index] = `/${resource}`
3✔
107
            }
108
          }
109
        }
110

111
        req.resources = resources
14✔
112
        req.operation = req.routeOptions.config.operation
14✔
113

114
        if (req.operation && typeof req.opentelemetry === 'function') {
14!
115
          req.opentelemetry()?.span?.setAttribute('http.operation', req.operation)
×
116
        }
117
        done()
14✔
118
      })
119

120
      fastify.addHook('onSend', (req, _reply, payload, done) => {
17✔
121
        req.executionTime = Math.round(performance.now() - req.startTime)
16✔
122
        done(null, payload)
16✔
123
      })
124

125
      fastify.addHook('onResponse', (req, reply, done) => {
17✔
126
        if (excludeUrls?.has(req.url)) {
16✔
127
          done()
2✔
128
          return
2✔
129
        }
130

131
        doRequestLog(req, {
14✔
132
          reply,
133
          statusCode: reply.statusCode,
134
          responseTime: reply.elapsedTime,
135
          executionTime: req.executionTime,
136
        })
137
        done()
14✔
138
      })
139
    },
140
    { name: 'log-request' }
141
  )
142

143
interface LogRequestOptions {
144
  reply?: FastifyReply
145
  statusCode: number | 'ABORTED REQ' | 'ABORTED RES'
146
  responseTime: number
147
  executionTime?: number
148
}
149

150
function doRequestLog(req: FastifyRequest, options: LogRequestOptions) {
151
  const requestLog = serializeRequestLog(req)
28✔
152
  const replyLog = serializeReplyLog(options.reply)
28✔
153
  const rMeth = requestLog.method
28✔
154
  const rUrl = requestLog.url
28✔
155
  const uAgent = req.headers['user-agent']
28✔
156
  const rId = req.id
28✔
157
  const cIP = req.ip
28✔
158
  const statusCode = options.statusCode
28✔
159
  const error = req.raw.executionError || req.executionError
28✔
160
  const tenantId = req.tenantId
28✔
161
  const traceparent = getValidTraceparent(req.headers)
28✔
162
  const traceId = traceparent?.slice(TRACE_ID_START, TRACE_ID_END) ?? ''
28✔
163
  const spanId = traceparent?.slice(SPAN_ID_START, SPAN_ID_END) ?? ''
28✔
164

165
  let reqMetadata = '{}'
28✔
166

167
  if (req.routeOptions.config.logMetadata) {
28✔
168
    try {
2✔
169
      const metadata = req.routeOptions.config.logMetadata(req)
2✔
170

171
      if (metadata) {
2!
172
        try {
2✔
173
          reqMetadata = JSON.stringify(metadata)
2✔
174

175
          if (typeof req.opentelemetry === 'function') {
2!
176
            req.opentelemetry()?.span?.setAttribute('http.metadata', reqMetadata)
×
177
          }
178
        } catch (e) {
179
          // do nothing
180
          logSchema.warning(req.log, 'Failed to serialize log metadata', {
×
181
            type: 'otel',
182
            tenantId,
183
            project: tenantId,
184
            reqId: rId,
185
            sbReqId: req.sbReqId,
186
            error: e,
187
          })
188
        }
189
      }
190
    } catch (e) {
191
      logSchema.error(req.log, 'Failed to get log metadata', {
×
192
        type: 'request',
193
        tenantId,
194
        project: tenantId,
195
        reqId: rId,
196
        sbReqId: req.sbReqId,
197
        error: e,
198
      })
199
    }
200
  }
201

202
  const buildLogMessage = `${tenantId} | ${rMeth} | ${statusCode} | ${cIP} | ${rId} | ${rUrl} | ${uAgent}`
28✔
203

204
  logSchema.request(req.log, buildLogMessage, {
28✔
205
    type: 'request',
206
    tenantId,
207
    project: tenantId,
208
    reqId: rId,
209
    sbReqId: req.sbReqId,
210
    traceId,
211
    spanId,
212
    req: requestLog,
213
    reqMetadata,
214
    res: replyLog,
215
    responseTime: options.responseTime,
216
    executionTime: options.executionTime,
217
    error,
218
    owner: req.owner,
219
    role: req.jwtPayload?.role,
220
    resources: req.resources,
221
    operation: req.operation ?? req.routeOptions.config.operation,
56✔
222
    serverTimes: req.serverTimings,
223
  })
224
}
225

226
function getResourceFromParams(params: unknown): string {
227
  if (!params || typeof params !== 'object') {
12!
228
    return ''
×
229
  }
230

231
  let resource = ''
12✔
232
  let first = true
12✔
233

234
  for (const key in params) {
12✔
235
    if (!Object.prototype.hasOwnProperty.call(params, key)) {
3!
236
      continue
×
237
    }
238

239
    if (!first) {
3✔
240
      resource += '/'
1✔
241
    }
242

243
    const value = (params as Record<string, unknown>)[key]
3✔
244
    resource += value == null ? '' : String(value)
3!
245
    first = false
3✔
246
  }
247

248
  return resource
12✔
249
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc