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

supabase / storage / 24027259782

06 Apr 2026 09:50AM UTC coverage: 80.536% (-0.04%) from 80.571%
24027259782

Pull #973

github

web-flow
Merge c8ecb4a20 into 553a6101f
Pull Request #973: fix: allocate less in interim

3155 of 4092 branches covered (77.1%)

Branch coverage included in aggregate %.

103 of 111 new or added lines in 5 files covered. (92.79%)

17 existing lines in 2 files now uncovered.

30108 of 37210 relevant lines covered (80.91%)

315.35 hits per line

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

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

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

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

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

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

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

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

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

1,411✔
62
        if (resources === undefined) {
1,411✔
63
          resources = req.routeOptions.config.resources?.(req)
1,411✔
64
        }
1,411✔
65

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

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

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

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

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

1,349✔
91
          resources = resourceFromParams ? [resourceFromParams] : []
1,349✔
92
        }
1,349✔
93

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

1,411✔
103
        req.resources = resources
1,411✔
104
        req.operation = req.routeOptions.config.operation
1,411✔
105

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

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

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

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

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

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

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

2,625✔
158
  if (req.routeOptions.config.logMetadata) {
2,625✔
159
    try {
380✔
160
      reqMetadata = req.routeOptions.config.logMetadata(req)
380✔
161

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

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

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