Skip to content

Commit 225ff44

Browse files
committed
feat: add instrumentation to @netlify/cache
1 parent a044bf8 commit 225ff44

File tree

4 files changed

+90
-38
lines changed

4 files changed

+90
-38
lines changed

package-lock.json

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/cache/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
"vitest": "^3.0.0"
7676
},
7777
"dependencies": {
78+
"@netlify/otel": "^4.3.1",
7879
"@netlify/runtime-utils": "2.2.0"
7980
}
8081
}

packages/cache/src/bootstrap/cache.ts

Lines changed: 69 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { getTracer, withActiveSpan } from '@netlify/otel'
2+
import { SpanStatusCode } from '@netlify/otel/opentelemetry'
13
import { base64Encode } from '@netlify/runtime-utils'
24

35
import { EnvironmentOptions, RequestContext, Operation, RequestContextFactory } from './environment.js'
@@ -105,11 +107,18 @@ export class NetlifyCache implements Cache {
105107
const context = this.#getContext({ operation: Operation.Delete })
106108

107109
if (context) {
108-
const resourceURL = extractAndValidateURL(request)
109-
110-
await fetch(`${context.url}/${toCacheKey(resourceURL)}`, {
111-
headers: this[getInternalHeaders](context),
112-
method: 'DELETE',
110+
await withActiveSpan(getTracer(), 'cache.delete', async (span) => {
111+
const resourceURL = extractAndValidateURL(request)
112+
113+
span?.setAttributes({
114+
'cache.store': this.#name,
115+
'cache.key': resourceURL.toString(),
116+
})
117+
118+
await fetch(`${context.url}/${toCacheKey(resourceURL)}`, {
119+
headers: this[getInternalHeaders](context),
120+
method: 'DELETE',
121+
})
113122
})
114123
}
115124

@@ -129,21 +138,33 @@ export class NetlifyCache implements Cache {
129138
return
130139
}
131140

132-
const resourceURL = extractAndValidateURL(request)
133-
const cacheURL = `${context.url}/${toCacheKey(resourceURL)}`
134-
const response = await fetch(cacheURL, {
135-
headers: {
136-
...(request instanceof Request ? this[serializeRequestHeaders](request.headers) : {}),
137-
...this[getInternalHeaders](context),
138-
},
139-
method: 'GET',
140-
})
141+
return await withActiveSpan(getTracer(), 'cache.read', async (span) => {
142+
const resourceURL = extractAndValidateURL(request)
143+
const cacheURL = `${context.url}/${toCacheKey(resourceURL)}`
141144

142-
if (!response.ok) {
143-
return
144-
}
145+
span?.setAttributes({
146+
'cache.store': this.#name,
147+
'cache.key': resourceURL.toString(),
148+
})
149+
150+
const response = await fetch(cacheURL, {
151+
headers: {
152+
...(request instanceof Request ? this[serializeRequestHeaders](request.headers) : {}),
153+
...this[getInternalHeaders](context),
154+
},
155+
method: 'GET',
156+
})
157+
158+
span?.setStatus({
159+
code: response.ok ? SpanStatusCode.UNSET : SpanStatusCode.ERROR,
160+
})
161+
162+
if (!response.ok) {
163+
return
164+
}
145165

146-
return response
166+
return response
167+
})
147168
} catch {
148169
// no-op
149170
}
@@ -182,26 +203,38 @@ export class NetlifyCache implements Cache {
182203
return
183204
}
184205

185-
const resourceURL = extractAndValidateURL(request)
186-
187-
const cacheResponse = await fetch(`${context.url}/${toCacheKey(resourceURL)}`, {
188-
body: response.body,
189-
headers: {
190-
...this[getInternalHeaders](context),
191-
[HEADERS.ResourceHeaders]: this[serializeResponseHeaders](response.headers),
192-
[HEADERS.ResourceStatus]: response.status.toString(),
193-
},
194-
// @ts-expect-error https://github.com/whatwg/fetch/pull/1457
195-
duplex: 'half',
196-
method: 'POST',
197-
})
206+
await withActiveSpan(getTracer(), 'cache.write', async (span) => {
207+
const resourceURL = extractAndValidateURL(request)
198208

199-
if (!cacheResponse.ok) {
200-
const errorDetail = cacheResponse.headers?.get(HEADERS.ErrorDetail) ?? ''
201-
const errorMessage = ERROR_CODES[errorDetail as keyof typeof ERROR_CODES] || GENERIC_ERROR
209+
span?.setAttributes({
210+
'cache.store': this.#name,
211+
'cache.key': resourceURL.toString(),
212+
})
202213

203-
context.logger?.(`Failed to write to the cache: ${errorMessage}`)
204-
}
214+
const cacheResponse = await fetch(`${context.url}/${toCacheKey(resourceURL)}`, {
215+
body: response.body,
216+
headers: {
217+
...this[getInternalHeaders](context),
218+
[HEADERS.ResourceHeaders]: this[serializeResponseHeaders](response.headers),
219+
[HEADERS.ResourceStatus]: response.status.toString(),
220+
},
221+
// @ts-expect-error https://github.com/whatwg/fetch/pull/1457
222+
duplex: 'half',
223+
method: 'POST',
224+
})
225+
226+
if (!cacheResponse.ok) {
227+
const errorDetail = cacheResponse.headers?.get(HEADERS.ErrorDetail) ?? ''
228+
const errorMessage = ERROR_CODES[errorDetail as keyof typeof ERROR_CODES] || GENERIC_ERROR
229+
230+
span?.setStatus({
231+
code: SpanStatusCode.ERROR,
232+
message: errorMessage,
233+
})
234+
235+
context.logger?.(`Failed to write to the cache: ${errorMessage}`)
236+
}
237+
})
205238
}
206239
}
207240

packages/cache/tsconfig.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33
"allowImportingTsExtensions": true,
44
"emitDeclarationOnly": true,
55
"target": "ES2020",
6-
"module": "es2020",
6+
"module": "NodeNext",
77
"allowJs": true,
88
"declaration": true,
99
"declarationMap": false,
1010
"sourceMap": false,
1111
"outDir": "./dist",
1212
"removeComments": false,
1313
"strict": true,
14-
"moduleResolution": "node",
14+
"moduleResolution": "nodenext",
1515
"esModuleInterop": true,
1616
"skipLibCheck": true,
1717
"forceConsistentCasingInFileNames": true

0 commit comments

Comments
 (0)