elastic / apm-agent-nodejs

https://www.elastic.co/guide/en/apm/agent/nodejs/current/index.html
BSD 2-Clause "Simplified" License
584 stars 225 forks source link

next js output standalone - next:13.2.1, elastic-apm-node: 3.46.0 #3342

Open mindyourlifeguide opened 1 year ago

mindyourlifeguide commented 1 year ago

Describe the bug

To Reproduce I have a monorepo with several applications. Package manager - pnpm v8.4.0.

my Dockerfile:

FROM node:16-alpine AS builder

ARG BUILD_ENV=prod
ARG PROJECT_NAME

WORKDIR /app

RUN apk add --no-cache build-base g++ cairo-dev jpeg-dev pango-dev giflib-dev
RUN npm install -g pnpm@8.4.0

COPY . .

RUN pnpm config set store-dir /app/.pnpm-store
RUN --mount=type=secret,id=npmrc,dst=/root/.npmrc \
    --mount=type=tmpfs,target=/app/node_modules/ \
    pnpm install && pnpm build:${BUILD_ENV} --filter=${PROJECT_NAME}

# Production image, copy the necessary files and run next
FROM node:16-alpine AS runner
WORKDIR /app

# args needed to build the docker image
ARG PATH_TO_PROJECT
ARG PROJECT_NAME

# service env variables
ENV PATH_TO_PROJECT $PATH_TO_PROJECT
ENV PROJECT_NAME $PROJECT_NAME
ENV RUN_ENV prod
ENV PORT 3000

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --chown=nextjs:nodejs  --from=builder /app/${PATH_TO_PROJECT}/.next/standalone .
COPY --chown=nextjs:nodejs  --from=builder /app/${PATH_TO_PROJECT}/public ./${PATH_TO_PROJECT}/public
COPY --chown=nextjs:nodejs  --from=builder /app/${PATH_TO_PROJECT}/.next/static ./${PATH_TO_PROJECT}/.next/static

USER nextjs

EXPOSE 3000

CMD node NODE_OPTIONS=--require=elastic-apm-node/start-next.js ./${PATH_TO_PROJECT}/server.js

I have a elastic-apm-node.js In normal mode, everything worked - the Dockerfile was a little different. When I turned on output: standalone - apm stopped working. You told me to import the api route package to include in the bundle. The package really dragged on and locally it seemed to even start. But when I build in ci - module not found at startup.

I solved this by removing the options for node NODE_OPTIONS=--require=elastic-apm-node/start-next.js from the Dockerfile and running apm agent manually in api routes. The agent worked, but now it says that the routes are unknown

Expected behavior

Working apm agent with output: standalone

Environment (please complete the following information)

How are you starting the agent? (please tick one of the boxes)

trentm commented 1 year ago
CMD node NODE_OPTIONS=--require=elastic-apm-node/start-next.js ./${PATH_TO_PROJECT}/server.js

That looks wrong. I think it should be either:

ENV NODE_OPTIONS=--require=elastic-apm-node/start-next.js
CMD node ./${PATH_TO_PROJECT}/server.js

or

CMD node --require=elastic-apm-node/start-next.js ./${PATH_TO_PROJECT}/server.js

I'm not sure this is the issue you are having, however.

trentm commented 1 year ago

Also, as I mentioned at https://github.com/elastic/apm-agent-nodejs/issues/1611#issuecomment-1541023864 I wonder if standalone mode is just completely broken in next@13.x.

Can you should a recursive directory listing of /app in your "runner" container? find /app

mindyourlifeguide commented 1 year ago

@trentm I tried:

CMD node --require=elastic-apm-node/start-next.js ./${PATH_TO_PROJECT}/server.js
CMD node NODE_OPTIONS=--require=elastic-apm-node/start-next.js ./${PATH_TO_PROJECT}/server.js
CMD node -r elastic-apm-node/start-next.js server.js

And always the result was the same. image

Standalone mode itself works in "next": "13.2.1". Only elastic-apm-node does not work. The same sentry works and doesn't require the api routes import hack.

mindyourlifeguide commented 1 year ago

Can you should a recursive directory listing of /app in your "runner" container? find /app

what exactly are you interested in? image

mindyourlifeguide commented 1 year ago

@trentm in order for everything to work in nextjs in output standalone mode, you need:

My final Dockerfile is more complicated since I don't run apm everywhere, but a simplified version would look like this:

FROM node:16-alpine AS builder

ARG BUILD_ENV=prod
ARG PROJECT_NAME

WORKDIR /app

RUN apk add --no-cache build-base g++ cairo-dev jpeg-dev pango-dev giflib-dev
RUN npm install -g pnpm@8.4.0

COPY . .

RUN pnpm config set store-dir /app/.pnpm-store
RUN --mount=type=secret,id=npmrc,dst=/root/.npmrc \
    --mount=type=tmpfs,target=/app/node_modules/ \
    pnpm install && pnpm build:${BUILD_ENV} --filter=${PROJECT_NAME}

# Production image, copy the necessary files and run next
FROM node:16-alpine AS runner
WORKDIR /app

# args needed to build the docker image
ARG PATH_TO_PROJECT
ARG PROJECT_NAME

# service env variables
ENV PATH_TO_PROJECT $PATH_TO_PROJECT
ENV PROJECT_NAME $PROJECT_NAME
ENV RUN_ENV prod
ENV PORT 3000

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --chown=nextjs:nodejs  --from=builder /app/${PATH_TO_PROJECT}/.next/standalone .
COPY --chown=nextjs:nodejs  --from=builder /app/${PATH_TO_PROJECT}/public ./${PATH_TO_PROJECT}/public
COPY --chown=nextjs:nodejs  --from=builder /app/${PATH_TO_PROJECT}/.next/static ./${PATH_TO_PROJECT}/.next/static
COPY --chown=nextjs:nodejs  --from=builder /app/${PATH_TO_PROJECT}/elastic-apm-node.* ./

USER nextjs

EXPOSE 3000

CMD node NODE_OPTIONS=--require=elastic-apm-node/start-next.js ./${PATH_TO_PROJECT}/server.js
sjoukedv commented 1 year ago

I am getting something similar with a more verbose error message below:

/app $ ELASTIC_APM_LOG_LEVEL=trace node --require=elastic-apm-node/start-next.js server.js
{"log.level":"debug","@timestamp":"2023-07-29T14:50:16.728Z","log":{"logger":"elastic-apm-node"},"ecs":{"version":"1.6.0"},"error":{"type":"Error","message":"Cannot find module './modules/next/dist/server/next.js'\nRequire stack:\n- /app/node_modules/elastic-apm-node/lib/instrumentation/index.js\n- /app/node_modules/elastic-apm-node/lib/agent.js\n- /app/node_modules/elastic-apm-node/index.js\n- /app/node_modules/elastic-apm-node/start.js\n- /app/node_modules/elastic-apm-node/start-next.js\n- internal/preload","stack_trace":"Error: Cannot find module './modules/next/dist/server/next.js'\nRequire stack:\n- /app/node_modules/elastic-apm-node/lib/instrumentation/index.js\n- /app/node_modules/elastic-apm-node/lib/agent.js\n- /app/node_modules/elastic-apm-node/index.js\n- /app/node_modules/elastic-apm-node/start.js\n- /app/node_modules/elastic-apm-node/start-next.js\n- internal/preload\n    at Module._resolveFilename (node:internal/modules/cjs/loader:1077:15)\n    at /app/node_modules/next/dist/server/require-hook.js:113:36\n    at Module._load (node:internal/modules/cjs/loader:922:27)\n    at Module.require (node:internal/modules/cjs/loader:1143:19)\n    at Hook._require.Module.require (/app/node_modules/require-in-the-middle/index.js:167:34)\n    at require (node:internal/modules/cjs/helpers:110:18)\n    at /app/node_modules/elastic-apm-node/lib/instrumentation/index.js:142:14\n    at Instrumentation._patchModule (/app/node_modules/elastic-apm-node/lib/instrumentation/index.js:433:20)\n    at /app/node_modules/elastic-apm-node/lib/instrumentation/index.js:358:17\n    at Hook._require.Module.require (/app/node_modules/require-in-the-middle/index.js:262:28)\n    at require (node:internal/modules/cjs/helpers:110:18)\n    at Object.<anonymous> (/app/node_modules/next/dist/server/lib/render-server.js:40:54)\n    at Module._compile (node:internal/modules/cjs/loader:1256:14)\n    at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)\n    at Module.load (node:internal/modules/cjs/loader:1119:32)\n    at Module._load (node:internal/modules/cjs/loader:960:12)\n    at Module.require (node:internal/modules/cjs/loader:1143:19)\n    at Hook._require.Module.require (/app/node_modules/require-in-the-middle/index.js:188:39)\n    at require (node:internal/modules/cjs/helpers:110:18)\n    at execMethod (/app/node_modules/next/dist/compiled/jest-worker/processChild.js:1:2366)\n    at process.messageListener (/app/node_modules/next/dist/compiled/jest-worker/processChild.js:1:1284)\n    at process.emit (node:events:514:28)\n    at emit (node:internal/child_process:937:14)\n    at process.processTicksAndRejections (node:internal/process/task_queues:83:21)"},"message":"Elastic APM caught unhandled exception"}
{"log.level":"trace","@timestamp":"2023-07-29T14:50:16.773Z","log":{"logger":"elastic-apm-node"},"ecs":{"version":"1.6.0"},"message":"azure metadata server responded, but there was an error parsing the result: {}"}
{"log.level":"debug","@timestamp":"2023-07-29T14:50:16.793Z","log":{"logger":"elastic-apm-node"},"filename":"/app/node_modules/next/dist/server/require-hook.js","ecs":{"version":"1.6.0"},"error":{"type":"Error","message":"Error reading sourcemap for file \"/app/node_modules/next/dist/server/require-hook.js\":\nENOENT: no such file or directory, open '/app/node_modules/next/dist/server/require-hook.js.map'","stack_trace":"Error: Error reading sourcemap for file \"/app/node_modules/next/dist/server/require-hook.js\":\nENOENT: no such file or directory, open '/app/node_modules/next/dist/server/require-hook.js.map'"},"message":"could not process file source map"}
{"log.level":"debug","@timestamp":"2023-07-29T14:50:16.794Z","log":{"logger":"elastic-apm-node"},"filename":"/app/node_modules/next/dist/server/lib/render-server.js","ecs":{"version":"1.6.0"},"error":{"type":"Error","message":"Error reading sourcemap for file \"/app/node_modules/next/dist/server/lib/render-server.js\":\nENOENT: no such file or directory, open '/app/node_modules/next/dist/server/lib/render-server.js.map'","stack_trace":"Error: Error reading sourcemap for file \"/app/node_modules/next/dist/server/lib/render-server.js\":\nENOENT: no such file or directory, open '/app/node_modules/next/dist/server/lib/render-server.js.map'"},"message":"could not process file source map"}

These files are indeed not present in the traced output. Can we make sure to add the proper require/include statement such that they are picked up? I believe NextJS uses this tool to do the tracing https://github.com/vercel/nft.

Note; I think the scope is broader to framework that use output file tracing solutions

snowman95 commented 10 months ago

I had the same problem with this. I am sharing my solution. (Caution: This is a trick.)

my package version

 "next": "13.2.3"
 "elastic-apm-node": "^4.1.0",