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

supabase / storage / 25398322339

05 May 2026 07:42PM UTC coverage: 74.031% (-0.2%) from 74.256%
25398322339

Pull #922

github

web-flow
Merge abfd0b1f9 into 44260a113
Pull Request #922: fix: no any rule

3969 of 5916 branches covered (67.09%)

Branch coverage included in aggregate %.

102 of 154 new or added lines in 23 files covered. (66.23%)

9 existing lines in 5 files now uncovered.

8004 of 10257 relevant lines covered (78.03%)

406.63 hits per line

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

87.61
/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) =>
285✔
41
  fastifyPlugin(
42
    async (fastify) => {
43
      fastify.addHook('onRequest', async (req, res) => {
285✔
44
        req.startTime = Date.now()
1,604✔
45

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

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

66
      /**
67
       * Adds req.resources and req.operation to the request object
68
       */
69
      fastify.addHook('preHandler', async (req) => {
285✔
70
        let resources = req.resources
1,528✔
71

72
        if (resources === undefined) {
1,528✔
73
          resources = req.routeOptions.config.resources?.(req)
1,527✔
74
        }
75

76
        if (resources === undefined) {
1,528✔
77
          resources = req.raw.resources
1,450✔
78
        }
79

80
        if (resources === undefined) {
1,528✔
81
          const resourceFromParams = getResourceFromParams(req.params)
1,450✔
82
          resources = resourceFromParams ? [resourceFromParams] : []
1,450✔
83
        }
84

85
        if (resources && resources.length > 0) {
1,528✔
86
          for (let index = 0; index < resources.length; index++) {
1,330✔
87
            const resource = resources[index]
41,365✔
88
            if (!resource.startsWith('/')) {
41,365✔
89
              resources[index] = `/${resource}`
41,363✔
90
            }
91
          }
92
        }
93

94
        req.resources = resources
1,528✔
95
        req.operation = req.routeOptions.config.operation
1,528✔
96

97
        if (req.operation && typeof req.opentelemetry === 'function') {
1,528!
98
          req.opentelemetry()?.span?.setAttribute('http.operation', req.operation.type)
×
99
        }
100
      })
101

102
      fastify.addHook('onSend', async (req, _, payload) => {
285✔
103
        req.executionTime = Date.now() - req.startTime
1,556✔
104
        return payload
1,556✔
105
      })
106

107
      fastify.addHook('onResponse', async (req, reply) => {
285✔
108
        doRequestLog(req, {
1,603✔
109
          reply,
110
          excludeUrls: options.excludeUrls,
111
          statusCode: reply.statusCode,
112
          responseTime: reply.elapsedTime,
113
          executionTime: req.executionTime,
114
        })
115
      })
116
    },
117
    { name: 'log-request' }
118
  )
119

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

128
function doRequestLog(req: FastifyRequest, options: LogRequestOptions) {
129
  if (options.excludeUrls?.includes(req.url)) {
2,818!
130
    return
×
131
  }
132

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

144
  let reqMetadata: Record<string, unknown> = {}
2,818✔
145

146
  if (req.routeOptions.config.logMetadata) {
2,818✔
147
    try {
380✔
148
      reqMetadata = req.routeOptions.config.logMetadata(req)
380✔
149

150
      if (reqMetadata) {
380!
151
        try {
380✔
152
          if (typeof req.opentelemetry === 'function') {
380!
153
            req.opentelemetry()?.span?.setAttribute('http.metadata', JSON.stringify(reqMetadata))
×
154
          }
155
        } catch (e) {
156
          // do nothing
157
          logSchema.warning(req.log, 'Failed to serialize log metadata', {
×
158
            type: 'otel',
159
            tenantId,
160
            project: tenantId,
161
            reqId: rId,
162
            sbReqId: req.sbReqId,
163
            error: e,
164
          })
165
        }
166
      }
167
    } catch (e) {
168
      logSchema.error(req.log, 'Failed to get log metadata', {
×
169
        type: 'request',
170
        tenantId,
171
        project: tenantId,
172
        reqId: rId,
173
        sbReqId: req.sbReqId,
174
        error: e,
175
      })
176
    }
177
  }
178

179
  const buildLogMessage = `${tenantId} | ${rMeth} | ${statusCode} | ${cIP} | ${rId} | ${rUrl} | ${uAgent}`
2,818✔
180

181
  logSchema.request(req.log, buildLogMessage, {
2,818✔
182
    type: 'request',
183
    tenantId,
184
    project: tenantId,
185
    reqId: rId,
186
    sbReqId: req.sbReqId,
187
    req: requestLog,
188
    reqMetadata,
189
    res: replyLog,
190
    responseTime: options.responseTime,
191
    executionTime: options.executionTime,
192
    error,
193
    owner: req.owner,
194
    role: req.jwtPayload?.role,
195
    resources: req.resources,
196
    operation: req.operation?.type ?? req.routeOptions.config.operation?.type,
3,747✔
197
    serverTimes: req.serverTimings,
198
  })
199
}
200

201
function getResourceFromParams(params: unknown): string {
202
  if (!params || typeof params !== 'object') {
1,450!
NEW
203
    return ''
×
204
  }
205

206
  let resource = ''
1,450✔
207
  let first = true
1,450✔
208

209
  for (const key in params) {
1,450✔
210
    if (!Object.prototype.hasOwnProperty.call(params, key)) {
1,750!
NEW
211
      continue
×
212
    }
213

214
    if (!first) {
1,750✔
215
      resource += '/'
498✔
216
    }
217

218
    const value = (params as Record<string, unknown>)[key]
1,750✔
219
    resource += value == null ? '' : String(value)
1,750!
220
    first = false
1,750✔
221
  }
222

223
  return resource
1,450✔
224
}
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