Skip to content

Commit 3f589e6

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

File tree

4 files changed

+88
-39
lines changed

4 files changed

+88
-39
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: 67 additions & 37 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,30 @@ 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-
142-
if (!response.ok) {
143-
return
144-
}
141+
return await withActiveSpan(getTracer(), 'cache.read', async (span) => {
142+
const resourceURL = extractAndValidateURL(request)
143+
const cacheURL = `${context.url}/${toCacheKey(resourceURL)}`
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+
if (!response.ok) {
159+
span?.setStatus({ code: SpanStatusCode.ERROR })
160+
return
161+
}
145162

146-
return response
163+
return response
164+
})
147165
} catch {
148166
// no-op
149167
}
@@ -182,26 +200,38 @@ export class NetlifyCache implements Cache {
182200
return
183201
}
184202

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-
})
203+
await withActiveSpan(getTracer(), 'cache.write', async (span) => {
204+
const resourceURL = extractAndValidateURL(request)
198205

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
206+
span?.setAttributes({
207+
'cache.store': this.#name,
208+
'cache.key': resourceURL.toString(),
209+
})
202210

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

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)