diff --git a/examples/with-opentelemetry/.gitignore b/examples/with-opentelemetry/.gitignore
new file mode 100644
index 0000000000000..c87c9b392c020
--- /dev/null
+++ b/examples/with-opentelemetry/.gitignore
@@ -0,0 +1,36 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+
+# local env files
+.env*.local
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
diff --git a/examples/with-opentelemetry/.vscode/settings.json b/examples/with-opentelemetry/.vscode/settings.json
new file mode 100644
index 0000000000000..87e57c9a65f37
--- /dev/null
+++ b/examples/with-opentelemetry/.vscode/settings.json
@@ -0,0 +1,4 @@
+{
+ "typescript.tsdk": "../../node_modules/.pnpm/typescript@4.7.4/node_modules/typescript/lib",
+ "typescript.enablePromptUseWorkspaceTsdk": true
+}
diff --git a/examples/with-opentelemetry/README.md b/examples/with-opentelemetry/README.md
new file mode 100644
index 0000000000000..b3adf6aef5fbf
--- /dev/null
+++ b/examples/with-opentelemetry/README.md
@@ -0,0 +1,30 @@
+# Data fetch example
+
+Next.js was conceived to make it easy to create universal apps. That's why fetching data
+on the server and the client when necessary is so easy with Next.js.
+
+By using `getStaticProps` Next.js will fetch data at build time from a page, and pre-render the page to static assets.
+
+## Deploy your own
+
+Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example) or preview live with [StackBlitz](https://stackblitz.com/github/vercel/next.js/tree/canary/examples/data-fetch)
+
+[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/data-fetch&project-name=data-fetch&repository-name=data-fetch)
+
+## How to use
+
+Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example:
+
+```bash
+npx create-next-app --example data-fetch data-fetch-app
+```
+
+```bash
+yarn create next-app --example data-fetch data-fetch-app
+```
+
+```bash
+pnpm create next-app --example data-fetch data-fetch-app
+```
+
+Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
diff --git a/examples/with-opentelemetry/app/layout.tsx b/examples/with-opentelemetry/app/layout.tsx
new file mode 100644
index 0000000000000..495209746f3ff
--- /dev/null
+++ b/examples/with-opentelemetry/app/layout.tsx
@@ -0,0 +1,12 @@
+export default function Layout({ children }) {
+ return (
+
+
+ Next.js with OpenTelemetry
+
+
+ {children}
+
+
+ )
+}
diff --git a/examples/with-opentelemetry/app/page.tsx b/examples/with-opentelemetry/app/page.tsx
new file mode 100644
index 0000000000000..cc9d3dd67267e
--- /dev/null
+++ b/examples/with-opentelemetry/app/page.tsx
@@ -0,0 +1,12 @@
+import Link from 'next/link'
+import { fetchGithubStars } from '../shared/fetch-github-stars'
+
+export default async function Page() {
+ const stars = await fetchGithubStars()
+ return (
+ <>
+ Next.js has {stars} ⭐️
+ How about preact?
+ >
+ )
+}
diff --git a/examples/with-opentelemetry/instrumentation.js b/examples/with-opentelemetry/instrumentation.js
new file mode 100644
index 0000000000000..f914d1bcb2fb5
--- /dev/null
+++ b/examples/with-opentelemetry/instrumentation.js
@@ -0,0 +1,23 @@
+export function register() {
+ if (process.env.NEXT_RUNTIME === 'nodejs') {
+ const opentelemetry = require('@opentelemetry/sdk-node')
+ const {
+ OTLPTraceExporter,
+ } = require('@opentelemetry/exporter-trace-otlp-http')
+ const { Resource } = require('@opentelemetry/resources')
+ const {
+ SemanticResourceAttributes,
+ } = require('@opentelemetry/semantic-conventions')
+
+ const sdk = new opentelemetry.NodeSDK({
+ traceExporter: new OTLPTraceExporter({}),
+ instrumentations: [],
+ resource: new Resource({
+ [SemanticResourceAttributes.SERVICE_NAME]: 'my-next-app',
+ [SemanticResourceAttributes.SERVICE_VERSION]: '1.0.0',
+ }),
+ })
+
+ sdk.start()
+ }
+}
diff --git a/examples/with-opentelemetry/next.config.js b/examples/with-opentelemetry/next.config.js
new file mode 100644
index 0000000000000..2c7924dfb80c7
--- /dev/null
+++ b/examples/with-opentelemetry/next.config.js
@@ -0,0 +1,6 @@
+module.exports = {
+ experimental: {
+ instrumentationHook: true,
+ appDir: true,
+ },
+}
diff --git a/examples/with-opentelemetry/package.json b/examples/with-opentelemetry/package.json
new file mode 100644
index 0000000000000..4f66a4f90618b
--- /dev/null
+++ b/examples/with-opentelemetry/package.json
@@ -0,0 +1,28 @@
+{
+ "private": true,
+ "scripts": {
+ "dev": "next",
+ "build": "next build",
+ "start": "next start",
+ "start:otel-verbose": "NEXT_OTEL_VERBOSE=1 next start"
+ },
+ "dependencies": {
+ "@opentelemetry/api": "1.4.0",
+ "@opentelemetry/auto-instrumentations-node": "0.36.3",
+ "@opentelemetry/exporter-trace-otlp-grpc": "0.35.1",
+ "@opentelemetry/exporter-trace-otlp-http": "0.35.1",
+ "@opentelemetry/instrumentation-fetch": "0.35.1",
+ "@opentelemetry/resources": "1.9.1",
+ "@opentelemetry/sdk-node": "0.35.1",
+ "@opentelemetry/sdk-trace-base": "1.9.1",
+ "@opentelemetry/semantic-conventions": "1.9.1",
+ "next": "latest",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0"
+ },
+ "devDependencies": {
+ "@types/node": "18.7.11",
+ "@types/react": "18.0.17",
+ "typescript": "4.7.4"
+ }
+}
diff --git a/examples/with-opentelemetry/pages/api/github-stars.ts b/examples/with-opentelemetry/pages/api/github-stars.ts
new file mode 100644
index 0000000000000..5d144e9be55b6
--- /dev/null
+++ b/examples/with-opentelemetry/pages/api/github-stars.ts
@@ -0,0 +1,11 @@
+// hello world api route
+import { NextApiRequest, NextApiResponse } from 'next'
+import { fetchGithubStars } from '../../shared/fetch-github-stars'
+
+export default async function handler(
+ req: NextApiRequest,
+ res: NextApiResponse
+) {
+ const stars = await fetchGithubStars()
+ res.status(200).json({ stars })
+}
diff --git a/examples/with-opentelemetry/pages/legacy.tsx b/examples/with-opentelemetry/pages/legacy.tsx
new file mode 100644
index 0000000000000..1621fb737fce9
--- /dev/null
+++ b/examples/with-opentelemetry/pages/legacy.tsx
@@ -0,0 +1,20 @@
+import Link from 'next/link'
+import { fetchGithubStars } from '../shared/fetch-github-stars'
+
+export async function getServerSideProps() {
+ const stars = await fetchGithubStars()
+ return {
+ props: {
+ stars,
+ },
+ }
+}
+
+export default function IndexPage({ stars }) {
+ return (
+ <>
+ Next.js has {stars} ⭐️
+ How about preact?
+ >
+ )
+}
diff --git a/examples/with-opentelemetry/shared/fetch-github-stars.ts b/examples/with-opentelemetry/shared/fetch-github-stars.ts
new file mode 100644
index 0000000000000..2a1223b82bd88
--- /dev/null
+++ b/examples/with-opentelemetry/shared/fetch-github-stars.ts
@@ -0,0 +1,15 @@
+import { trace } from '@opentelemetry/api'
+
+export async function fetchGithubStars() {
+ const span = trace.getTracer('nextjs-example').startSpan('fetchGithubStars')
+ return fetch('https://api.github.com/repos/vercel/next.js', {
+ next: {
+ revalidate: 0,
+ },
+ })
+ .then((res) => res.json())
+ .then((data) => data.stargazers_count)
+ .finally(() => {
+ span.end()
+ })
+}
diff --git a/examples/with-opentelemetry/tsconfig.json b/examples/with-opentelemetry/tsconfig.json
new file mode 100644
index 0000000000000..0eae8b96e9f90
--- /dev/null
+++ b/examples/with-opentelemetry/tsconfig.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": false,
+ "forceConsistentCasingInFileNames": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "strictNullChecks": true
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/packages/next/package.json b/packages/next/package.json
index dfb3af75d8369..892e4d8313456 100644
--- a/packages/next/package.json
+++ b/packages/next/package.json
@@ -91,7 +91,8 @@
"node-sass": "^6.0.0 || ^7.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
- "sass": "^1.3.0"
+ "sass": "^1.3.0",
+ "@opentelemetry/api": "^1.4.0"
},
"peerDependenciesMeta": {
"node-sass": {
@@ -102,6 +103,9 @@
},
"fibers": {
"optional": true
+ },
+ "@opentelemetry/api": {
+ "optional": true
}
},
"devDependencies": {
@@ -131,6 +135,7 @@
"@hapi/accept": "5.0.2",
"@napi-rs/cli": "2.14.7",
"@napi-rs/triples": "1.1.0",
+ "@opentelemetry/api": "1.4.0",
"@next/polyfill-module": "13.1.7-canary.25",
"@next/polyfill-nomodule": "13.1.7-canary.25",
"@next/react-dev-overlay": "13.1.7-canary.25",
@@ -153,6 +158,7 @@
"@types/cookie": "0.3.3",
"@types/cross-spawn": "6.0.0",
"@types/debug": "4.1.5",
+ "@types/express-serve-static-core": "4.17.33",
"@types/fresh": "0.5.0",
"@types/glob": "7.1.1",
"@types/jsonwebtoken": "9.0.0",
diff --git a/packages/next/src/build/index.ts b/packages/next/src/build/index.ts
index c5c4db6f5f689..d4255c6735595 100644
--- a/packages/next/src/build/index.ts
+++ b/packages/next/src/build/index.ts
@@ -956,6 +956,18 @@ export default async function build(
appDir ? path.join(SERVER_DIRECTORY, APP_PATHS_MANIFEST) : null,
path.join(SERVER_DIRECTORY, FONT_LOADER_MANIFEST + '.js'),
path.join(SERVER_DIRECTORY, FONT_LOADER_MANIFEST + '.json'),
+ ...(hasInstrumentationHook
+ ? [
+ path.join(
+ SERVER_DIRECTORY,
+ `${INSTRUMENTATION_HOOK_FILENAME}.js`
+ ),
+ path.join(
+ SERVER_DIRECTORY,
+ `edge-${INSTRUMENTATION_HOOK_FILENAME}.js`
+ ),
+ ]
+ : []),
]
.filter(nonNullable)
.map((file) => path.join(config.distDir, file)),
diff --git a/packages/next/src/build/webpack-config.ts b/packages/next/src/build/webpack-config.ts
index f0192de93d05e..ec8c97c24ed26 100644
--- a/packages/next/src/build/webpack-config.ts
+++ b/packages/next/src/build/webpack-config.ts
@@ -913,6 +913,11 @@ export default async function getBaseWebpackConfig(
const reactDir = path.dirname(require.resolve('react/package.json'))
const reactDomDir = path.dirname(require.resolve('react-dom/package.json'))
+ let hasOptionalOTELAPIPackage = false
+ try {
+ require('@opentelemetry/api')
+ hasOptionalOTELAPIPackage = true
+ } catch {}
const resolveConfig: webpack.Configuration['resolve'] = {
// Disable .mjs for node_modules bundling
@@ -955,6 +960,9 @@ export default async function getBaseWebpackConfig(
'next/dist/client/components/navigation',
[require.resolve('next/dist/client/components/headers')]:
'next/dist/client/components/headers',
+ '@opentelemetry/api': hasOptionalOTELAPIPackage
+ ? '@opentelemetry/api'
+ : 'next/dist/compiled/@opentelemetry/api',
}
: undefined),
@@ -1101,7 +1109,6 @@ export default async function getBaseWebpackConfig(
// Returning from the function in case the directory has already been added and traversed
if (topLevelFrameworkPaths.includes(directory)) return
topLevelFrameworkPaths.push(directory)
-
const dependencies = require(packageJsonPath).dependencies || {}
for (const name of Object.keys(dependencies)) {
addPackagePath(name, directory)
diff --git a/packages/next/src/compiled/@opentelemetry/api/LICENSE b/packages/next/src/compiled/@opentelemetry/api/LICENSE
new file mode 100644
index 0000000000000..261eeb9e9f8b2
--- /dev/null
+++ b/packages/next/src/compiled/@opentelemetry/api/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/packages/next/src/compiled/@opentelemetry/api/index.js b/packages/next/src/compiled/@opentelemetry/api/index.js
new file mode 100644
index 0000000000000..ad40c24a1ec83
--- /dev/null
+++ b/packages/next/src/compiled/@opentelemetry/api/index.js
@@ -0,0 +1 @@
+(()=>{"use strict";var e={959:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.ContextAPI=void 0;const n=r(190);const a=r(83);const o=r(669);const i="context";const c=new n.NoopContextManager;class ContextAPI{constructor(){}static getInstance(){if(!this._instance){this._instance=new ContextAPI}return this._instance}setGlobalContextManager(e){return(0,a.registerGlobal)(i,e,o.DiagAPI.instance())}active(){return this._getContextManager().active()}with(e,t,r,...n){return this._getContextManager().with(e,t,r,...n)}bind(e,t){return this._getContextManager().bind(e,t)}_getContextManager(){return(0,a.getGlobal)(i)||c}disable(){this._getContextManager().disable();(0,a.unregisterGlobal)(i,o.DiagAPI.instance())}}t.ContextAPI=ContextAPI},669:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.DiagAPI=void 0;const n=r(908);const a=r(802);const o=r(963);const i=r(83);const c="diag";class DiagAPI{constructor(){function _logProxy(e){return function(...t){const r=(0,i.getGlobal)("diag");if(!r)return;return r[e](...t)}}const e=this;const setLogger=(t,r={logLevel:o.DiagLogLevel.INFO})=>{var n,c,s;if(t===e){const t=new Error("Cannot use diag as the logger for itself. Please use a DiagLogger implementation like ConsoleDiagLogger or a custom implementation");e.error((n=t.stack)!==null&&n!==void 0?n:t.message);return false}if(typeof r==="number"){r={logLevel:r}}const u=(0,i.getGlobal)("diag");const l=(0,a.createLogLevelDiagLogger)((c=r.logLevel)!==null&&c!==void 0?c:o.DiagLogLevel.INFO,t);if(u&&!r.suppressOverrideMessage){const e=(s=(new Error).stack)!==null&&s!==void 0?s:"";u.warn(`Current logger will be overwritten from ${e}`);l.warn(`Current logger will overwrite one already registered from ${e}`)}return(0,i.registerGlobal)("diag",l,e,true)};e.setLogger=setLogger;e.disable=()=>{(0,i.unregisterGlobal)(c,e)};e.createComponentLogger=e=>new n.DiagComponentLogger(e);e.verbose=_logProxy("verbose");e.debug=_logProxy("debug");e.info=_logProxy("info");e.warn=_logProxy("warn");e.error=_logProxy("error")}static instance(){if(!this._instance){this._instance=new DiagAPI}return this._instance}}t.DiagAPI=DiagAPI},680:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.MetricsAPI=void 0;const n=r(429);const a=r(83);const o=r(669);const i="metrics";class MetricsAPI{constructor(){}static getInstance(){if(!this._instance){this._instance=new MetricsAPI}return this._instance}setGlobalMeterProvider(e){return(0,a.registerGlobal)(i,e,o.DiagAPI.instance())}getMeterProvider(){return(0,a.getGlobal)(i)||n.NOOP_METER_PROVIDER}getMeter(e,t,r){return this.getMeterProvider().getMeter(e,t,r)}disable(){(0,a.unregisterGlobal)(i,o.DiagAPI.instance())}}t.MetricsAPI=MetricsAPI},425:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.PropagationAPI=void 0;const n=r(83);const a=r(528);const o=r(675);const i=r(409);const c=r(957);const s=r(669);const u="propagation";const l=new a.NoopTextMapPropagator;class PropagationAPI{constructor(){this.createBaggage=c.createBaggage;this.getBaggage=i.getBaggage;this.getActiveBaggage=i.getActiveBaggage;this.setBaggage=i.setBaggage;this.deleteBaggage=i.deleteBaggage}static getInstance(){if(!this._instance){this._instance=new PropagationAPI}return this._instance}setGlobalPropagator(e){return(0,n.registerGlobal)(u,e,s.DiagAPI.instance())}inject(e,t,r=o.defaultTextMapSetter){return this._getGlobalPropagator().inject(e,t,r)}extract(e,t,r=o.defaultTextMapGetter){return this._getGlobalPropagator().extract(e,t,r)}fields(){return this._getGlobalPropagator().fields()}disable(){(0,n.unregisterGlobal)(u,s.DiagAPI.instance())}_getGlobalPropagator(){return(0,n.getGlobal)(u)||l}}t.PropagationAPI=PropagationAPI},463:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.TraceAPI=void 0;const n=r(83);const a=r(743);const o=r(566);const i=r(54);const c=r(669);const s="trace";class TraceAPI{constructor(){this._proxyTracerProvider=new a.ProxyTracerProvider;this.wrapSpanContext=o.wrapSpanContext;this.isSpanContextValid=o.isSpanContextValid;this.deleteSpan=i.deleteSpan;this.getSpan=i.getSpan;this.getActiveSpan=i.getActiveSpan;this.getSpanContext=i.getSpanContext;this.setSpan=i.setSpan;this.setSpanContext=i.setSpanContext}static getInstance(){if(!this._instance){this._instance=new TraceAPI}return this._instance}setGlobalTracerProvider(e){const t=(0,n.registerGlobal)(s,this._proxyTracerProvider,c.DiagAPI.instance());if(t){this._proxyTracerProvider.setDelegate(e)}return t}getTracerProvider(){return(0,n.getGlobal)(s)||this._proxyTracerProvider}getTracer(e,t){return this.getTracerProvider().getTracer(e,t)}disable(){(0,n.unregisterGlobal)(s,c.DiagAPI.instance());this._proxyTracerProvider=new a.ProxyTracerProvider}}t.TraceAPI=TraceAPI},409:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.deleteBaggage=t.setBaggage=t.getActiveBaggage=t.getBaggage=void 0;const n=r(959);const a=r(12);const o=(0,a.createContextKey)("OpenTelemetry Baggage Key");function getBaggage(e){return e.getValue(o)||undefined}t.getBaggage=getBaggage;function getActiveBaggage(){return getBaggage(n.ContextAPI.getInstance().active())}t.getActiveBaggage=getActiveBaggage;function setBaggage(e,t){return e.setValue(o,t)}t.setBaggage=setBaggage;function deleteBaggage(e){return e.deleteValue(o)}t.deleteBaggage=deleteBaggage},37:(e,t)=>{Object.defineProperty(t,"__esModule",{value:true});t.BaggageImpl=void 0;class BaggageImpl{constructor(e){this._entries=e?new Map(e):new Map}getEntry(e){const t=this._entries.get(e);if(!t){return undefined}return Object.assign({},t)}getAllEntries(){return Array.from(this._entries.entries()).map((([e,t])=>[e,t]))}setEntry(e,t){const r=new BaggageImpl(this._entries);r._entries.set(e,t);return r}removeEntry(e){const t=new BaggageImpl(this._entries);t._entries.delete(e);return t}removeEntries(...e){const t=new BaggageImpl(this._entries);for(const r of e){t._entries.delete(r)}return t}clear(){return new BaggageImpl}}t.BaggageImpl=BaggageImpl},714:(e,t)=>{Object.defineProperty(t,"__esModule",{value:true});t.baggageEntryMetadataSymbol=void 0;t.baggageEntryMetadataSymbol=Symbol("BaggageEntryMetadata")},957:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.baggageEntryMetadataFromString=t.createBaggage=void 0;const n=r(669);const a=r(37);const o=r(714);const i=n.DiagAPI.instance();function createBaggage(e={}){return new a.BaggageImpl(new Map(Object.entries(e)))}t.createBaggage=createBaggage;function baggageEntryMetadataFromString(e){if(typeof e!=="string"){i.error(`Cannot create baggage metadata from unknown type: ${typeof e}`);e=""}return{__TYPE__:o.baggageEntryMetadataSymbol,toString(){return e}}}t.baggageEntryMetadataFromString=baggageEntryMetadataFromString},493:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.context=void 0;const n=r(959);t.context=n.ContextAPI.getInstance()},190:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.NoopContextManager=void 0;const n=r(12);class NoopContextManager{active(){return n.ROOT_CONTEXT}with(e,t,r,...n){return t.call(r,...n)}bind(e,t){return t}enable(){return this}disable(){return this}}t.NoopContextManager=NoopContextManager},12:(e,t)=>{Object.defineProperty(t,"__esModule",{value:true});t.ROOT_CONTEXT=t.createContextKey=void 0;function createContextKey(e){return Symbol.for(e)}t.createContextKey=createContextKey;class BaseContext{constructor(e){const t=this;t._currentContext=e?new Map(e):new Map;t.getValue=e=>t._currentContext.get(e);t.setValue=(e,r)=>{const n=new BaseContext(t._currentContext);n._currentContext.set(e,r);return n};t.deleteValue=e=>{const r=new BaseContext(t._currentContext);r._currentContext.delete(e);return r}}}t.ROOT_CONTEXT=new BaseContext},305:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.diag=void 0;const n=r(669);t.diag=n.DiagAPI.instance()},908:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.DiagComponentLogger=void 0;const n=r(83);class DiagComponentLogger{constructor(e){this._namespace=e.namespace||"DiagComponentLogger"}debug(...e){return logProxy("debug",this._namespace,e)}error(...e){return logProxy("error",this._namespace,e)}info(...e){return logProxy("info",this._namespace,e)}warn(...e){return logProxy("warn",this._namespace,e)}verbose(...e){return logProxy("verbose",this._namespace,e)}}t.DiagComponentLogger=DiagComponentLogger;function logProxy(e,t,r){const a=(0,n.getGlobal)("diag");if(!a){return}r.unshift(t);return a[e](...r)}},479:(e,t)=>{Object.defineProperty(t,"__esModule",{value:true});t.DiagConsoleLogger=void 0;const r=[{n:"error",c:"error"},{n:"warn",c:"warn"},{n:"info",c:"info"},{n:"debug",c:"debug"},{n:"verbose",c:"trace"}];class DiagConsoleLogger{constructor(){function _consoleFunc(e){return function(...t){if(console){let r=console[e];if(typeof r!=="function"){r=console.log}if(typeof r==="function"){return r.apply(console,t)}}}}for(let e=0;e{Object.defineProperty(t,"__esModule",{value:true});t.createLogLevelDiagLogger=void 0;const n=r(963);function createLogLevelDiagLogger(e,t){if(en.DiagLogLevel.ALL){e=n.DiagLogLevel.ALL}t=t||{};function _filterFunc(r,n){const a=t[r];if(typeof a==="function"&&e>=n){return a.bind(t)}return function(){}}return{error:_filterFunc("error",n.DiagLogLevel.ERROR),warn:_filterFunc("warn",n.DiagLogLevel.WARN),info:_filterFunc("info",n.DiagLogLevel.INFO),debug:_filterFunc("debug",n.DiagLogLevel.DEBUG),verbose:_filterFunc("verbose",n.DiagLogLevel.VERBOSE)}}t.createLogLevelDiagLogger=createLogLevelDiagLogger},963:(e,t)=>{Object.defineProperty(t,"__esModule",{value:true});t.DiagLogLevel=void 0;var r;(function(e){e[e["NONE"]=0]="NONE";e[e["ERROR"]=30]="ERROR";e[e["WARN"]=50]="WARN";e[e["INFO"]=60]="INFO";e[e["DEBUG"]=70]="DEBUG";e[e["VERBOSE"]=80]="VERBOSE";e[e["ALL"]=9999]="ALL"})(r=t.DiagLogLevel||(t.DiagLogLevel={}))},83:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.unregisterGlobal=t.getGlobal=t.registerGlobal=void 0;const n=r(287);const a=r(236);const o=r(381);const i=a.VERSION.split(".")[0];const c=Symbol.for(`opentelemetry.js.api.${i}`);const s=n._globalThis;function registerGlobal(e,t,r,n=false){var o;const i=s[c]=(o=s[c])!==null&&o!==void 0?o:{version:a.VERSION};if(!n&&i[e]){const t=new Error(`@opentelemetry/api: Attempted duplicate registration of API: ${e}`);r.error(t.stack||t.message);return false}if(i.version!==a.VERSION){const e=new Error("@opentelemetry/api: All API registration versions must match");r.error(e.stack||e.message);return false}i[e]=t;r.debug(`@opentelemetry/api: Registered a global for ${e} v${a.VERSION}.`);return true}t.registerGlobal=registerGlobal;function getGlobal(e){var t,r;const n=(t=s[c])===null||t===void 0?void 0:t.version;if(!n||!(0,o.isCompatible)(n)){return}return(r=s[c])===null||r===void 0?void 0:r[e]}t.getGlobal=getGlobal;function unregisterGlobal(e,t){t.debug(`@opentelemetry/api: Unregistering a global for ${e} v${a.VERSION}.`);const r=s[c];if(r){delete r[e]}}t.unregisterGlobal=unregisterGlobal},381:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.isCompatible=t._makeCompatibilityCheck=void 0;const n=r(236);const a=/^(\d+)\.(\d+)\.(\d+)(-(.+))?$/;function _makeCompatibilityCheck(e){const t=new Set([e]);const r=new Set;const n=e.match(a);if(!n){return()=>false}const o={major:+n[1],minor:+n[2],patch:+n[3],prerelease:n[4]};if(o.prerelease!=null){return function isExactmatch(t){return t===e}}function _reject(e){r.add(e);return false}function _accept(e){t.add(e);return true}return function isCompatible(e){if(t.has(e)){return true}if(r.has(e)){return false}const n=e.match(a);if(!n){return _reject(e)}const i={major:+n[1],minor:+n[2],patch:+n[3],prerelease:n[4]};if(i.prerelease!=null){return _reject(e)}if(o.major!==i.major){return _reject(e)}if(o.major===0){if(o.minor===i.minor&&o.patch<=i.patch){return _accept(e)}return _reject(e)}if(o.minor<=i.minor){return _accept(e)}return _reject(e)}}t._makeCompatibilityCheck=_makeCompatibilityCheck;t.isCompatible=_makeCompatibilityCheck(n.VERSION)},39:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.metrics=void 0;const n=r(680);t.metrics=n.MetricsAPI.getInstance()},770:(e,t)=>{Object.defineProperty(t,"__esModule",{value:true});t.ValueType=void 0;var r;(function(e){e[e["INT"]=0]="INT";e[e["DOUBLE"]=1]="DOUBLE"})(r=t.ValueType||(t.ValueType={}))},745:(e,t)=>{Object.defineProperty(t,"__esModule",{value:true});t.createNoopMeter=t.NOOP_OBSERVABLE_UP_DOWN_COUNTER_METRIC=t.NOOP_OBSERVABLE_GAUGE_METRIC=t.NOOP_OBSERVABLE_COUNTER_METRIC=t.NOOP_UP_DOWN_COUNTER_METRIC=t.NOOP_HISTOGRAM_METRIC=t.NOOP_COUNTER_METRIC=t.NOOP_METER=t.NoopObservableUpDownCounterMetric=t.NoopObservableGaugeMetric=t.NoopObservableCounterMetric=t.NoopObservableMetric=t.NoopHistogramMetric=t.NoopUpDownCounterMetric=t.NoopCounterMetric=t.NoopMetric=t.NoopMeter=void 0;class NoopMeter{constructor(){}createHistogram(e,r){return t.NOOP_HISTOGRAM_METRIC}createCounter(e,r){return t.NOOP_COUNTER_METRIC}createUpDownCounter(e,r){return t.NOOP_UP_DOWN_COUNTER_METRIC}createObservableGauge(e,r){return t.NOOP_OBSERVABLE_GAUGE_METRIC}createObservableCounter(e,r){return t.NOOP_OBSERVABLE_COUNTER_METRIC}createObservableUpDownCounter(e,r){return t.NOOP_OBSERVABLE_UP_DOWN_COUNTER_METRIC}addBatchObservableCallback(e,t){}removeBatchObservableCallback(e){}}t.NoopMeter=NoopMeter;class NoopMetric{}t.NoopMetric=NoopMetric;class NoopCounterMetric extends NoopMetric{add(e,t){}}t.NoopCounterMetric=NoopCounterMetric;class NoopUpDownCounterMetric extends NoopMetric{add(e,t){}}t.NoopUpDownCounterMetric=NoopUpDownCounterMetric;class NoopHistogramMetric extends NoopMetric{record(e,t){}}t.NoopHistogramMetric=NoopHistogramMetric;class NoopObservableMetric{addCallback(e){}removeCallback(e){}}t.NoopObservableMetric=NoopObservableMetric;class NoopObservableCounterMetric extends NoopObservableMetric{}t.NoopObservableCounterMetric=NoopObservableCounterMetric;class NoopObservableGaugeMetric extends NoopObservableMetric{}t.NoopObservableGaugeMetric=NoopObservableGaugeMetric;class NoopObservableUpDownCounterMetric extends NoopObservableMetric{}t.NoopObservableUpDownCounterMetric=NoopObservableUpDownCounterMetric;t.NOOP_METER=new NoopMeter;t.NOOP_COUNTER_METRIC=new NoopCounterMetric;t.NOOP_HISTOGRAM_METRIC=new NoopHistogramMetric;t.NOOP_UP_DOWN_COUNTER_METRIC=new NoopUpDownCounterMetric;t.NOOP_OBSERVABLE_COUNTER_METRIC=new NoopObservableCounterMetric;t.NOOP_OBSERVABLE_GAUGE_METRIC=new NoopObservableGaugeMetric;t.NOOP_OBSERVABLE_UP_DOWN_COUNTER_METRIC=new NoopObservableUpDownCounterMetric;function createNoopMeter(){return t.NOOP_METER}t.createNoopMeter=createNoopMeter},429:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.NOOP_METER_PROVIDER=t.NoopMeterProvider=void 0;const n=r(745);class NoopMeterProvider{getMeter(e,t,r){return n.NOOP_METER}}t.NoopMeterProvider=NoopMeterProvider;t.NOOP_METER_PROVIDER=new NoopMeterProvider},287:function(e,t,r){var n=this&&this.__createBinding||(Object.create?function(e,t,r,n){if(n===undefined)n=r;Object.defineProperty(e,n,{enumerable:true,get:function(){return t[r]}})}:function(e,t,r,n){if(n===undefined)n=r;e[n]=t[r]});var a=this&&this.__exportStar||function(e,t){for(var r in e)if(r!=="default"&&!Object.prototype.hasOwnProperty.call(t,r))n(t,e,r)};Object.defineProperty(t,"__esModule",{value:true});a(r(951),t)},542:(e,t)=>{Object.defineProperty(t,"__esModule",{value:true});t._globalThis=void 0;t._globalThis=typeof globalThis==="object"?globalThis:global},951:function(e,t,r){var n=this&&this.__createBinding||(Object.create?function(e,t,r,n){if(n===undefined)n=r;Object.defineProperty(e,n,{enumerable:true,get:function(){return t[r]}})}:function(e,t,r,n){if(n===undefined)n=r;e[n]=t[r]});var a=this&&this.__exportStar||function(e,t){for(var r in e)if(r!=="default"&&!Object.prototype.hasOwnProperty.call(t,r))n(t,e,r)};Object.defineProperty(t,"__esModule",{value:true});a(r(542),t)},282:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.propagation=void 0;const n=r(425);t.propagation=n.PropagationAPI.getInstance()},528:(e,t)=>{Object.defineProperty(t,"__esModule",{value:true});t.NoopTextMapPropagator=void 0;class NoopTextMapPropagator{inject(e,t){}extract(e,t){return e}fields(){return[]}}t.NoopTextMapPropagator=NoopTextMapPropagator},675:(e,t)=>{Object.defineProperty(t,"__esModule",{value:true});t.defaultTextMapSetter=t.defaultTextMapGetter=void 0;t.defaultTextMapGetter={get(e,t){if(e==null){return undefined}return e[t]},keys(e){if(e==null){return[]}return Object.keys(e)}};t.defaultTextMapSetter={set(e,t,r){if(e==null){return}e[t]=r}}},873:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.trace=void 0;const n=r(463);t.trace=n.TraceAPI.getInstance()},115:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.NonRecordingSpan=void 0;const n=r(212);class NonRecordingSpan{constructor(e=n.INVALID_SPAN_CONTEXT){this._spanContext=e}spanContext(){return this._spanContext}setAttribute(e,t){return this}setAttributes(e){return this}addEvent(e,t){return this}setStatus(e){return this}updateName(e){return this}end(e){}isRecording(){return false}recordException(e,t){}}t.NonRecordingSpan=NonRecordingSpan},796:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.NoopTracer=void 0;const n=r(959);const a=r(54);const o=r(115);const i=r(566);const c=n.ContextAPI.getInstance();class NoopTracer{startSpan(e,t,r=c.active()){const n=Boolean(t===null||t===void 0?void 0:t.root);if(n){return new o.NonRecordingSpan}const s=r&&(0,a.getSpanContext)(r);if(isSpanContext(s)&&(0,i.isSpanContextValid)(s)){return new o.NonRecordingSpan(s)}else{return new o.NonRecordingSpan}}startActiveSpan(e,t,r,n){let o;let i;let s;if(arguments.length<2){return}else if(arguments.length===2){s=t}else if(arguments.length===3){o=t;s=r}else{o=t;i=r;s=n}const u=i!==null&&i!==void 0?i:c.active();const l=this.startSpan(e,o,u);const g=(0,a.setSpan)(u,l);return c.with(g,s,undefined,l)}}t.NoopTracer=NoopTracer;function isSpanContext(e){return typeof e==="object"&&typeof e["spanId"]==="string"&&typeof e["traceId"]==="string"&&typeof e["traceFlags"]==="number"}},69:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.NoopTracerProvider=void 0;const n=r(796);class NoopTracerProvider{getTracer(e,t,r){return new n.NoopTracer}}t.NoopTracerProvider=NoopTracerProvider},889:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.ProxyTracer=void 0;const n=r(796);const a=new n.NoopTracer;class ProxyTracer{constructor(e,t,r,n){this._provider=e;this.name=t;this.version=r;this.options=n}startSpan(e,t,r){return this._getTracer().startSpan(e,t,r)}startActiveSpan(e,t,r,n){const a=this._getTracer();return Reflect.apply(a.startActiveSpan,a,arguments)}_getTracer(){if(this._delegate){return this._delegate}const e=this._provider.getDelegateTracer(this.name,this.version,this.options);if(!e){return a}this._delegate=e;return this._delegate}}t.ProxyTracer=ProxyTracer},743:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.ProxyTracerProvider=void 0;const n=r(889);const a=r(69);const o=new a.NoopTracerProvider;class ProxyTracerProvider{getTracer(e,t,r){var a;return(a=this.getDelegateTracer(e,t,r))!==null&&a!==void 0?a:new n.ProxyTracer(this,e,t,r)}getDelegate(){var e;return(e=this._delegate)!==null&&e!==void 0?e:o}setDelegate(e){this._delegate=e}getDelegateTracer(e,t,r){var n;return(n=this._delegate)===null||n===void 0?void 0:n.getTracer(e,t,r)}}t.ProxyTracerProvider=ProxyTracerProvider},847:(e,t)=>{Object.defineProperty(t,"__esModule",{value:true});t.SamplingDecision=void 0;var r;(function(e){e[e["NOT_RECORD"]=0]="NOT_RECORD";e[e["RECORD"]=1]="RECORD";e[e["RECORD_AND_SAMPLED"]=2]="RECORD_AND_SAMPLED"})(r=t.SamplingDecision||(t.SamplingDecision={}))},54:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.getSpanContext=t.setSpanContext=t.deleteSpan=t.setSpan=t.getActiveSpan=t.getSpan=void 0;const n=r(12);const a=r(115);const o=r(959);const i=(0,n.createContextKey)("OpenTelemetry Context Key SPAN");function getSpan(e){return e.getValue(i)||undefined}t.getSpan=getSpan;function getActiveSpan(){return getSpan(o.ContextAPI.getInstance().active())}t.getActiveSpan=getActiveSpan;function setSpan(e,t){return e.setValue(i,t)}t.setSpan=setSpan;function deleteSpan(e){return e.deleteValue(i)}t.deleteSpan=deleteSpan;function setSpanContext(e,t){return setSpan(e,new a.NonRecordingSpan(t))}t.setSpanContext=setSpanContext;function getSpanContext(e){var t;return(t=getSpan(e))===null||t===void 0?void 0:t.spanContext()}t.getSpanContext=getSpanContext},79:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.TraceStateImpl=void 0;const n=r(825);const a=32;const o=512;const i=",";const c="=";class TraceStateImpl{constructor(e){this._internalState=new Map;if(e)this._parse(e)}set(e,t){const r=this._clone();if(r._internalState.has(e)){r._internalState.delete(e)}r._internalState.set(e,t);return r}unset(e){const t=this._clone();t._internalState.delete(e);return t}get(e){return this._internalState.get(e)}serialize(){return this._keys().reduce(((e,t)=>{e.push(t+c+this.get(t));return e}),[]).join(i)}_parse(e){if(e.length>o)return;this._internalState=e.split(i).reverse().reduce(((e,t)=>{const r=t.trim();const a=r.indexOf(c);if(a!==-1){const o=r.slice(0,a);const i=r.slice(a+1,t.length);if((0,n.validateKey)(o)&&(0,n.validateValue)(i)){e.set(o,i)}else{}}return e}),new Map);if(this._internalState.size>a){this._internalState=new Map(Array.from(this._internalState.entries()).reverse().slice(0,a))}}_keys(){return Array.from(this._internalState.keys()).reverse()}_clone(){const e=new TraceStateImpl;e._internalState=new Map(this._internalState);return e}}t.TraceStateImpl=TraceStateImpl},825:(e,t)=>{Object.defineProperty(t,"__esModule",{value:true});t.validateValue=t.validateKey=void 0;const r="[_0-9a-z-*/]";const n=`[a-z]${r}{0,255}`;const a=`[a-z0-9]${r}{0,240}@[a-z]${r}{0,13}`;const o=new RegExp(`^(?:${n}|${a})$`);const i=/^[ -~]{0,255}[!-~]$/;const c=/,|=/;function validateKey(e){return o.test(e)}t.validateKey=validateKey;function validateValue(e){return i.test(e)&&!c.test(e)}t.validateValue=validateValue},339:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.createTraceState=void 0;const n=r(79);function createTraceState(e){return new n.TraceStateImpl(e)}t.createTraceState=createTraceState},212:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.INVALID_SPAN_CONTEXT=t.INVALID_TRACEID=t.INVALID_SPANID=void 0;const n=r(785);t.INVALID_SPANID="0000000000000000";t.INVALID_TRACEID="00000000000000000000000000000000";t.INVALID_SPAN_CONTEXT={traceId:t.INVALID_TRACEID,spanId:t.INVALID_SPANID,traceFlags:n.TraceFlags.NONE}},155:(e,t)=>{Object.defineProperty(t,"__esModule",{value:true});t.SpanKind=void 0;var r;(function(e){e[e["INTERNAL"]=0]="INTERNAL";e[e["SERVER"]=1]="SERVER";e[e["CLIENT"]=2]="CLIENT";e[e["PRODUCER"]=3]="PRODUCER";e[e["CONSUMER"]=4]="CONSUMER"})(r=t.SpanKind||(t.SpanKind={}))},566:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:true});t.wrapSpanContext=t.isSpanContextValid=t.isValidSpanId=t.isValidTraceId=void 0;const n=r(212);const a=r(115);const o=/^([0-9a-f]{32})$/i;const i=/^[0-9a-f]{16}$/i;function isValidTraceId(e){return o.test(e)&&e!==n.INVALID_TRACEID}t.isValidTraceId=isValidTraceId;function isValidSpanId(e){return i.test(e)&&e!==n.INVALID_SPANID}t.isValidSpanId=isValidSpanId;function isSpanContextValid(e){return isValidTraceId(e.traceId)&&isValidSpanId(e.spanId)}t.isSpanContextValid=isSpanContextValid;function wrapSpanContext(e){return new a.NonRecordingSpan(e)}t.wrapSpanContext=wrapSpanContext},645:(e,t)=>{Object.defineProperty(t,"__esModule",{value:true});t.SpanStatusCode=void 0;var r;(function(e){e[e["UNSET"]=0]="UNSET";e[e["OK"]=1]="OK";e[e["ERROR"]=2]="ERROR"})(r=t.SpanStatusCode||(t.SpanStatusCode={}))},785:(e,t)=>{Object.defineProperty(t,"__esModule",{value:true});t.TraceFlags=void 0;var r;(function(e){e[e["NONE"]=0]="NONE";e[e["SAMPLED"]=1]="SAMPLED"})(r=t.TraceFlags||(t.TraceFlags={}))},236:(e,t)=>{Object.defineProperty(t,"__esModule",{value:true});t.VERSION=void 0;t.VERSION="1.4.0"}};var t={};function __nccwpck_require__(r){var n=t[r];if(n!==undefined){return n.exports}var a=t[r]={exports:{}};var o=true;try{e[r].call(a.exports,a,a.exports,__nccwpck_require__);o=false}finally{if(o)delete t[r]}return a.exports}if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=__dirname+"/";var r={};(()=>{var e=r;Object.defineProperty(e,"__esModule",{value:true});e.trace=e.propagation=e.metrics=e.diag=e.context=e.INVALID_SPAN_CONTEXT=e.INVALID_TRACEID=e.INVALID_SPANID=e.isValidSpanId=e.isValidTraceId=e.isSpanContextValid=e.createTraceState=e.TraceFlags=e.SpanStatusCode=e.SpanKind=e.SamplingDecision=e.ProxyTracerProvider=e.ProxyTracer=e.defaultTextMapSetter=e.defaultTextMapGetter=e.ValueType=e.createNoopMeter=e.DiagLogLevel=e.DiagConsoleLogger=e.ROOT_CONTEXT=e.createContextKey=e.baggageEntryMetadataFromString=void 0;var t=__nccwpck_require__(957);Object.defineProperty(e,"baggageEntryMetadataFromString",{enumerable:true,get:function(){return t.baggageEntryMetadataFromString}});var n=__nccwpck_require__(12);Object.defineProperty(e,"createContextKey",{enumerable:true,get:function(){return n.createContextKey}});Object.defineProperty(e,"ROOT_CONTEXT",{enumerable:true,get:function(){return n.ROOT_CONTEXT}});var a=__nccwpck_require__(479);Object.defineProperty(e,"DiagConsoleLogger",{enumerable:true,get:function(){return a.DiagConsoleLogger}});var o=__nccwpck_require__(963);Object.defineProperty(e,"DiagLogLevel",{enumerable:true,get:function(){return o.DiagLogLevel}});var i=__nccwpck_require__(745);Object.defineProperty(e,"createNoopMeter",{enumerable:true,get:function(){return i.createNoopMeter}});var c=__nccwpck_require__(770);Object.defineProperty(e,"ValueType",{enumerable:true,get:function(){return c.ValueType}});var s=__nccwpck_require__(675);Object.defineProperty(e,"defaultTextMapGetter",{enumerable:true,get:function(){return s.defaultTextMapGetter}});Object.defineProperty(e,"defaultTextMapSetter",{enumerable:true,get:function(){return s.defaultTextMapSetter}});var u=__nccwpck_require__(889);Object.defineProperty(e,"ProxyTracer",{enumerable:true,get:function(){return u.ProxyTracer}});var l=__nccwpck_require__(743);Object.defineProperty(e,"ProxyTracerProvider",{enumerable:true,get:function(){return l.ProxyTracerProvider}});var g=__nccwpck_require__(847);Object.defineProperty(e,"SamplingDecision",{enumerable:true,get:function(){return g.SamplingDecision}});var p=__nccwpck_require__(155);Object.defineProperty(e,"SpanKind",{enumerable:true,get:function(){return p.SpanKind}});var _=__nccwpck_require__(645);Object.defineProperty(e,"SpanStatusCode",{enumerable:true,get:function(){return _.SpanStatusCode}});var d=__nccwpck_require__(785);Object.defineProperty(e,"TraceFlags",{enumerable:true,get:function(){return d.TraceFlags}});var f=__nccwpck_require__(339);Object.defineProperty(e,"createTraceState",{enumerable:true,get:function(){return f.createTraceState}});var b=__nccwpck_require__(566);Object.defineProperty(e,"isSpanContextValid",{enumerable:true,get:function(){return b.isSpanContextValid}});Object.defineProperty(e,"isValidTraceId",{enumerable:true,get:function(){return b.isValidTraceId}});Object.defineProperty(e,"isValidSpanId",{enumerable:true,get:function(){return b.isValidSpanId}});var v=__nccwpck_require__(212);Object.defineProperty(e,"INVALID_SPANID",{enumerable:true,get:function(){return v.INVALID_SPANID}});Object.defineProperty(e,"INVALID_TRACEID",{enumerable:true,get:function(){return v.INVALID_TRACEID}});Object.defineProperty(e,"INVALID_SPAN_CONTEXT",{enumerable:true,get:function(){return v.INVALID_SPAN_CONTEXT}});const O=__nccwpck_require__(493);Object.defineProperty(e,"context",{enumerable:true,get:function(){return O.context}});const P=__nccwpck_require__(305);Object.defineProperty(e,"diag",{enumerable:true,get:function(){return P.diag}});const N=__nccwpck_require__(39);Object.defineProperty(e,"metrics",{enumerable:true,get:function(){return N.metrics}});const S=__nccwpck_require__(282);Object.defineProperty(e,"propagation",{enumerable:true,get:function(){return S.propagation}});const C=__nccwpck_require__(873);Object.defineProperty(e,"trace",{enumerable:true,get:function(){return C.trace}});e["default"]={context:O.context,diag:P.diag,metrics:N.metrics,propagation:S.propagation,trace:C.trace}})();module.exports=r})();
\ No newline at end of file
diff --git a/packages/next/src/compiled/@opentelemetry/api/package.json b/packages/next/src/compiled/@opentelemetry/api/package.json
new file mode 100644
index 0000000000000..8d1e180d1a87a
--- /dev/null
+++ b/packages/next/src/compiled/@opentelemetry/api/package.json
@@ -0,0 +1 @@
+{"name":"@opentelemetry/api","main":"index.js","author":"OpenTelemetry Authors","license":"Apache-2.0"}
diff --git a/packages/next/src/server/app-render.tsx b/packages/next/src/server/app-render.tsx
index 9022a0f90a966..483d4a53e9caf 100644
--- a/packages/next/src/server/app-render.tsx
+++ b/packages/next/src/server/app-render.tsx
@@ -56,6 +56,8 @@ import { isNotFoundError } from '../client/components/not-found'
import { isRedirectError } from '../client/components/redirect'
import { NEXT_DYNAMIC_NO_SSR_CODE } from '../shared/lib/lazy-dynamic/no-ssr-error'
import { patchFetch } from './lib/patch-fetch'
+import { AppRenderSpan } from './lib/trace/constants'
+import { getTracer } from './lib/trace/tracer'
const isEdgeRuntime = process.env.NEXT_RUNTIME === 'edge'
@@ -725,9 +727,11 @@ function getScriptNonceFromHeader(cspHeaderValue: string): string | undefined {
}
async function renderToString(element: React.ReactElement) {
- const renderStream = await ReactDOMServer.renderToReadableStream(element)
- await renderStream.allReady
- return streamToString(renderStream)
+ return getTracer().trace(AppRenderSpan.renderToString, async () => {
+ const renderStream = await ReactDOMServer.renderToReadableStream(element)
+ await renderStream.allReady
+ return streamToString(renderStream)
+ })
}
export async function renderToHTMLOrFlight(
@@ -1791,124 +1795,127 @@ export async function renderToHTMLOrFlight(
)
}
- const bodyResult = async () => {
- const polyfills = buildManifest.polyfillFiles
- .filter(
- (polyfill) =>
- polyfill.endsWith('.js') && !polyfill.endsWith('.module.js')
+ const bodyResult = getTracer().wrap(
+ AppRenderSpan.getBodyResult,
+ async () => {
+ const polyfills = buildManifest.polyfillFiles
+ .filter(
+ (polyfill) =>
+ polyfill.endsWith('.js') && !polyfill.endsWith('.module.js')
+ )
+ .map((polyfill) => ({
+ src: `${assetPrefix}/_next/${polyfill}`,
+ integrity: subresourceIntegrityManifest?.[polyfill],
+ }))
+
+ const content = (
+
+
+
)
- .map((polyfill) => ({
- src: `${assetPrefix}/_next/${polyfill}`,
- integrity: subresourceIntegrityManifest?.[polyfill],
- }))
-
- const content = (
-
-
-
- )
- let polyfillsFlushed = false
- const getServerInsertedHTML = (): Promise => {
- const flushed = renderToString(
- <>
- {Array.from(serverInsertedHTMLCallbacks).map((callback) =>
- callback()
- )}
- {polyfillsFlushed
- ? null
- : polyfills?.map((polyfill) => {
- return (
-
- )
- })}
- >
- )
- polyfillsFlushed = true
- return flushed
- }
+ let polyfillsFlushed = false
+ const getServerInsertedHTML = (): Promise => {
+ const flushed = renderToString(
+ <>
+ {Array.from(serverInsertedHTMLCallbacks).map((callback) =>
+ callback()
+ )}
+ {polyfillsFlushed
+ ? null
+ : polyfills?.map((polyfill) => {
+ return (
+
+ )
+ })}
+ >
+ )
+ polyfillsFlushed = true
+ return flushed
+ }
- try {
- const renderStream = await renderToInitialStream({
- ReactDOMServer,
- element: content,
- streamOptions: {
- onError: htmlRendererErrorHandler,
- nonce,
- // Include hydration scripts in the HTML
- bootstrapScripts: [
- ...(subresourceIntegrityManifest
+ try {
+ const renderStream = await renderToInitialStream({
+ ReactDOMServer,
+ element: content,
+ streamOptions: {
+ onError: htmlRendererErrorHandler,
+ nonce,
+ // Include hydration scripts in the HTML
+ bootstrapScripts: [
+ ...(subresourceIntegrityManifest
+ ? buildManifest.rootMainFiles.map((src) => ({
+ src: `${assetPrefix}/_next/` + src,
+ integrity: subresourceIntegrityManifest[src],
+ }))
+ : buildManifest.rootMainFiles.map(
+ (src) => `${assetPrefix}/_next/` + src
+ )),
+ ],
+ },
+ })
+
+ const result = await continueFromInitialStream(renderStream, {
+ dataStream: serverComponentsInlinedTransformStream?.readable,
+ generateStaticHTML:
+ staticGenerationStore.isStaticGeneration || generateStaticHTML,
+ getServerInsertedHTML,
+ serverInsertedHTMLToHead: true,
+ ...validateRootLayout,
+ })
+
+ return result
+ } catch (err: any) {
+ const shouldNotIndex = isNotFoundError(err)
+ if (isNotFoundError(err)) {
+ res.statusCode = 404
+ }
+ if (isRedirectError(err)) {
+ res.statusCode = 307
+ }
+
+ const renderStream = await renderToInitialStream({
+ ReactDOMServer,
+ element: (
+
+
+ {shouldNotIndex ? (
+
+ ) : null}
+
+
+
+ ),
+ streamOptions: {
+ nonce,
+ // Include hydration scripts in the HTML
+ bootstrapScripts: subresourceIntegrityManifest
? buildManifest.rootMainFiles.map((src) => ({
src: `${assetPrefix}/_next/` + src,
integrity: subresourceIntegrityManifest[src],
}))
: buildManifest.rootMainFiles.map(
(src) => `${assetPrefix}/_next/` + src
- )),
- ],
- },
- })
-
- const result = await continueFromInitialStream(renderStream, {
- dataStream: serverComponentsInlinedTransformStream?.readable,
- generateStaticHTML:
- staticGenerationStore.isStaticGeneration || generateStaticHTML,
- getServerInsertedHTML,
- serverInsertedHTMLToHead: true,
- ...validateRootLayout,
- })
+ ),
+ },
+ })
- return result
- } catch (err: any) {
- const shouldNotIndex = isNotFoundError(err)
- if (isNotFoundError(err)) {
- res.statusCode = 404
- }
- if (isRedirectError(err)) {
- res.statusCode = 307
+ return await continueFromInitialStream(renderStream, {
+ dataStream: serverComponentsInlinedTransformStream?.readable,
+ generateStaticHTML: staticGenerationStore.isStaticGeneration,
+ getServerInsertedHTML,
+ serverInsertedHTMLToHead: true,
+ ...validateRootLayout,
+ })
}
-
- const renderStream = await renderToInitialStream({
- ReactDOMServer,
- element: (
-
-
- {shouldNotIndex ? (
-
- ) : null}
-
-
-
- ),
- streamOptions: {
- nonce,
- // Include hydration scripts in the HTML
- bootstrapScripts: subresourceIntegrityManifest
- ? buildManifest.rootMainFiles.map((src) => ({
- src: `${assetPrefix}/_next/` + src,
- integrity: subresourceIntegrityManifest[src],
- }))
- : buildManifest.rootMainFiles.map(
- (src) => `${assetPrefix}/_next/` + src
- ),
- },
- })
-
- return await continueFromInitialStream(renderStream, {
- dataStream: serverComponentsInlinedTransformStream?.readable,
- generateStaticHTML: staticGenerationStore.isStaticGeneration,
- getServerInsertedHTML,
- serverInsertedHTMLToHead: true,
- ...validateRootLayout,
- })
}
- }
+ )
const renderResult = new RenderResult(await bodyResult())
if (staticGenerationStore.pendingRevalidates) {
diff --git a/packages/next/src/server/base-server.ts b/packages/next/src/server/base-server.ts
index b6607acd7c5df..f689a86e648cb 100644
--- a/packages/next/src/server/base-server.ts
+++ b/packages/next/src/server/base-server.ts
@@ -92,6 +92,8 @@ import { AppRouteRouteMatcherProvider } from './future/route-matcher-providers/a
import { PagesAPIRouteMatcherProvider } from './future/route-matcher-providers/pages-api-route-matcher-provider'
import { PagesRouteMatcherProvider } from './future/route-matcher-providers/pages-route-matcher-provider'
import { ServerManifestLoader } from './future/route-matcher-providers/helpers/manifest-loaders/server-manifest-loader'
+import { getTracer } from './lib/trace/tracer'
+import { BaseServerSpan } from './lib/trace/constants'
import { sendResponse } from './future/route-handlers/app-route-route-handler'
export type FindComponentsResult = {
@@ -510,7 +512,18 @@ export default abstract class Server {
console.error(err)
}
- private async handleRequest(
+ public async handleRequest(
+ req: BaseNextRequest,
+ res: BaseNextResponse,
+ parsedUrl?: NextUrlWithParsedQuery
+ ): Promise {
+ return getTracer().trace(
+ BaseServerSpan.handleRequest,
+ async () => await this.handleRequestImpl(req, res, parsedUrl)
+ )
+ }
+
+ private async handleRequestImpl(
req: BaseNextRequest,
res: BaseNextResponse,
parsedUrl?: NextUrlWithParsedQuery
@@ -893,6 +906,16 @@ export default abstract class Server {
req: BaseNextRequest,
res: BaseNextResponse,
parsedUrl: UrlWithParsedQuery
+ ): Promise {
+ return getTracer().trace(BaseServerSpan.run, async () =>
+ this.runImpl(req, res, parsedUrl)
+ )
+ }
+
+ private async runImpl(
+ req: BaseNextRequest,
+ res: BaseNextResponse,
+ parsedUrl: UrlWithParsedQuery
): Promise {
this.handleCompression(req, res)
@@ -933,6 +956,20 @@ export default abstract class Server {
pathname: string
query: NextParsedUrlQuery
}
+ ): Promise {
+ return getTracer().trace(BaseServerSpan.pipe, async () =>
+ this.pipeImpl(fn, partialContext)
+ )
+ }
+
+ private async pipeImpl(
+ fn: (ctx: RequestContext) => Promise,
+ partialContext: {
+ req: BaseNextRequest
+ res: BaseNextResponse
+ pathname: string
+ query: NextParsedUrlQuery
+ }
): Promise {
const isBotRequest = isBot(partialContext.req.headers['user-agent'] || '')
const ctx = {
@@ -994,6 +1031,19 @@ export default abstract class Server {
query: NextParsedUrlQuery = {},
parsedUrl?: NextUrlWithParsedQuery,
internalRender = false
+ ): Promise {
+ return getTracer().trace(BaseServerSpan.render, async () =>
+ this.renderImpl(req, res, pathname, query, parsedUrl, internalRender)
+ )
+ }
+
+ private async renderImpl(
+ req: BaseNextRequest,
+ res: BaseNextResponse,
+ pathname: string,
+ query: NextParsedUrlQuery = {},
+ parsedUrl?: NextUrlWithParsedQuery,
+ internalRender = false
): Promise {
if (!pathname.startsWith('/')) {
console.warn(
@@ -1071,6 +1121,20 @@ export default abstract class Server {
}
private async renderToResponseWithComponents(
+ requestContext: RequestContext,
+ findComponentsResult: FindComponentsResult
+ ): Promise {
+ return getTracer().trace(
+ BaseServerSpan.renderToResponseWithComponents,
+ async () =>
+ this.renderToResponseWithComponentsImpl(
+ requestContext,
+ findComponentsResult
+ )
+ )
+ }
+
+ private async renderToResponseWithComponentsImpl(
{ req, res, pathname, renderOpts: opts }: RequestContext,
{ components, query }: FindComponentsResult
): Promise {
@@ -1849,6 +1913,14 @@ export default abstract class Server {
private async renderToResponse(
ctx: RequestContext
+ ): Promise {
+ return getTracer().trace(BaseServerSpan.renderToResponse, async () => {
+ return this.renderToResponseImpl(ctx)
+ })
+ }
+
+ private async renderToResponseImpl(
+ ctx: RequestContext
): Promise {
const { res, query, pathname } = ctx
let page = pathname
@@ -1970,6 +2042,17 @@ export default abstract class Server {
res: BaseNextResponse,
pathname: string,
query: ParsedUrlQuery = {}
+ ): Promise {
+ return getTracer().trace(BaseServerSpan.renderToHTML, async () => {
+ return this.renderToHTMLImpl(req, res, pathname, query)
+ })
+ }
+
+ private async renderToHTMLImpl(
+ req: BaseNextRequest,
+ res: BaseNextResponse,
+ pathname: string,
+ query: ParsedUrlQuery = {}
): Promise {
return this.getStaticHTML((ctx) => this.renderToResponse(ctx), {
req,
@@ -1986,6 +2069,19 @@ export default abstract class Server {
pathname: string,
query: NextParsedUrlQuery = {},
setHeaders = true
+ ): Promise {
+ return getTracer().trace(BaseServerSpan.renderError, async () => {
+ return this.renderErrorImpl(err, req, res, pathname, query, setHeaders)
+ })
+ }
+
+ private async renderErrorImpl(
+ err: Error | null,
+ req: BaseNextRequest,
+ res: BaseNextResponse,
+ pathname: string,
+ query: NextParsedUrlQuery = {},
+ setHeaders = true
): Promise {
if (setHeaders) {
res.setHeader(
@@ -2015,6 +2111,15 @@ export default abstract class Server {
private async renderErrorToResponse(
ctx: RequestContext,
err: Error | null
+ ): Promise {
+ return getTracer().trace(BaseServerSpan.renderErrorToResponse, async () => {
+ return this.renderErrorToResponseImpl(ctx, err)
+ })
+ }
+
+ private async renderErrorToResponseImpl(
+ ctx: RequestContext,
+ err: Error | null
): Promise {
const { res, query } = ctx
try {
diff --git a/packages/next/src/server/lib/patch-fetch.ts b/packages/next/src/server/lib/patch-fetch.ts
index 9ce85d5e7004a..d3412bbdd379f 100644
--- a/packages/next/src/server/lib/patch-fetch.ts
+++ b/packages/next/src/server/lib/patch-fetch.ts
@@ -1,4 +1,6 @@
import type { StaticGenerationAsyncStorage } from '../../client/components/static-generation-async-storage'
+import { AppRenderSpan } from './trace/constants'
+import { getTracer, SpanKind } from './trace/tracer'
const isEdgeRuntime = process.env.NEXT_RUNTIME === 'edge'
const CACHE_ONE_YEAR = 31536000
@@ -17,165 +19,143 @@ export function patchFetch({
const { DynamicServerError } = serverHooks
const originFetch = fetch
- // @ts-ignore
- // eslint-disable-next-line no-native-reassign
- fetch = async (input: RequestInfo | URL, init: RequestInit | undefined) => {
- const staticGenerationStore = staticGenerationAsyncStorage.getStore()
-
- // If the staticGenerationStore is not available, we can't do any special
- // treatment of fetch, therefore fallback to the original fetch
- // implementation.
- if (!staticGenerationStore) {
- return originFetch(input, init)
- }
-
- let revalidate: number | undefined | boolean
-
- if (typeof init?.next?.revalidate === 'number') {
- revalidate = init.next.revalidate
- }
- if (init?.next?.revalidate === false) {
- revalidate = CACHE_ONE_YEAR
- }
+ // @ts-expect-error - we're patching fetch
+ // eslint-disable-next-line no-native-reassign
+ fetch = getTracer().wrap(
+ AppRenderSpan.fetch,
+ {
+ kind: SpanKind.CLIENT,
+ },
+ async (input: RequestInfo | URL, init: RequestInit | undefined) => {
+ const staticGenerationStore = staticGenerationAsyncStorage.getStore()
+
+ // If the staticGenerationStore is not available, we can't do any special
+ // treatment of fetch, therefore fallback to the original fetch
+ // implementation.
+ if (!staticGenerationStore) {
+ return originFetch(input, init)
+ }
- if (
- !staticGenerationStore.revalidate ||
- (typeof revalidate === 'number' &&
- revalidate < staticGenerationStore.revalidate)
- ) {
- staticGenerationStore.revalidate = revalidate
- }
+ let revalidate: number | undefined | boolean
- let cacheKey: string | undefined
+ if (typeof init?.next?.revalidate === 'number') {
+ revalidate = init.next.revalidate
+ }
- const doOriginalFetch = async () => {
- return originFetch(input, init).then(async (res) => {
- if (
- staticGenerationStore.incrementalCache &&
- cacheKey &&
- typeof revalidate === 'number' &&
- revalidate > 0
- ) {
- const clonedRes = res.clone()
+ if (init?.next?.revalidate === false) {
+ revalidate = CACHE_ONE_YEAR
+ }
- let base64Body = ''
+ if (
+ !staticGenerationStore.revalidate ||
+ (typeof revalidate === 'number' &&
+ revalidate < staticGenerationStore.revalidate)
+ ) {
+ staticGenerationStore.revalidate = revalidate
+ }
- if (process.env.NEXT_RUNTIME === 'edge') {
- let string = ''
- new Uint8Array(await clonedRes.arrayBuffer()).forEach((byte) => {
- string += String.fromCharCode(byte)
- })
- base64Body = btoa(string)
- } else {
- base64Body = Buffer.from(await clonedRes.arrayBuffer()).toString(
- 'base64'
- )
- }
+ let cacheKey: string | undefined
+
+ const doOriginalFetch = async () => {
+ return originFetch(input, init).then(async (res) => {
+ if (
+ staticGenerationStore.incrementalCache &&
+ cacheKey &&
+ typeof revalidate === 'number' &&
+ revalidate > 0
+ ) {
+ const clonedRes = res.clone()
+
+ let base64Body = ''
+
+ if (process.env.NEXT_RUNTIME === 'edge') {
+ let string = ''
+ new Uint8Array(await clonedRes.arrayBuffer()).forEach((byte) => {
+ string += String.fromCharCode(byte)
+ })
+ base64Body = btoa(string)
+ } else {
+ base64Body = Buffer.from(await clonedRes.arrayBuffer()).toString(
+ 'base64'
+ )
+ }
- await staticGenerationStore.incrementalCache.set(
- cacheKey,
- {
- kind: 'FETCH',
- data: {
- headers: Object.fromEntries(clonedRes.headers.entries()),
- body: base64Body,
+ await staticGenerationStore.incrementalCache.set(
+ cacheKey,
+ {
+ kind: 'FETCH',
+ data: {
+ headers: Object.fromEntries(clonedRes.headers.entries()),
+ body: base64Body,
+ },
+ revalidate,
},
revalidate,
- },
- revalidate,
- true
- )
- }
- return res
- })
- }
-
- if (
- staticGenerationStore.incrementalCache &&
- typeof revalidate === 'number' &&
- revalidate > 0
- ) {
- cacheKey = await staticGenerationStore.incrementalCache.fetchCacheKey(
- input.toString(),
- init
- )
- const entry = await staticGenerationStore.incrementalCache.get(
- cacheKey,
- true
- )
-
- if (entry?.value && entry.value.kind === 'FETCH') {
- // when stale and is revalidating we wait for fresh data
- // so the revalidated entry has the updated data
- if (!staticGenerationStore.isRevalidate || !entry.isStale) {
- if (entry.isStale) {
- if (!staticGenerationStore.pendingRevalidates) {
- staticGenerationStore.pendingRevalidates = []
- }
- staticGenerationStore.pendingRevalidates.push(
- doOriginalFetch().catch(console.error)
+ true
)
}
+ return res
+ })
+ }
- const resData = entry.value.data
- let decodedBody = ''
+ if (
+ staticGenerationStore.incrementalCache &&
+ typeof revalidate === 'number' &&
+ revalidate > 0
+ ) {
+ cacheKey = await staticGenerationStore.incrementalCache.fetchCacheKey(
+ input.toString(),
+ init
+ )
+ const entry = await staticGenerationStore.incrementalCache.get(
+ cacheKey,
+ true
+ )
+
+ if (entry?.value && entry.value.kind === 'FETCH') {
+ // when stale and is revalidating we wait for fresh data
+ // so the revalidated entry has the updated data
+ if (!staticGenerationStore.isRevalidate || !entry.isStale) {
+ if (entry.isStale) {
+ if (!staticGenerationStore.pendingRevalidates) {
+ staticGenerationStore.pendingRevalidates = []
+ }
+ staticGenerationStore.pendingRevalidates.push(
+ doOriginalFetch().catch(console.error)
+ )
+ }
- // TODO: handle non-text response bodies
- if (process.env.NEXT_RUNTIME === 'edge') {
- decodedBody = atob(resData.body)
- } else {
- decodedBody = Buffer.from(resData.body, 'base64').toString()
- }
+ const resData = entry.value.data
+ let decodedBody = ''
- return new Response(decodedBody, {
- headers: resData.headers,
- status: resData.status,
- })
- }
- }
- }
+ // TODO: handle non-text response bodies
+ if (process.env.NEXT_RUNTIME === 'edge') {
+ decodedBody = atob(resData.body)
+ } else {
+ decodedBody = Buffer.from(resData.body, 'base64').toString()
+ }
- if (staticGenerationStore.isStaticGeneration) {
- if (init && typeof init === 'object') {
- const cache = init.cache
- // Delete `cache` property as Cloudflare Workers will throw an error
- if (isEdgeRuntime) {
- delete init.cache
- }
- if (cache === 'no-store') {
- staticGenerationStore.revalidate = 0
- // TODO: ensure this error isn't logged to the user
- // seems it's slipping through currently
- const dynamicUsageReason = `no-store fetch ${input}${
- staticGenerationStore.pathname
- ? ` ${staticGenerationStore.pathname}`
- : ''
- }`
- const err = new DynamicServerError(dynamicUsageReason)
- staticGenerationStore.dynamicUsageStack = err.stack
- staticGenerationStore.dynamicUsageDescription = dynamicUsageReason
-
- throw err
+ return new Response(decodedBody, {
+ headers: resData.headers,
+ status: resData.status,
+ })
+ }
}
+ }
- const hasNextConfig = 'next' in init
- const next = init.next || {}
- if (
- typeof next.revalidate === 'number' &&
- (typeof staticGenerationStore.revalidate === 'undefined' ||
- next.revalidate < staticGenerationStore.revalidate)
- ) {
- const forceDynamic = staticGenerationStore.forceDynamic
-
- if (!forceDynamic || next.revalidate !== 0) {
- staticGenerationStore.revalidate = next.revalidate
+ if (staticGenerationStore.isStaticGeneration) {
+ if (init && typeof init === 'object') {
+ const cache = init.cache
+ // Delete `cache` property as Cloudflare Workers will throw an error
+ if (isEdgeRuntime) {
+ delete init.cache
}
-
- if (!forceDynamic && next.revalidate === 0) {
- const dynamicUsageReason = `revalidate: ${
- next.revalidate
- } fetch ${input}${
+ if (cache === 'no-store') {
+ staticGenerationStore.revalidate = 0
+ // TODO: ensure this error isn't logged to the user
+ // seems it's slipping through currently
+ const dynamicUsageReason = `no-store fetch ${input}${
staticGenerationStore.pathname
? ` ${staticGenerationStore.pathname}`
: ''
@@ -186,12 +166,41 @@ export function patchFetch({
throw err
}
+
+ const hasNextConfig = 'next' in init
+ const next = init.next || {}
+ if (
+ typeof next.revalidate === 'number' &&
+ (typeof staticGenerationStore.revalidate === 'undefined' ||
+ next.revalidate < staticGenerationStore.revalidate)
+ ) {
+ const forceDynamic = staticGenerationStore.forceDynamic
+
+ if (!forceDynamic || next.revalidate !== 0) {
+ staticGenerationStore.revalidate = next.revalidate
+ }
+
+ if (!forceDynamic && next.revalidate === 0) {
+ const dynamicUsageReason = `revalidate: ${
+ next.revalidate
+ } fetch ${input}${
+ staticGenerationStore.pathname
+ ? ` ${staticGenerationStore.pathname}`
+ : ''
+ }`
+ const err = new DynamicServerError(dynamicUsageReason)
+ staticGenerationStore.dynamicUsageStack = err.stack
+ staticGenerationStore.dynamicUsageDescription = dynamicUsageReason
+
+ throw err
+ }
+ }
+ if (hasNextConfig) delete init.next
}
- if (hasNextConfig) delete init.next
}
- }
- return doOriginalFetch()
- }
+ return doOriginalFetch()
+ }
+ )
;(globalThis.fetch as any).patched = true
}
diff --git a/packages/next/src/server/lib/trace/constants.ts b/packages/next/src/server/lib/trace/constants.ts
new file mode 100644
index 0000000000000..7b27fe7fd7ccd
--- /dev/null
+++ b/packages/next/src/server/lib/trace/constants.ts
@@ -0,0 +1,120 @@
+/**
+ * Contains predefined constants for the trace span name in next/server.
+ *
+ * Currently, next/server/tracer is internal implementation only for tracking
+ * next.js's implementation only with known span names defined here.
+ **/
+
+// eslint typescript has a bug with TS enums
+/* eslint-disable no-shadow */
+
+enum BaseServerSpan {
+ handleRequest = 'BaseServer.handleRequest',
+ run = 'BaseServer.run',
+ pipe = 'BaseServer.pipe',
+ getStaticHTML = 'BaseServer.getStaticHTML',
+ render = 'BaseServer.render',
+ renderToResponseWithComponents = 'BaseServer.renderToResponseWithComponents',
+ renderToResponse = 'BaseServer.renderToResponse',
+ renderToHTML = 'BaseServer.renderToHTML',
+ renderError = 'BaseServer.renderError',
+ renderErrorToResponse = 'BaseServer.renderErrorToResponse',
+ renderErrorToHTML = 'BaseServer.renderErrorToHTML',
+ render404 = 'BaseServer.render404',
+}
+
+enum LoadComponentsSpan {
+ loadDefaultErrorComponents = 'LoadComponents.loadDefaultErrorComponents',
+ loadComponents = 'LoadComponents.loadComponents',
+}
+
+enum NextServerSpan {
+ getRequestHandler = 'NextServer.getRequestHandler',
+ getServer = 'NextServer.getServer',
+ getServerRequestHandler = 'NextServer.getServerRequestHandler',
+ createServer = 'createServer.createServer',
+}
+
+enum NextNodeServerSpan {
+ compression = 'NextNodeServer.compression',
+ getBuildId = 'NextNodeServer.getBuildId',
+ generateStaticRoutes = 'NextNodeServer.generateStaticRoutes',
+ generateFsStaticRoutes = 'NextNodeServer.generateFsStaticRoutes',
+ generatePublicRoutes = 'NextNodeServer.generatePublicRoutes',
+ generateImageRoutes = 'NextNodeServer.generateImageRoutes.route',
+ sendRenderResult = 'NextNodeServer.sendRenderResult',
+ sendStatic = 'NextNodeServer.sendStatic',
+ proxyRequest = 'NextNodeServer.proxyRequest',
+ runApi = 'NextNodeServer.runApi',
+ render = 'NextNodeServer.render',
+ renderHTML = 'NextNodeServer.renderHTML',
+ imageOptimizer = 'NextNodeServer.imageOptimizer',
+ getPagePath = 'NextNodeServer.getPagePath',
+ getRoutesManifest = 'NextNodeServer.getRoutesManifest',
+ findPageComponents = 'NextNodeServer.findPageComponents',
+ getFontManifest = 'NextNodeServer.getFontManifest',
+ getServerComponentManifest = 'NextNodeServer.getServerComponentManifest',
+ getRequestHandler = 'NextNodeServer.getRequestHandler',
+ renderToHTML = 'NextNodeServer.renderToHTML',
+ renderError = 'NextNodeServer.renderError',
+ renderErrorToHTML = 'NextNodeServer.renderErrorToHTML',
+ render404 = 'NextNodeServer.render404',
+
+ // nested inner span, does not require parent scope name
+ route = 'route',
+ onProxyReq = 'onProxyReq',
+ apiResolver = 'apiResolver',
+}
+
+enum StartServerSpan {
+ startServer = 'startServer.startServer',
+}
+
+enum RenderSpan {
+ getServerSideProps = 'Render.getServerSideProps',
+ renderToString = 'Render.renderToString',
+ renderDocument = 'Render.renderDocument',
+ createBodyResult = 'Render.createBodyResult',
+}
+
+enum AppRenderSpan {
+ renderToString = 'AppRender.renderToString',
+ renderToReadableStream = 'AppRender.renderToReadableStream',
+ getBodyResult = 'AppRender.getBodyResult',
+ fetch = 'AppRender.fetch',
+}
+
+enum RouterSpan {
+ executeRoute = 'Router.executeRoute',
+}
+
+type SpanNames =
+ | `${BaseServerSpan}`
+ | `${LoadComponentsSpan}`
+ | `${NextServerSpan}`
+ | `${StartServerSpan}`
+ | `${NextNodeServerSpan}`
+ | `${RenderSpan}`
+ | `${RouterSpan}`
+ | `${AppRenderSpan}`
+
+// This list is used to filter out spans that are not relevant to the user
+export const NextVanillaSpanAllowlist = [
+ NextServerSpan.getRequestHandler,
+ NextNodeServerSpan.findPageComponents,
+ BaseServerSpan.renderToResponse,
+ RenderSpan.getServerSideProps,
+ AppRenderSpan.fetch,
+]
+
+export {
+ BaseServerSpan,
+ LoadComponentsSpan,
+ NextServerSpan,
+ NextNodeServerSpan,
+ StartServerSpan,
+ SpanNames,
+ RenderSpan,
+ RouterSpan,
+ AppRenderSpan,
+}
diff --git a/packages/next/src/server/lib/trace/tracer.ts b/packages/next/src/server/lib/trace/tracer.ts
new file mode 100644
index 0000000000000..f3f1a307e3d83
--- /dev/null
+++ b/packages/next/src/server/lib/trace/tracer.ts
@@ -0,0 +1,306 @@
+import { NextVanillaSpanAllowlist, SpanNames } from './constants'
+
+import type { ContextAPI, Span, SpanOptions, Tracer } from '@opentelemetry/api'
+
+let api: typeof import('@opentelemetry/api')
+
+// we want to allow users to use their own version of @opentelemetry/api if they
+// want to, so we try to require it first, and if it fails we fall back to the
+// version that is bundled with Next.js
+// this is because @opentelemetry/api has to be synced with the version of
+// @opentelemetry/tracing that is used, and we don't want to force users to use
+// the version that is bundled with Next.js.
+// the API is ~stable, so this should be fine
+try {
+ api = require('@opentelemetry/api')
+} catch (err) {
+ api = require('next/dist/compiled/@opentelemetry/api')
+}
+
+const { context, trace, SpanStatusCode, SpanKind } = api
+
+const isPromise = (p: any): p is Promise => {
+ return p !== null && typeof p === 'object' && typeof p.then === 'function'
+}
+
+const closeSpanWithError = (span: Span, error?: Error) => {
+ span.setStatus({ code: SpanStatusCode.ERROR, message: error?.message })
+ span.end()
+}
+
+type TracerSpanOptions = SpanOptions & {
+ parentSpan?: Span
+ tracerName?: string
+}
+
+interface NextTracer {
+ getContext(): ContextAPI
+
+ /**
+ * Instruments a function by automatically creating a span activated on its
+ * scope.
+ *
+ * The span will automatically be finished when one of these conditions is
+ * met:
+ *
+ * * The function returns a promise, in which case the span will finish when
+ * the promise is resolved or rejected.
+ * * The function takes a callback as its second parameter, in which case the
+ * span will finish when that callback is called.
+ * * The function doesn't accept a callback and doesn't return a promise, in
+ * which case the span will finish at the end of the function execution.
+ *
+ */
+ trace(
+ name: SpanNames,
+ fn: (span: Span, done?: (error?: Error) => any) => Promise
+ ): Promise
+ trace(
+ name: SpanNames,
+ fn: (span: Span, done?: (error?: Error) => any) => T
+ ): T
+ trace(
+ name: SpanNames,
+ options: TracerSpanOptions,
+ fn: (span: Span, done?: (error?: Error) => any) => Promise
+ ): Promise
+ trace(
+ name: SpanNames,
+ options: TracerSpanOptions,
+ fn: (span: Span, done?: (error?: Error) => any) => T
+ ): T
+
+ /**
+ * Wrap a function to automatically create a span activated on its
+ * scope when it's called.
+ *
+ * The span will automatically be finished when one of these conditions is
+ * met:
+ *
+ * * The function returns a promise, in which case the span will finish when
+ * the promise is resolved or rejected.
+ * * The function takes a callback as its last parameter, in which case the
+ * span will finish when that callback is called.
+ * * The function doesn't accept a callback and doesn't return a promise, in
+ * which case the span will finish at the end of the function execution.
+ */
+ wrap) => any>(name: SpanNames, fn: T): T
+ wrap) => any>(
+ name: SpanNames,
+ options: TracerSpanOptions,
+ fn: T
+ ): T
+ wrap) => any>(
+ name: SpanNames,
+ options: (...args: any[]) => TracerSpanOptions,
+ fn: T
+ ): T
+
+ /**
+ * Starts and returns a new Span representing a logical unit of work.
+ *
+ * This method do NOT modify the current Context by default. In result, any inner span will not
+ * automatically set its parent context to the span created by this method unless manually activate
+ * context via `tracer.getContext().with`. `trace`, or `wrap` is generally recommended as it gracefully
+ * handles context activation. (ref: https://github.com/open-telemetry/opentelemetry-js/issues/1923)
+ */
+ startSpan(name: SpanNames): Span
+ startSpan(name: SpanNames, options: TracerSpanOptions): Span
+
+ /**
+ * Returns currently activated span if current context is in the scope of the span.
+ * Returns undefined otherwise.
+ */
+ getActiveScopeSpan(): Span | undefined
+}
+
+class NextTracerImpl implements NextTracer {
+ /**
+ * Returns an instance to the trace with configured name.
+ * Since wrap / trace can be defined in any place prior to actual trace subscriber initialization,
+ * This should be lazily evaluated.
+ */
+ private getTracerInstance(): Tracer {
+ return trace.getTracer('next.js', '0.0.1')
+ }
+
+ public getContext(): ContextAPI {
+ return context
+ }
+
+ public getActiveScopeSpan(): Span | undefined {
+ return trace.getSpan(context?.active())
+ }
+
+ // Trace, wrap implementation is inspired by datadog trace implementation
+ // (https://datadoghq.dev/dd-trace-js/interfaces/tracer.html#trace).
+ public trace(
+ name: SpanNames,
+ fn: (span: Span, done?: (error?: Error) => any) => Promise
+ ): Promise
+ public trace(
+ name: SpanNames,
+ fn: (span: Span, done?: (error?: Error) => any) => T
+ ): T
+ public trace(
+ name: SpanNames,
+ options: TracerSpanOptions,
+ fn: (span: Span, done?: (error?: Error) => any) => Promise
+ ): Promise
+ public trace(
+ name: SpanNames,
+ options: TracerSpanOptions,
+ fn: (span: Span, done?: (error?: Error) => any) => T
+ ): T
+ public trace(...args: Array) {
+ const [name, fnOrOptions, fnOrEmpty] = args
+
+ // coerce options form overload
+ const {
+ fn,
+ options,
+ }: {
+ fn: (span?: Span, done?: (error?: Error) => any) => T | Promise
+ options: TracerSpanOptions
+ } =
+ typeof fnOrOptions === 'function'
+ ? {
+ fn: fnOrOptions,
+ options: {},
+ }
+ : {
+ fn: fnOrEmpty,
+ options: fnOrOptions,
+ }
+
+ if (
+ !NextVanillaSpanAllowlist.includes(name) &&
+ process.env.NEXT_OTEL_VERBOSE !== '1'
+ ) {
+ return fn()
+ }
+
+ // Trying to get active scoped span to assign parent. If option specifies parent span manually, will try to use it.
+ const spanContext = this.getSpanContext(
+ options?.parentSpan ?? this.getActiveScopeSpan()
+ )
+
+ const runWithContext = (actualFn: (span: Span) => T | Promise) =>
+ spanContext
+ ? this.getTracerInstance().startActiveSpan(
+ name,
+ options,
+ spanContext,
+ actualFn
+ )
+ : this.getTracerInstance().startActiveSpan(name, options, actualFn)
+
+ return runWithContext((span: Span) => {
+ try {
+ if (fn.length > 1) {
+ return fn(span, (err?: Error) => closeSpanWithError(span, err))
+ }
+
+ const result = fn(span)
+
+ if (isPromise(result)) {
+ result.then(
+ () => span.end(),
+ (err) => closeSpanWithError(span, err)
+ )
+ } else {
+ span.end()
+ }
+
+ return result
+ } catch (err: any) {
+ closeSpanWithError(span, err)
+ throw err
+ }
+ })
+ }
+
+ public wrap) => any>(name: SpanNames, fn: T): T
+ public wrap) => any>(
+ name: SpanNames,
+ options: TracerSpanOptions,
+ fn: T
+ ): T
+ public wrap) => any>(
+ name: SpanNames,
+ options: (...args: any[]) => TracerSpanOptions,
+ fn: T
+ ): T
+ public wrap(...args: Array) {
+ const tracer = this
+ const [name, options, fn] =
+ args.length === 3 ? args : [args[0], {}, args[1]]
+
+ if (
+ !NextVanillaSpanAllowlist.includes(name) &&
+ process.env.NEXT_OTEL_VERBOSE !== '1'
+ ) {
+ return fn
+ }
+
+ return function (this: any) {
+ let optionsObj = options
+ if (typeof optionsObj === 'function' && typeof fn === 'function') {
+ optionsObj = optionsObj.apply(this, arguments)
+ }
+
+ const lastArgId = arguments.length - 1
+ const cb = arguments[lastArgId]
+
+ if (typeof cb === 'function') {
+ const scopeBoundCb = tracer.getContext().bind(context.active(), cb)
+ return tracer.trace(name, optionsObj, (_span, done) => {
+ arguments[lastArgId] = function (err: any) {
+ done?.(err)
+ return scopeBoundCb.apply(this, arguments)
+ }
+
+ return fn.apply(this, arguments)
+ })
+ } else {
+ return tracer.trace(name, optionsObj, () => fn.apply(this, arguments))
+ }
+ }
+ }
+
+ public startSpan(name: SpanNames): Span
+ public startSpan(name: SpanNames, options: TracerSpanOptions): Span
+ public startSpan(...args: Array): Span {
+ const [name, options]: [string, TracerSpanOptions | undefined] = args as any
+
+ const spanContext = this.getSpanContext(
+ options?.parentSpan ?? this.getActiveScopeSpan()
+ )
+ return this.getTracerInstance().startSpan(name, options, spanContext)
+ }
+
+ private getSpanContext(parentSpan?: Span) {
+ const spanContext = parentSpan
+ ? trace.setSpan(context.active(), parentSpan)
+ : undefined
+
+ return spanContext
+ }
+}
+
+const getTracer = (() => {
+ const tracer = new NextTracerImpl()
+
+ return () => tracer
+})()
+
+export {
+ NextTracer,
+ getTracer,
+ Span,
+ SpanOptions,
+ ContextAPI,
+ SpanStatusCode,
+ TracerSpanOptions,
+ SpanKind,
+}
diff --git a/packages/next/src/server/load-components.ts b/packages/next/src/server/load-components.ts
index 11039706ed4c7..73f36f6d173c0 100644
--- a/packages/next/src/server/load-components.ts
+++ b/packages/next/src/server/load-components.ts
@@ -19,6 +19,8 @@ import { join } from 'path'
import { requirePage } from './require'
import { BuildManifest } from './get-page-files'
import { interopDefault } from '../lib/interop-default'
+import { getTracer } from './lib/trace/tracer'
+import { LoadComponentsSpan } from './lib/trace/constants'
export type ManifestItem = {
id: number | string
@@ -45,7 +47,7 @@ export type LoadComponentsReturnType = {
pathname: string
}
-export async function loadDefaultErrorComponents(distDir: string) {
+async function loadDefaultErrorComponentsImpl(distDir: string) {
const Document = interopDefault(require('next/dist/pages/_document'))
const AppMod = require('next/dist/pages/_app')
const App = interopDefault(AppMod)
@@ -76,7 +78,7 @@ async function loadManifest(manifestPath: string, attempts = 1): Promise {
}
}
-export async function loadComponents({
+async function loadComponentsImpl({
distDir,
pathname,
hasServerComponents,
@@ -142,3 +144,13 @@ export async function loadComponents({
pathname,
}
}
+
+export const loadComponents = getTracer().wrap(
+ LoadComponentsSpan.loadComponents,
+ loadComponentsImpl
+)
+
+export const loadDefaultErrorComponents = getTracer().wrap(
+ LoadComponentsSpan.loadDefaultErrorComponents,
+ loadDefaultErrorComponentsImpl
+)
diff --git a/packages/next/src/server/next-server.ts b/packages/next/src/server/next-server.ts
index 3d53c857e88b5..6f5dec54a3f18 100644
--- a/packages/next/src/server/next-server.ts
+++ b/packages/next/src/server/next-server.ts
@@ -97,6 +97,8 @@ import { AppRouteRouteHandler } from './future/route-handlers/app-route-route-ha
import { PagesAPIRouteMatch } from './future/route-matches/pages-api-route-match'
import { MatchOptions } from './future/route-matcher-managers/route-matcher-manager'
import { INSTRUMENTATION_HOOK_FILENAME } from '../lib/constants'
+import { getTracer } from './lib/trace/tracer'
+import { NextNodeServerSpan } from './lib/trace/constants'
export * from './base-server'
@@ -826,6 +828,18 @@ export default class NextNodeServer extends BaseServer {
pathname: string,
query: NextParsedUrlQuery,
renderOpts: RenderOpts
+ ): Promise {
+ return getTracer().trace(NextNodeServerSpan.renderHTML, async () =>
+ this.renderHTMLImpl(req, res, pathname, query, renderOpts)
+ )
+ }
+
+ private async renderHTMLImpl(
+ req: NodeNextRequest,
+ res: NodeNextResponse,
+ pathname: string,
+ query: NextParsedUrlQuery,
+ renderOpts: RenderOpts
): Promise {
// Due to the way we pass data by mutating `renderOpts`, we can't extend the
// object here but only updating its `serverComponentManifest` field.
@@ -933,6 +947,22 @@ export default class NextNodeServer extends BaseServer {
query: NextParsedUrlQuery
params: Params | null
isAppPath: boolean
+ }): Promise {
+ return getTracer().trace(NextNodeServerSpan.findPageComponents, () =>
+ this.findPageComponentsImpl({ pathname, query, params, isAppPath })
+ )
+ }
+
+ private async findPageComponentsImpl({
+ pathname,
+ query,
+ params,
+ isAppPath,
+ }: {
+ pathname: string
+ query: NextParsedUrlQuery
+ params: Params | null
+ isAppPath: boolean
}): Promise {
const paths: string[] = [pathname]
if (query.amp) {
@@ -2045,7 +2075,9 @@ export default class NextNodeServer extends BaseServer {
}
protected getRoutesManifest() {
- return require(join(this.distDir, ROUTES_MANIFEST))
+ return getTracer().trace(NextNodeServerSpan.getRoutesManifest, () =>
+ require(join(this.distDir, ROUTES_MANIFEST))
+ )
}
protected attachRequestMeta(
diff --git a/packages/next/src/server/next.ts b/packages/next/src/server/next.ts
index 656a1b844c6fc..20188ec0efcda 100644
--- a/packages/next/src/server/next.ts
+++ b/packages/next/src/server/next.ts
@@ -15,6 +15,8 @@ import {
loadRequireHook,
overrideBuiltInReactPackages,
} from '../build/webpack/require-hook'
+import { getTracer } from './lib/trace/tracer'
+import { NextServerSpan } from './lib/trace/constants'
loadRequireHook()
@@ -62,8 +64,10 @@ export class NextServer {
res: ServerResponse,
parsedUrl?: UrlWithParsedQuery
) => {
- const requestHandler = await this.getServerRequestHandler()
- return requestHandler(req, res, parsedUrl)
+ return getTracer().trace(NextServerSpan.getRequestHandler, async () => {
+ const requestHandler = await this.getServerRequestHandler()
+ return requestHandler(req, res, parsedUrl)
+ })
}
}
@@ -171,7 +175,10 @@ export class NextServer {
// Memoize request handler creation
if (!this.reqHandlerPromise) {
this.reqHandlerPromise = this.getServer().then((server) =>
- server.getRequestHandler().bind(server)
+ getTracer().wrap(
+ NextServerSpan.getServerRequestHandler,
+ server.getRequestHandler().bind(server)
+ )
)
}
return this.reqHandlerPromise
diff --git a/packages/next/src/server/node-web-streams-helper.ts b/packages/next/src/server/node-web-streams-helper.ts
index c82ff473824f2..6138100f97899 100644
--- a/packages/next/src/server/node-web-streams-helper.ts
+++ b/packages/next/src/server/node-web-streams-helper.ts
@@ -1,5 +1,7 @@
import type { FlightRouterState } from './app-render'
import { nonNullable } from '../lib/non-nullable'
+import { getTracer } from './lib/trace/tracer'
+import { AppRenderSpan } from './lib/trace/constants'
const queueTask =
process.env.NEXT_RUNTIME === 'edge' ? globalThis.setTimeout : setImmediate
@@ -149,7 +151,9 @@ export function renderToInitialStream({
element: React.ReactElement
streamOptions?: any
}): Promise {
- return ReactDOMServer.renderToReadableStream(element, streamOptions)
+ return getTracer().trace(AppRenderSpan.renderToReadableStream, async () =>
+ ReactDOMServer.renderToReadableStream(element, streamOptions)
+ )
}
function createHeadInsertionTransformStream(
diff --git a/packages/next/src/server/render.tsx b/packages/next/src/server/render.tsx
index f43a7edb2cf9a..4036142a7e725 100644
--- a/packages/next/src/server/render.tsx
+++ b/packages/next/src/server/render.tsx
@@ -88,6 +88,8 @@ import {
} from '../shared/lib/router/adapters'
import { AppRouterContext } from '../shared/lib/app-router-context'
import { SearchParamsContext } from '../shared/lib/hooks-client-context'
+import { getTracer } from './lib/trace/tracer'
+import { RenderSpan } from './lib/trace/constants'
let tryGetPreviewData: typeof import('./api-utils/node').tryGetPreviewData
let warn: typeof import('../build/output/log').warn
@@ -944,21 +946,23 @@ export async function renderToHTML(
}
try {
- data = await getServerSideProps({
- req: req as IncomingMessage & {
- cookies: NextApiRequestCookies
- },
- res: resOrProxy,
- query,
- resolvedUrl: renderOpts.resolvedUrl as string,
- ...(pageIsDynamic ? { params: params as ParsedUrlQuery } : undefined),
- ...(previewData !== false
- ? { preview: true, previewData: previewData }
- : undefined),
- locales: renderOpts.locales,
- locale: renderOpts.locale,
- defaultLocale: renderOpts.defaultLocale,
- })
+ data = await getTracer().trace(RenderSpan.getServerSideProps, async () =>
+ getServerSideProps({
+ req: req as IncomingMessage & {
+ cookies: NextApiRequestCookies
+ },
+ res: resOrProxy,
+ query,
+ resolvedUrl: renderOpts.resolvedUrl as string,
+ ...(pageIsDynamic ? { params: params as ParsedUrlQuery } : undefined),
+ ...(previewData !== false
+ ? { preview: true, previewData: previewData }
+ : undefined),
+ locales: renderOpts.locales,
+ locale: renderOpts.locale,
+ defaultLocale: renderOpts.defaultLocale,
+ })
+ )
canAccessRes = false
} catch (serverSidePropsError: any) {
// remove not found error code to prevent triggering legacy
@@ -1233,24 +1237,24 @@ export async function renderToHTML(
})
}
- const createBodyResult = (
- initialStream: ReactReadableStream,
- suffix?: string
- ) => {
- // this must be called inside bodyResult so appWrappers is
- // up to date when `wrapApp` is called
- const getServerInsertedHTML = async (): Promise => {
- return renderToString(styledJsxInsertedHTML())
- }
+ const createBodyResult = getTracer().wrap(
+ RenderSpan.createBodyResult,
+ (initialStream: ReactReadableStream, suffix?: string) => {
+ // this must be called inside bodyResult so appWrappers is
+ // up to date when `wrapApp` is called
+ const getServerInsertedHTML = async (): Promise => {
+ return renderToString(styledJsxInsertedHTML())
+ }
- return continueFromInitialStream(initialStream, {
- suffix,
- dataStream: serverComponentsInlinedTransformStream?.readable,
- generateStaticHTML,
- getServerInsertedHTML,
- serverInsertedHTMLToHead: false,
- })
- }
+ return continueFromInitialStream(initialStream, {
+ suffix,
+ dataStream: serverComponentsInlinedTransformStream?.readable,
+ generateStaticHTML,
+ getServerInsertedHTML,
+ serverInsertedHTMLToHead: false,
+ })
+ }
+ )
const hasDocumentGetInitialProps = !(
process.env.NEXT_RUNTIME === 'edge' || !Document.getInitialProps
@@ -1303,7 +1307,10 @@ export async function renderToHTML(
}
}
- const documentResult = await renderDocument()
+ const documentResult = await getTracer().trace(
+ RenderSpan.renderDocument,
+ async () => renderDocument()
+ )
if (!documentResult) {
return null
}
@@ -1407,7 +1414,10 @@ export async function renderToHTML(
)
- const documentHTML = await renderToString(document)
+ const documentHTML = await getTracer().trace(
+ RenderSpan.renderToString,
+ async () => renderToString(document)
+ )
if (process.env.NODE_ENV !== 'production') {
const nonRenderedComponents = []
diff --git a/packages/next/src/server/router.ts b/packages/next/src/server/router.ts
index 01b8466565448..150657673405f 100644
--- a/packages/next/src/server/router.ts
+++ b/packages/next/src/server/router.ts
@@ -25,6 +25,8 @@ import {
} from './future/route-matcher-managers/route-matcher-manager'
import { removeTrailingSlash } from '../shared/lib/router/utils/remove-trailing-slash'
import { LocaleRouteNormalizer } from './future/normalizers/locale-route-normalizer'
+import { getTracer } from './lib/trace/tracer'
+import { RouterSpan } from './lib/trace/constants'
type RouteResult = {
finished: boolean
@@ -231,7 +233,23 @@ export default class Router {
// We only check the catch-all route if public page routes hasn't been
// disabled
...(this.useFileSystemPublicRoutes ? [this.catchAllRoute] : []),
- ]
+ ].map((route) => {
+ if (route.fn) {
+ return {
+ ...route,
+ fn: getTracer().wrap(
+ RouterSpan.executeRoute,
+ {
+ attributes: {
+ route: route.name,
+ },
+ },
+ route.fn
+ ),
+ }
+ }
+ return route
+ })
}
private async checkFsRoutes(
diff --git a/packages/next/taskfile.js b/packages/next/taskfile.js
index b2b99399baf84..7d76968f239ee 100644
--- a/packages/next/taskfile.js
+++ b/packages/next/taskfile.js
@@ -2027,6 +2027,17 @@ export async function path_to_regexp(task, opts) {
.target('dist/compiled/path-to-regexp')
}
+// eslint-disable-next-line camelcase
+externals['@opentelemetry/api'] = 'next/dist/compiled/@opentelemetry/api'
+export async function ncc_opentelemetry_api(task, opts) {
+ await task
+ .source(
+ opts.src || relative(__dirname, require.resolve('@opentelemetry/api'))
+ )
+ .ncc({ packageName: '@opentelemetry/api', externals })
+ .target('src/compiled/@opentelemetry/api')
+}
+
export async function precompile(task, opts) {
await task.parallel(
[
@@ -2159,6 +2170,7 @@ export async function ncc(task, opts) {
'ncc_ws',
'ncc_ua_parser_js',
'ncc_minimatch',
+ 'ncc_opentelemetry_api',
'ncc_mini_css_extract_plugin',
],
opts
diff --git a/packages/next/types/misc.d.ts b/packages/next/types/misc.d.ts
index a7119966cff76..f0fa351a91c5d 100644
--- a/packages/next/types/misc.d.ts
+++ b/packages/next/types/misc.d.ts
@@ -434,3 +434,8 @@ declare module 'next/dist/compiled/watchpack' {
declare module 'next/dist/compiled/is-animated' {
export default function isAnimated(buffer: Buffer): boolean
}
+
+declare module 'next/dist/compiled/@opentelemetry/api' {
+ import * as m from '@opentelemetry/api'
+ export = m
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e30f119d64f58..1b78d741d2f4b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -517,6 +517,7 @@ importers:
'@next/react-dev-overlay': 13.1.7-canary.25
'@next/react-refresh-utils': 13.1.7-canary.25
'@next/swc': 13.1.7-canary.25
+ '@opentelemetry/api': 1.4.0
'@segment/ajv-human-errors': 2.1.2
'@swc/helpers': 0.4.14
'@taskr/clear': 1.1.0
@@ -535,6 +536,7 @@ importers:
'@types/cookie': 0.3.3
'@types/cross-spawn': 6.0.0
'@types/debug': 4.1.5
+ '@types/express-serve-static-core': 4.17.33
'@types/fresh': 0.5.0
'@types/glob': 7.1.1
'@types/jsonwebtoken': 9.0.0
@@ -728,6 +730,7 @@ importers:
'@next/react-dev-overlay': link:../react-dev-overlay
'@next/react-refresh-utils': link:../react-refresh-utils
'@next/swc': link:../next-swc
+ '@opentelemetry/api': 1.4.0
'@segment/ajv-human-errors': 2.1.2_ajv@8.11.0
'@taskr/clear': 1.1.0
'@taskr/esnext': 1.1.0
@@ -745,6 +748,7 @@ importers:
'@types/cookie': 0.3.3
'@types/cross-spawn': 6.0.0
'@types/debug': 4.1.5
+ '@types/express-serve-static-core': 4.17.33
'@types/fresh': 0.5.0
'@types/glob': 7.1.1
'@types/jsonwebtoken': 9.0.0
@@ -6122,6 +6126,11 @@ packages:
'@types/node': 18.11.18
dev: true
+ /@opentelemetry/api/1.4.0:
+ resolution: {integrity: sha512-IgMK9i3sFGNUqPMbjABm0G26g0QCKCUBfglhQ7rQq6WcxbKfEHRcmwsoER4hZcuYqJgkYn2OeuoJIv7Jsftp7g==}
+ engines: {node: '>=8.0.0'}
+ dev: true
+
/@pkgr/utils/2.3.1:
resolution: {integrity: sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
@@ -6894,10 +6903,11 @@ packages:
resolution: {integrity: sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==}
dev: true
- /@types/express-serve-static-core/4.17.1:
- resolution: {integrity: sha512-9e7jj549ZI+RxY21Cl0t8uBnWyb22HzILupyHZjYEVK//5TT/1bZodU+yUbLnPdoYViBBnNWbxp4zYjGV0zUGw==}
+ /@types/express-serve-static-core/4.17.33:
+ resolution: {integrity: sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==}
dependencies:
'@types/node': 18.11.18
+ '@types/qs': 6.9.7
'@types/range-parser': 1.2.3
dev: true
@@ -6905,7 +6915,7 @@ packages:
resolution: {integrity: sha512-5mHFNyavtLoJmnusB8OKJ5bshSzw+qkMIBAobLrIM48HJvunFva9mOa6aBwh64lBFyNwBbs0xiEFuj4eU/NjCA==}
dependencies:
'@types/body-parser': 1.17.1
- '@types/express-serve-static-core': 4.17.1
+ '@types/express-serve-static-core': 4.17.33
'@types/serve-static': 1.13.3
dev: true
@@ -7154,6 +7164,10 @@ packages:
resolution: {integrity: sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==}
dev: true
+ /@types/qs/6.9.7:
+ resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==}
+ dev: true
+
/@types/range-parser/1.2.3:
resolution: {integrity: sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==}
dev: true
@@ -7229,7 +7243,7 @@ packages:
/@types/serve-static/1.13.3:
resolution: {integrity: sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==}
dependencies:
- '@types/express-serve-static-core': 4.17.1
+ '@types/express-serve-static-core': 4.17.33
'@types/mime': 2.0.1
dev: true