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

supabase / storage / 24215194021

09 Apr 2026 09:53PM UTC coverage: 63.259% (-17.6%) from 80.843%
24215194021

Pull #1000

github

web-flow
Merge 434cd89fa into 41cfe7541
Pull Request #1000: feat: intro vitest & split units

3465 of 7093 branches covered (48.85%)

Branch coverage included in aggregate %.

2 of 12 new or added lines in 1 file covered. (16.67%)

2655 existing lines in 92 files now uncovered.

13417 of 19594 relevant lines covered (68.48%)

72.32 hits per line

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

73.57
/src/http/plugins/log-request.ts
1
import { logger, logSchema, redactQueryParamFromRequest } from '@internal/monitoring'
2
import { FastifyReply } from 'fastify/types/reply'
3
import { FastifyRequest } from 'fastify/types/request'
4
import fastifyPlugin from 'fastify-plugin'
5

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

10
declare module 'fastify' {
11
  interface FastifyRequest {
12
    executionError?: Error
13
    operation?: { type: string }
14
    resources?: string[]
15
    startTime: number
16
    executionTime?: number
17
  }
18

19
  interface FastifyContextConfig {
20
    operation?: { type: string }
21
    resources?: (req: FastifyRequest<any>) => string[]
22
    logMetadata?: (req: FastifyRequest<any>) => Record<string, unknown>
23
  }
24
}
25

26
/**
27
 * Request logger plugin
28
 * @param options
29
 */
30
export const logRequest = (options: RequestLoggerOptions) =>
2✔
31
  fastifyPlugin(
32
    async (fastify) => {
265✔
33
      fastify.addHook('onRequest', async (req, res) => {
2✔
34
        req.startTime = Date.now()
2✔
35

36
        res.raw.once('close', () => {
2✔
37
          if (req.raw.aborted) {
2!
38
            doRequestLog(req, {
×
39
              excludeUrls: options.excludeUrls,
40
              statusCode: 'ABORTED REQ',
41
              responseTime: (Date.now() - req.startTime) / 1000,
42
            })
43
            return
×
44
          }
45

46
          if (!res.raw.writableFinished) {
2!
47
            doRequestLog(req, {
2✔
48
              excludeUrls: options.excludeUrls,
49
              statusCode: 'ABORTED RES',
50
              responseTime: (Date.now() - req.startTime) / 1000,
51
            })
52
          }
53
        })
54
      })
55

56
      /**
57
       * Adds req.resources and req.operation to the request object
58
       */
59
      fastify.addHook('preHandler', async (req) => {
2✔
60
        let resources = req.resources
2✔
61

62
        if (resources === undefined) {
2!
63
          resources = req.routeOptions.config.resources?.(req)
2✔
64
        }
65

66
        if (resources === undefined) {
2✔
67
          resources = (req.raw as any).resources
1✔
68
        }
69

70
        if (resources === undefined) {
2✔
71
          const params = req.params as Record<string, unknown> | undefined
1✔
72
          let resourceFromParams = ''
1✔
73

74
          if (params) {
1!
75
            let first = true
1✔
76
            for (const key in params) {
1✔
77
              if (!Object.prototype.hasOwnProperty.call(params, key)) {
2!
78
                continue
×
79
              }
80

81
              if (!first) {
2✔
82
                resourceFromParams += '/'
1✔
83
              }
84

85
              const value = params[key]
2✔
86
              resourceFromParams += value == null ? '' : String(value)
2!
87
              first = false
2✔
88
            }
89
          }
90

91
          resources = resourceFromParams ? [resourceFromParams] : []
1!
92
        }
93

94
        if (resources && resources.length > 0) {
2!
95
          for (let index = 0; index < resources.length; index++) {
2✔
96
            const resource = resources[index]
3✔
97
            if (!resource.startsWith('/')) {
3✔
98
              resources[index] = `/${resource}`
2✔
99
            }
100
          }
101
        }
102

103
        req.resources = resources
2✔
104
        req.operation = req.routeOptions.config.operation
2✔
105

106
        if (req.operation && typeof req.opentelemetry === 'function') {
2!
107
          req.opentelemetry()?.span?.setAttribute('http.operation', req.operation.type)
×
108
        }
109
      })
110

111
      fastify.addHook('onSend', async (req, _, payload) => {
2✔
112
        req.executionTime = Date.now() - req.startTime
2✔
113
        return payload
2✔
114
      })
115

116
      fastify.addHook('onResponse', async (req, reply) => {
2✔
117
        doRequestLog(req, {
2✔
118
          reply,
119
          excludeUrls: options.excludeUrls,
120
          statusCode: reply.statusCode,
121
          responseTime: reply.elapsedTime,
122
          executionTime: req.executionTime,
123
        })
124
      })
125
    },
126
    { name: 'log-request' }
127
  )
128

129
interface LogRequestOptions {
130
  reply?: FastifyReply
131
  excludeUrls?: string[]
132
  statusCode: number | 'ABORTED REQ' | 'ABORTED RES'
133
  responseTime: number
134
  executionTime?: number
135
}
136

137
function doRequestLog(req: FastifyRequest, options: LogRequestOptions) {
2,666✔
138
  if (options.excludeUrls?.includes(req.url)) {
4!
139
    return
×
140
  }
141

142
  const rMeth = req.method
4✔
143
  const rUrl = redactQueryParamFromRequest(req, [
4✔
144
    'token',
145
    'X-Amz-Credential',
146
    'X-Amz-Signature',
147
    'X-Amz-Security-Token',
148
  ])
149
  const uAgent = req.headers['user-agent']
4✔
150
  const rId = req.id
4✔
151
  const cIP = req.ip
4✔
152
  const statusCode = options.statusCode
4✔
153
  const error = (req.raw as any).executionError || req.executionError
4✔
154
  const tenantId = req.tenantId
4✔
155

156
  let reqMetadata: Record<string, unknown> = {}
4✔
157

158
  if (req.routeOptions.config.logMetadata) {
4!
UNCOV
159
    try {
×
UNCOV
160
      reqMetadata = req.routeOptions.config.logMetadata(req)
×
161

UNCOV
162
      if (reqMetadata) {
×
UNCOV
163
        try {
×
UNCOV
164
          if (typeof req.opentelemetry === 'function') {
×
165
            req.opentelemetry()?.span?.setAttribute('http.metadata', JSON.stringify(reqMetadata))
×
166
          }
167
        } catch (e) {
×
168
          // do nothing
169
          logSchema.warning(logger, 'Failed to serialize log metadata', {
×
170
            type: 'otel',
171
            error: e,
172
          })
173
        }
174
      }
175
    } catch (e) {
×
176
      logSchema.error(logger, 'Failed to get log metadata', {
×
177
        type: 'request',
178
        error: e,
179
      })
180
    }
181
  }
182

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

185
  logSchema.request(req.log, buildLogMessage, {
4✔
186
    type: 'request',
187
    req,
188
    reqMetadata,
189
    res: options.reply,
190
    responseTime: options.responseTime,
191
    executionTime: options.executionTime,
192
    error,
193
    owner: req.owner,
194
    role: req.jwtPayload?.role,
1,754✔
195
    resources: req.resources,
196
    operation: req.operation?.type ?? req.routeOptions.config.operation?.type,
2,805✔
197
    serverTimes: req.serverTimings,
198
  })
199
}
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