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

supabase / storage / 28511119445

01 Jul 2026 10:30AM UTC coverage: 47.871% (-31.0%) from 78.826%
28511119445

Pull #1190

github

web-flow
Merge 1a4de70c1 into be0f7c90f
Pull Request #1190: Update wattpm to 3.59.0

3091 of 6992 branches covered (44.21%)

Branch coverage included in aggregate %.

6025 of 12051 relevant lines covered (50.0%)

62.32 hits per line

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

83.76
/src/http/plugins/log-request.ts
1
import { logSchema, serializeReplyLog, serializeRequestLog } from '@internal/monitoring'
2
import type { FastifyReply, FastifyRequest } from 'fastify'
3
import fastifyPlugin from 'fastify-plugin'
4

5
interface RequestLoggerOptions {
6
  excludeUrls?: string[]
7
}
8

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

13
declare module 'http' {
14
  interface IncomingMessage {
15
    executionError?: Error
16
    resources?: string[]
17
  }
18
}
19

20
declare module 'fastify' {
21
  interface FastifyRequest {
22
    executionError?: Error
23
    operation?: { type: string }
24
    resources?: string[]
25
    startTime: number
26
    executionTime?: number
27
  }
28

29
  interface FastifyContextConfig {
30
    operation?: { type: string }
31
    resources?: BivariantHandler<[req: FastifyRequest], string[]>
32
    logMetadata?: BivariantHandler<[req: FastifyRequest], Record<string, unknown>>
33
  }
34
}
35

36
/**
37
 * Request logger plugin
38
 * @param options
39
 */
40
export const logRequest = (options: RequestLoggerOptions) =>
11✔
41
  fastifyPlugin(
42
    async (fastify) => {
43
      fastify.addHook('onRequest', (req, res, done) => {
11✔
44
        req.startTime = Date.now()
10✔
45

46
        res.raw.once('close', () => {
10✔
47
          if (req.raw.aborted) {
10!
48
            doRequestLog(req, {
×
49
              excludeUrls: options.excludeUrls,
50
              statusCode: 'ABORTED REQ',
51
              responseTime: (Date.now() - req.startTime) / 1000,
52
            })
53
            return
×
54
          }
55

56
          if (!res.raw.writableFinished) {
10!
57
            doRequestLog(req, {
10✔
58
              excludeUrls: options.excludeUrls,
59
              statusCode: 'ABORTED RES',
60
              responseTime: (Date.now() - req.startTime) / 1000,
61
            })
62
          }
63
        })
64
        done()
10✔
65
      })
66

67
      /**
68
       * Adds req.resources and req.operation to the request object
69
       */
70
      fastify.addHook('preHandler', (req, _reply, done) => {
11✔
71
        let resources = req.resources
9✔
72

73
        if (resources === undefined) {
9✔
74
          resources = req.routeOptions.config.resources?.(req)
8✔
75
        }
76

77
        if (resources === undefined) {
9✔
78
          resources = req.raw.resources
7✔
79
        }
80

81
        if (resources === undefined) {
9✔
82
          const resourceFromParams = getResourceFromParams(req.params)
7✔
83
          resources = resourceFromParams ? [resourceFromParams] : []
7✔
84
        }
85

86
        if (resources && resources.length > 0) {
9✔
87
          for (let index = 0; index < resources.length; index++) {
4✔
88
            const resource = resources[index]
5✔
89
            if (!resource.startsWith('/')) {
5✔
90
              resources[index] = `/${resource}`
3✔
91
            }
92
          }
93
        }
94

95
        req.resources = resources
9✔
96
        req.operation = req.routeOptions.config.operation
9✔
97

98
        if (req.operation && typeof req.opentelemetry === 'function') {
9!
99
          req.opentelemetry()?.span?.setAttribute('http.operation', req.operation.type)
×
100
        }
101
        done()
9✔
102
      })
103

104
      fastify.addHook('onSend', (req, _reply, payload, done) => {
11✔
105
        req.executionTime = Date.now() - req.startTime
10✔
106
        done(null, payload)
10✔
107
      })
108

109
      fastify.addHook('onResponse', (req, reply, done) => {
11✔
110
        doRequestLog(req, {
10✔
111
          reply,
112
          excludeUrls: options.excludeUrls,
113
          statusCode: reply.statusCode,
114
          responseTime: reply.elapsedTime,
115
          executionTime: req.executionTime,
116
        })
117
        done()
10✔
118
      })
119
    },
120
    { name: 'log-request' }
121
  )
122

123
interface LogRequestOptions {
124
  reply?: FastifyReply
125
  excludeUrls?: string[]
126
  statusCode: number | 'ABORTED REQ' | 'ABORTED RES'
127
  responseTime: number
128
  executionTime?: number
129
}
130

131
function doRequestLog(req: FastifyRequest, options: LogRequestOptions) {
132
  if (options.excludeUrls?.includes(req.url)) {
20!
133
    return
×
134
  }
135

136
  const requestLog = serializeRequestLog(req)
20✔
137
  const replyLog = serializeReplyLog(options.reply)
20✔
138
  const rMeth = requestLog.method
20✔
139
  const rUrl = requestLog.url
20✔
140
  const uAgent = req.headers['user-agent']
20✔
141
  const rId = req.id
20✔
142
  const cIP = req.ip
20✔
143
  const statusCode = options.statusCode
20✔
144
  const error = req.raw.executionError || req.executionError
20✔
145
  const tenantId = req.tenantId
20✔
146

147
  let reqMetadata = '{}'
20✔
148

149
  if (req.routeOptions.config.logMetadata) {
20✔
150
    try {
2✔
151
      const metadata = req.routeOptions.config.logMetadata(req)
2✔
152

153
      if (metadata) {
2!
154
        try {
2✔
155
          reqMetadata = JSON.stringify(metadata)
2✔
156

157
          if (typeof req.opentelemetry === 'function') {
2!
158
            req.opentelemetry()?.span?.setAttribute('http.metadata', reqMetadata)
×
159
          }
160
        } catch (e) {
161
          // do nothing
162
          logSchema.warning(req.log, 'Failed to serialize log metadata', {
×
163
            type: 'otel',
164
            tenantId,
165
            project: tenantId,
166
            reqId: rId,
167
            sbReqId: req.sbReqId,
168
            error: e,
169
          })
170
        }
171
      }
172
    } catch (e) {
173
      logSchema.error(req.log, 'Failed to get log metadata', {
×
174
        type: 'request',
175
        tenantId,
176
        project: tenantId,
177
        reqId: rId,
178
        sbReqId: req.sbReqId,
179
        error: e,
180
      })
181
    }
182
  }
183

184
  const buildLogMessage = `${tenantId} | ${rMeth} | ${statusCode} | ${cIP} | ${rId} | ${rUrl} | ${uAgent}`
20✔
185

186
  logSchema.request(req.log, buildLogMessage, {
20✔
187
    type: 'request',
188
    tenantId,
189
    project: tenantId,
190
    reqId: rId,
191
    sbReqId: req.sbReqId,
192
    req: requestLog,
193
    reqMetadata,
194
    res: replyLog,
195
    responseTime: options.responseTime,
196
    executionTime: options.executionTime,
197
    error,
198
    owner: req.owner,
199
    role: req.jwtPayload?.role,
200
    resources: req.resources,
201
    operation: req.operation?.type ?? req.routeOptions.config.operation?.type,
40✔
202
    serverTimes: req.serverTimings,
203
  })
204
}
205

206
function getResourceFromParams(params: unknown): string {
207
  if (!params || typeof params !== 'object') {
7!
208
    return ''
×
209
  }
210

211
  let resource = ''
7✔
212
  let first = true
7✔
213

214
  for (const key in params) {
7✔
215
    if (!Object.prototype.hasOwnProperty.call(params, key)) {
3!
216
      continue
×
217
    }
218

219
    if (!first) {
3✔
220
      resource += '/'
1✔
221
    }
222

223
    const value = (params as Record<string, unknown>)[key]
3✔
224
    resource += value == null ? '' : String(value)
3!
225
    first = false
3✔
226
  }
227

228
  return resource
7✔
229
}
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