From d1da366e142fcf58e69b3387ec2a4e81f91422f3 Mon Sep 17 00:00:00 2001
From: Michal Piechowiak <misiek.piechowiak@gmail.com>
Date: Thu, 6 Feb 2025 22:14:14 +0100
Subject: [PATCH 1/2] initial stuff

---
 src/run/handlers/cache.cts      | 47 ++++++++++++++++------------
 src/run/handlers/pc-client.cts  | 55 +++++++++++++++++++++++++++++++++
 src/run/headers.ts              |  5 ++-
 src/run/next.cts                | 35 ++++++++++-----------
 src/run/regional-blob-store.cts | 11 -------
 5 files changed, 102 insertions(+), 51 deletions(-)
 create mode 100644 src/run/handlers/pc-client.cts
 delete mode 100644 src/run/regional-blob-store.cts

diff --git a/src/run/handlers/cache.cts b/src/run/handlers/cache.cts
index c00a4d5e14..471c8703cc 100644
--- a/src/run/handlers/cache.cts
+++ b/src/run/handlers/cache.cts
@@ -5,7 +5,6 @@ import { Buffer } from 'node:buffer'
 import { join } from 'node:path'
 import { join as posixJoin } from 'node:path/posix'
 
-import { Store } from '@netlify/blobs'
 import { purgeCache } from '@netlify/functions'
 import { type Span } from '@opentelemetry/api'
 import type { PrerenderManifest } from 'next/dist/build/index.js'
@@ -22,8 +21,8 @@ import {
   type NetlifyCacheHandlerValue,
   type NetlifyIncrementalCacheValue,
 } from '../../shared/cache-types.cjs'
-import { getRegionalBlobStore } from '../regional-blob-store.cjs'
 
+import { getFromProgrammableCache, setInProgrammableCache } from './pc-client.cjs'
 import { getLogger, getRequestContext } from './request-context.cjs'
 import { getTracer } from './tracer.cjs'
 
@@ -36,14 +35,12 @@ const purgeCacheUserAgent = `${nextRuntimePkgName}@${nextRuntimePkgVersion}`
 export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions {
   options: CacheHandlerContext
   revalidatedTags: string[]
-  blobStore: Store
   tracer = getTracer()
   tagManifestsFetchedFromBlobStoreInCurrentRequest: TagManifestBlobCache
 
   constructor(options: CacheHandlerContext) {
     this.options = options
     this.revalidatedTags = options.revalidatedTags
-    this.blobStore = getRegionalBlobStore({ consistency: 'strong' })
     this.tagManifestsFetchedFromBlobStoreInCurrentRequest = {}
   }
 
@@ -206,12 +203,7 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions {
       const blobKey = await this.encodeBlobKey(key)
       span.setAttributes({ key, blobKey })
 
-      const blob = (await this.tracer.withActiveSpan('blobStore.get', async (blobGetSpan) => {
-        blobGetSpan.setAttributes({ key, blobKey })
-        return await this.blobStore.get(blobKey, {
-          type: 'json',
-        })
-      })) as NetlifyCacheHandlerValue | null
+      const blob = (await getFromProgrammableCache(blobKey)) as NetlifyCacheHandlerValue | null
 
       // if blob is null then we don't have a cache entry
       if (!blob) {
@@ -338,10 +330,16 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions {
       // and we didn't yet capture cache tags, we try to get cache tags from freshly produced cache value
       this.captureCacheTags(value, key)
 
-      await this.blobStore.setJSON(blobKey, {
+      const cacheEntry = {
         lastModified,
         value,
-      })
+      } satisfies NetlifyCacheHandlerValue
+
+      await setInProgrammableCache(
+        blobKey,
+        cacheEntry,
+        this.getCacheTagsFromCacheEntry(cacheEntry, context.tags),
+      )
 
       if (data?.kind === 'PAGE' || data?.kind === 'PAGES') {
         const requestContext = getRequestContext()
@@ -399,7 +397,7 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions {
     await Promise.all(
       tags.map(async (tag) => {
         try {
-          await this.blobStore.setJSON(await this.encodeBlobKey(tag), data)
+          await setInProgrammableCache(await this.encodeBlobKey(tag), data)
         } catch (error) {
           getLogger().withError(error).log(`Failed to update tag manifest for ${tag}`)
         }
@@ -418,10 +416,7 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions {
     this.tagManifestsFetchedFromBlobStoreInCurrentRequest = {}
   }
 
-  /**
-   * Checks if a cache entry is stale through on demand revalidated tags
-   */
-  private async checkCacheEntryStaleByTags(
+  private getCacheTagsFromCacheEntry(
     cacheEntry: NetlifyCacheHandlerValue,
     tags: string[] = [],
     softTags: string[] = [],
@@ -439,7 +434,21 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions {
     ) {
       cacheTags =
         (cacheEntry.value.headers?.[NEXT_CACHE_TAGS_HEADER] as string)?.split(/,|%2c/gi) || []
-    } else {
+    }
+
+    return cacheTags
+  }
+
+  /**
+   * Checks if a cache entry is stale through on demand revalidated tags
+   */
+  private async checkCacheEntryStaleByTags(
+    cacheEntry: NetlifyCacheHandlerValue,
+    tags: string[] = [],
+    softTags: string[] = [],
+  ) {
+    const cacheTags = this.getCacheTagsFromCacheEntry(cacheEntry, tags, softTags)
+    if (cacheTags.length === 0) {
       return false
     }
 
@@ -474,7 +483,7 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions {
           tagManifestPromise = this.encodeBlobKey(tag).then((blobKey) => {
             return this.tracer.withActiveSpan(`get tag manifest`, async (span) => {
               span.setAttributes({ tag, blobKey })
-              return this.blobStore.get(blobKey, { type: 'json' })
+              return getFromProgrammableCache(blobKey)
             })
           })
 
diff --git a/src/run/handlers/pc-client.cts b/src/run/handlers/pc-client.cts
new file mode 100644
index 0000000000..79dc2d44d8
--- /dev/null
+++ b/src/run/handlers/pc-client.cts
@@ -0,0 +1,55 @@
+// const fetchBeforeNextPatchedIt = globalThis.fetch
+
+import { getTracer } from './tracer.cjs'
+
+const CACHE_NAME = 'next-runtime'
+
+const fetchBeforeNextPatchedIt = globalThis.fetch
+
+function getURL(key: string) {
+  return new URL(key, 'https://n.org')
+}
+
+export async function getFromProgrammableCache(key: string) {
+  return await getTracer().withActiveSpan('pc.get', async (span) => {
+    const url = getURL(key)
+    span.setAttributes({ key, url: url.href })
+
+    const cache = await caches.open(CACHE_NAME)
+    const previousFetch = globalThis.fetch
+    globalThis.fetch = fetchBeforeNextPatchedIt
+    const response = await cache.match(url)
+
+    console.log({ response })
+    globalThis.fetch = previousFetch
+    return response?.json()
+  })
+}
+
+export async function setInProgrammableCache(key: string, value: any, tags?: string[]) {
+  return await getTracer().withActiveSpan('pc.set', async (span) => {
+    const url = getURL(key)
+    span.setAttributes({ key, url: url.href })
+
+    const cache = await caches.open(CACHE_NAME)
+
+    const headers: HeadersInit = {
+      'Content-Type': 'application/json',
+      'Cache-Control': 'max-age=3600',
+    }
+
+    // ignore for now
+    // if (tags && tags.length !== 0) {
+    //   headers['Cache-Tag'] = tags.join(',')
+    // }
+    const previousFetch = globalThis.fetch
+    globalThis.fetch = fetchBeforeNextPatchedIt
+    await cache.put(
+      url,
+      new Response(JSON.stringify(value), {
+        headers,
+      }),
+    )
+    globalThis.fetch = previousFetch
+  })
+}
diff --git a/src/run/headers.ts b/src/run/headers.ts
index c93c0a605b..2c92381406 100644
--- a/src/run/headers.ts
+++ b/src/run/headers.ts
@@ -5,7 +5,7 @@ import { encodeBlobKey } from '../shared/blobkey.js'
 
 import { getLogger, RequestContext } from './handlers/request-context.cjs'
 import type { RuntimeTracer } from './handlers/tracer.cjs'
-import { getRegionalBlobStore } from './regional-blob-store.cjs'
+import { getFromProgrammableCache } from './handlers/pc-client.cjs'
 
 const ALL_VARIATIONS = Symbol.for('ALL_VARIATIONS')
 interface NetlifyVaryValues {
@@ -167,7 +167,6 @@ export const adjustDateHeader = async ({
       warning: true,
     })
 
-    const blobStore = getRegionalBlobStore({ consistency: 'strong' })
     const blobKey = await encodeBlobKey(key)
 
     // TODO: use metadata for this
@@ -178,7 +177,7 @@ export const adjustDateHeader = async ({
           key,
           blobKey,
         })
-        const blob = (await blobStore.get(blobKey, { type: 'json' })) ?? {}
+        const blob = (await getFromProgrammableCache(blobKey)) ?? {}
 
         getBlobForDateSpan.addEvent(blob ? 'Cache hit' : 'Cache miss')
         return blob.lastModified
diff --git a/src/run/next.cts b/src/run/next.cts
index 17859c4835..f2315fb725 100644
--- a/src/run/next.cts
+++ b/src/run/next.cts
@@ -6,7 +6,6 @@ import { patchFs } from 'fs-monkey'
 
 import { getRequestContext } from './handlers/request-context.cjs'
 import { getTracer } from './handlers/tracer.cjs'
-import { getRegionalBlobStore } from './regional-blob-store.cjs'
 
 // https://github.com/vercel/next.js/pull/68193/files#diff-37243d614f1f5d3f7ea50bbf2af263f6b1a9a4f70e84427977781e07b02f57f1R49
 // This import resulted in importing unbundled React which depending if NODE_ENV is `production` or not would use
@@ -96,23 +95,23 @@ export async function getMockedRequestHandler(...args: Parameters<typeof getRequ
         return await ofs.readFile(path, options)
       } catch (error) {
         // only try to get .html files from the blob store
-        if (typeof path === 'string' && path.endsWith('.html')) {
-          const store = getRegionalBlobStore()
-          const relPath = relative(resolve('.next/server/pages'), path)
-          const file = (await store.get(await encodeBlobKey(relPath), {
-            type: 'json',
-          })) as HtmlBlob | null
-          if (file !== null) {
-            if (!file.isFallback) {
-              const requestContext = getRequestContext()
-              if (requestContext) {
-                requestContext.usedFsReadForNonFallback = true
-              }
-            }
-
-            return file.html
-          }
-        }
+        // if (typeof path === 'string' && path.endsWith('.html')) {
+        //   const store = getRegionalBlobStore()
+        //   const relPath = relative(resolve('.next/server/pages'), path)
+        //   const file = (await store.get(await encodeBlobKey(relPath), {
+        //     type: 'json',
+        //   })) as HtmlBlob | null
+        //   if (file !== null) {
+        //     if (!file.isFallback) {
+        //       const requestContext = getRequestContext()
+        //       if (requestContext) {
+        //         requestContext.usedFsReadForNonFallback = true
+        //       }
+        //     }
+
+        //     return file.html
+        //   }
+        // }
 
         throw error
       }
diff --git a/src/run/regional-blob-store.cts b/src/run/regional-blob-store.cts
deleted file mode 100644
index a524569cc4..0000000000
--- a/src/run/regional-blob-store.cts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { getDeployStore, GetWithMetadataOptions, Store } from '@netlify/blobs'
-
-const fetchBeforeNextPatchedIt = globalThis.fetch
-
-export const getRegionalBlobStore = (args: GetWithMetadataOptions = {}): Store => {
-  return getDeployStore({
-    ...args,
-    fetch: fetchBeforeNextPatchedIt,
-    region: process.env.USE_REGIONAL_BLOBS?.toUpperCase() === 'TRUE' ? undefined : 'us-east-2',
-  })
-}

From bb8e9c296c1699df5108aa1138c0588aa85d5b80 Mon Sep 17 00:00:00 2001
From: Michal Piechowiak <misiek.piechowiak@gmail.com>
Date: Mon, 10 Feb 2025 12:16:08 +0100
Subject: [PATCH 2/2] otel-http-instrumentation-attempt

---
 package-lock.json           | 221 ++++++++++++++++++++++++++++++------
 package.json                |   2 +
 src/run/handlers/tracing.ts |   6 +
 3 files changed, 196 insertions(+), 33 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 8ef9bd3734..7fdf7e1b51 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,6 +20,8 @@
         "@netlify/zip-it-and-ship-it": "^9.41.0",
         "@opentelemetry/api": "^1.8.0",
         "@opentelemetry/exporter-trace-otlp-http": "^0.51.0",
+        "@opentelemetry/instrumentation": "^0.57.1",
+        "@opentelemetry/instrumentation-http": "^0.57.1",
         "@opentelemetry/resources": "^1.24.0",
         "@opentelemetry/sdk-node": "^0.51.0",
         "@opentelemetry/sdk-trace-node": "^1.24.0",
@@ -5902,14 +5904,15 @@
       }
     },
     "node_modules/@opentelemetry/instrumentation": {
-      "version": "0.51.1",
-      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.51.1.tgz",
-      "integrity": "sha512-JIrvhpgqY6437QIqToyozrUG1h5UhwHkaGK/WAX+fkrpyPtc+RO5FkRtUd9BH0MibabHHvqsnBGKfKVijbmp8w==",
+      "version": "0.57.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.1.tgz",
+      "integrity": "sha512-SgHEKXoVxOjc20ZYusPG3Fh+RLIZTSa4x8QtD3NfgAUDyqdFFS9W1F2ZVbZkqDCdyMcQG02Ok4duUGLHJXHgbA==",
       "dev": true,
+      "license": "Apache-2.0",
       "dependencies": {
-        "@opentelemetry/api-logs": "0.51.1",
-        "@types/shimmer": "^1.0.2",
-        "import-in-the-middle": "1.7.4",
+        "@opentelemetry/api-logs": "0.57.1",
+        "@types/shimmer": "^1.2.0",
+        "import-in-the-middle": "^1.8.1",
         "require-in-the-middle": "^7.1.1",
         "semver": "^7.5.2",
         "shimmer": "^1.2.1"
@@ -5921,23 +5924,71 @@
         "@opentelemetry/api": "^1.3.0"
       }
     },
+    "node_modules/@opentelemetry/instrumentation-http": {
+      "version": "0.57.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.57.1.tgz",
+      "integrity": "sha512-ThLmzAQDs7b/tdKI3BV2+yawuF09jF111OFsovqT1Qj3D8vjwKBwhi/rDE5xethwn4tSXtZcJ9hBsVAlWFQZ7g==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "1.30.1",
+        "@opentelemetry/instrumentation": "0.57.1",
+        "@opentelemetry/semantic-conventions": "1.28.0",
+        "forwarded-parse": "2.1.2",
+        "semver": "^7.5.2"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/core": {
+      "version": "1.30.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz",
+      "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/semantic-conventions": "1.28.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": ">=1.0.0 <1.10.0"
+      }
+    },
+    "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/semantic-conventions": {
+      "version": "1.28.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz",
+      "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=14"
+      }
+    },
     "node_modules/@opentelemetry/instrumentation/node_modules/@opentelemetry/api-logs": {
-      "version": "0.51.1",
-      "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.51.1.tgz",
-      "integrity": "sha512-E3skn949Pk1z2XtXu/lxf6QAZpawuTM/IUEXcAzpiUkTd73Hmvw26FiN3cJuTmkpM5hZzHwkomVdtrh/n/zzwA==",
+      "version": "0.57.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.57.1.tgz",
+      "integrity": "sha512-I4PHczeujhQAQv6ZBzqHYEUiggZL4IdSMixtVD3EYqbdrjujE7kRfI5QohjlPoJm8BvenoW5YaTMWRrbpot6tg==",
       "dev": true,
+      "license": "Apache-2.0",
       "dependencies": {
-        "@opentelemetry/api": "^1.0.0"
+        "@opentelemetry/api": "^1.3.0"
       },
       "engines": {
         "node": ">=14"
       }
     },
     "node_modules/@opentelemetry/instrumentation/node_modules/import-in-the-middle": {
-      "version": "1.7.4",
-      "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.7.4.tgz",
-      "integrity": "sha512-Lk+qzWmiQuRPPulGQeK5qq0v32k2bHnWrRPFgqyvhw7Kkov5L6MOLOIU3pcWeujc9W4q54Cp3Q2WV16eQkc7Bg==",
+      "version": "1.12.0",
+      "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.12.0.tgz",
+      "integrity": "sha512-yAgSE7GmtRcu4ZUSFX/4v69UGXwugFFSdIQJ14LHPOPPQrWv8Y7O9PHsw8Ovk7bKCLe4sjXMbZFqGFcLHpZ89w==",
       "dev": true,
+      "license": "Apache-2.0",
       "dependencies": {
         "acorn": "^8.8.2",
         "acorn-import-attributes": "^1.9.5",
@@ -6333,6 +6384,27 @@
         "@opentelemetry/api": "^1.0.0"
       }
     },
+    "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/instrumentation": {
+      "version": "0.51.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.51.1.tgz",
+      "integrity": "sha512-JIrvhpgqY6437QIqToyozrUG1h5UhwHkaGK/WAX+fkrpyPtc+RO5FkRtUd9BH0MibabHHvqsnBGKfKVijbmp8w==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/api-logs": "0.51.1",
+        "@types/shimmer": "^1.0.2",
+        "import-in-the-middle": "1.7.4",
+        "require-in-the-middle": "^7.1.1",
+        "semver": "^7.5.2",
+        "shimmer": "^1.2.1"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
     "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/otlp-exporter-base": {
       "version": "0.51.1",
       "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.51.1.tgz",
@@ -6454,6 +6526,19 @@
         "@opentelemetry/api": ">=1.0.0 <1.9.0"
       }
     },
+    "node_modules/@opentelemetry/sdk-node/node_modules/import-in-the-middle": {
+      "version": "1.7.4",
+      "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.7.4.tgz",
+      "integrity": "sha512-Lk+qzWmiQuRPPulGQeK5qq0v32k2bHnWrRPFgqyvhw7Kkov5L6MOLOIU3pcWeujc9W4q54Cp3Q2WV16eQkc7Bg==",
+      "dev": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "acorn": "^8.8.2",
+        "acorn-import-attributes": "^1.9.5",
+        "cjs-module-lexer": "^1.2.2",
+        "module-details-from-path": "^1.0.3"
+      }
+    },
     "node_modules/@opentelemetry/sdk-trace-base": {
       "version": "1.22.0",
       "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.22.0.tgz",
@@ -7091,10 +7176,11 @@
       "dev": true
     },
     "node_modules/@types/shimmer": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.0.5.tgz",
-      "integrity": "sha512-9Hp0ObzwwO57DpLFF0InUjUm/II8GmKAvzbefxQTihCb7KI6yc9yzf0nLc4mVdby5N4DRCgQM2wCup9KTieeww==",
-      "dev": true
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz",
+      "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==",
+      "dev": true,
+      "license": "MIT"
     },
     "node_modules/@types/statuses": {
       "version": "2.0.4",
@@ -12256,6 +12342,13 @@
         "node": ">=12.20.0"
       }
     },
+    "node_modules/forwarded-parse": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz",
+      "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/fs-extra": {
       "version": "11.1.1",
       "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz",
@@ -40251,32 +40344,32 @@
       }
     },
     "@opentelemetry/instrumentation": {
-      "version": "0.51.1",
-      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.51.1.tgz",
-      "integrity": "sha512-JIrvhpgqY6437QIqToyozrUG1h5UhwHkaGK/WAX+fkrpyPtc+RO5FkRtUd9BH0MibabHHvqsnBGKfKVijbmp8w==",
+      "version": "0.57.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.1.tgz",
+      "integrity": "sha512-SgHEKXoVxOjc20ZYusPG3Fh+RLIZTSa4x8QtD3NfgAUDyqdFFS9W1F2ZVbZkqDCdyMcQG02Ok4duUGLHJXHgbA==",
       "dev": true,
       "requires": {
-        "@opentelemetry/api-logs": "0.51.1",
-        "@types/shimmer": "^1.0.2",
-        "import-in-the-middle": "1.7.4",
+        "@opentelemetry/api-logs": "0.57.1",
+        "@types/shimmer": "^1.2.0",
+        "import-in-the-middle": "^1.8.1",
         "require-in-the-middle": "^7.1.1",
         "semver": "^7.5.2",
         "shimmer": "^1.2.1"
       },
       "dependencies": {
         "@opentelemetry/api-logs": {
-          "version": "0.51.1",
-          "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.51.1.tgz",
-          "integrity": "sha512-E3skn949Pk1z2XtXu/lxf6QAZpawuTM/IUEXcAzpiUkTd73Hmvw26FiN3cJuTmkpM5hZzHwkomVdtrh/n/zzwA==",
+          "version": "0.57.1",
+          "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.57.1.tgz",
+          "integrity": "sha512-I4PHczeujhQAQv6ZBzqHYEUiggZL4IdSMixtVD3EYqbdrjujE7kRfI5QohjlPoJm8BvenoW5YaTMWRrbpot6tg==",
           "dev": true,
           "requires": {
-            "@opentelemetry/api": "^1.0.0"
+            "@opentelemetry/api": "^1.3.0"
           }
         },
         "import-in-the-middle": {
-          "version": "1.7.4",
-          "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.7.4.tgz",
-          "integrity": "sha512-Lk+qzWmiQuRPPulGQeK5qq0v32k2bHnWrRPFgqyvhw7Kkov5L6MOLOIU3pcWeujc9W4q54Cp3Q2WV16eQkc7Bg==",
+          "version": "1.12.0",
+          "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.12.0.tgz",
+          "integrity": "sha512-yAgSE7GmtRcu4ZUSFX/4v69UGXwugFFSdIQJ14LHPOPPQrWv8Y7O9PHsw8Ovk7bKCLe4sjXMbZFqGFcLHpZ89w==",
           "dev": true,
           "requires": {
             "acorn": "^8.8.2",
@@ -40287,6 +40380,36 @@
         }
       }
     },
+    "@opentelemetry/instrumentation-http": {
+      "version": "0.57.1",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.57.1.tgz",
+      "integrity": "sha512-ThLmzAQDs7b/tdKI3BV2+yawuF09jF111OFsovqT1Qj3D8vjwKBwhi/rDE5xethwn4tSXtZcJ9hBsVAlWFQZ7g==",
+      "dev": true,
+      "requires": {
+        "@opentelemetry/core": "1.30.1",
+        "@opentelemetry/instrumentation": "0.57.1",
+        "@opentelemetry/semantic-conventions": "1.28.0",
+        "forwarded-parse": "2.1.2",
+        "semver": "^7.5.2"
+      },
+      "dependencies": {
+        "@opentelemetry/core": {
+          "version": "1.30.1",
+          "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz",
+          "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==",
+          "dev": true,
+          "requires": {
+            "@opentelemetry/semantic-conventions": "1.28.0"
+          }
+        },
+        "@opentelemetry/semantic-conventions": {
+          "version": "1.28.0",
+          "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz",
+          "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==",
+          "dev": true
+        }
+      }
+    },
     "@opentelemetry/otlp-exporter-base": {
       "version": "0.49.1",
       "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.49.1.tgz",
@@ -40560,6 +40683,20 @@
             "@opentelemetry/sdk-trace-base": "1.24.1"
           }
         },
+        "@opentelemetry/instrumentation": {
+          "version": "0.51.1",
+          "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.51.1.tgz",
+          "integrity": "sha512-JIrvhpgqY6437QIqToyozrUG1h5UhwHkaGK/WAX+fkrpyPtc+RO5FkRtUd9BH0MibabHHvqsnBGKfKVijbmp8w==",
+          "dev": true,
+          "requires": {
+            "@opentelemetry/api-logs": "0.51.1",
+            "@types/shimmer": "^1.0.2",
+            "import-in-the-middle": "1.7.4",
+            "require-in-the-middle": "^7.1.1",
+            "semver": "^7.5.2",
+            "shimmer": "^1.2.1"
+          }
+        },
         "@opentelemetry/otlp-exporter-base": {
           "version": "0.51.1",
           "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.51.1.tgz",
@@ -40637,6 +40774,18 @@
             "@opentelemetry/resources": "1.24.1",
             "@opentelemetry/semantic-conventions": "1.24.1"
           }
+        },
+        "import-in-the-middle": {
+          "version": "1.7.4",
+          "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.7.4.tgz",
+          "integrity": "sha512-Lk+qzWmiQuRPPulGQeK5qq0v32k2bHnWrRPFgqyvhw7Kkov5L6MOLOIU3pcWeujc9W4q54Cp3Q2WV16eQkc7Bg==",
+          "dev": true,
+          "requires": {
+            "acorn": "^8.8.2",
+            "acorn-import-attributes": "^1.9.5",
+            "cjs-module-lexer": "^1.2.2",
+            "module-details-from-path": "^1.0.3"
+          }
         }
       }
     },
@@ -41112,9 +41261,9 @@
       "dev": true
     },
     "@types/shimmer": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.0.5.tgz",
-      "integrity": "sha512-9Hp0ObzwwO57DpLFF0InUjUm/II8GmKAvzbefxQTihCb7KI6yc9yzf0nLc4mVdby5N4DRCgQM2wCup9KTieeww==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz",
+      "integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==",
       "dev": true
     },
     "@types/statuses": {
@@ -44732,6 +44881,12 @@
         "fetch-blob": "^3.1.2"
       }
     },
+    "forwarded-parse": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz",
+      "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==",
+      "dev": true
+    },
     "fs-extra": {
       "version": "11.1.1",
       "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz",
diff --git a/package.json b/package.json
index 6a5779e5e1..69fe13088c 100644
--- a/package.json
+++ b/package.json
@@ -59,6 +59,8 @@
     "@netlify/zip-it-and-ship-it": "^9.41.0",
     "@opentelemetry/api": "^1.8.0",
     "@opentelemetry/exporter-trace-otlp-http": "^0.51.0",
+    "@opentelemetry/instrumentation": "^0.57.1",
+    "@opentelemetry/instrumentation-http": "^0.57.1",
     "@opentelemetry/resources": "^1.24.0",
     "@opentelemetry/sdk-node": "^0.51.0",
     "@opentelemetry/sdk-trace-node": "^1.24.0",
diff --git a/src/run/handlers/tracing.ts b/src/run/handlers/tracing.ts
index c10724b3ef..21ff44145b 100644
--- a/src/run/handlers/tracing.ts
+++ b/src/run/handlers/tracing.ts
@@ -1,4 +1,6 @@
 import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
+import { registerInstrumentations } from '@opentelemetry/instrumentation'
+import { HttpInstrumentation } from '@opentelemetry/instrumentation-http'
 import { Resource } from '@opentelemetry/resources'
 import { NodeSDK } from '@opentelemetry/sdk-node'
 import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node'
@@ -24,6 +26,10 @@ const sdk = new NodeSDK({
     }),
   ),
 })
+
+registerInstrumentations({
+  instrumentations: [new HttpInstrumentation()],
+})
 export default sdk
 
 // gracefully shut down the SDK on process exit
