diff --git a/src/frontend/Dockerfile b/src/frontend/Dockerfile index f47f6e122e..232e0b0d0d 100644 --- a/src/frontend/Dockerfile +++ b/src/frontend/Dockerfile @@ -16,6 +16,8 @@ COPY ./src/frontend . RUN npm run grpc:generate RUN npm run build +FROM ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-nodejs:0.31.0 as instrumentation + FROM node:16-alpine AS runner WORKDIR /app RUN apk add --no-cache protoc @@ -32,6 +34,12 @@ COPY --from=builder /app/package.json ./package.json COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static +# Use deps from AutoInstrumentation +COPY --from=instrumentation --chown=nextjs:nodejs /autoinstrumentation/. /otel-auto-instrumentation +# Customize instrumentation +COPY --from=builder --chown=nextjs:nodejs /app/utils/opentelemetry.js /otel-auto-instrumentation/autoinstrumentation.js +ENV NODE_OPTIONS=" --require /otel-auto-instrumentation/autoinstrumentation.js" + USER nextjs ENV PORT 8080 diff --git a/src/frontend/utils/opentelemetry.js b/src/frontend/utils/opentelemetry.js new file mode 100644 index 0000000000..22138b859e --- /dev/null +++ b/src/frontend/utils/opentelemetry.js @@ -0,0 +1,66 @@ +const process = require("process") +const opentelemetry = require("@opentelemetry/sdk-node") +const { getNodeAutoInstrumentations } = require("@opentelemetry/auto-instrumentations-node") +const { OTLPTraceExporter } = require("@opentelemetry/exporter-trace-otlp-grpc") + +// configure the SDK to export telemetry data to the console +// enable all auto-instrumentations from the meta package +const sdk = new opentelemetry.NodeSDK({ + autoDetectResources: true, + instrumentations: [ + getNodeAutoInstrumentations({ + // Each of the auto-instrumentations + // can have config set here or you can + // npm install each individually and not use the auto-instruments + "@opentelemetry/instrumentation-http": { + ignoreIncomingPaths: [ + // Pattern match to filter endpoints + // that you really want to stop altogether + "/ping", + + // You can filter conditionally + // Next.js gets a little too chatty + // if you trace all the incoming requests + ...(process.env.NODE_ENV !== "production" + ? [/^\/_next\/static.*/] + : []), + ], + + // This gives your request spans a more meaningful name + // than `HTTP GET` + requestHook: (span, request) => { + span.setAttributes({ + name: `${request.method} ${request.url || request.path}`, + }) + }, + + // Re-assign the root span's attributes + startIncomingSpanHook: (request) => { + return { + name: `${request.method} ${request.url || request.path}`, + "request.path": request.url || request.path, + } + }, + } + }), + ], + traceExporter: new OTLPTraceExporter(), +}) + +// initialize the SDK and register with the OpenTelemetry API +// this enables the API to record telemetry +sdk + .start() + .then(() => console.log("Tracing initialized")) + .catch((error) => + console.log("Error initializing tracing and starting server", error) + ) + +// gracefully shut down the SDK on process exit +process.on("SIGTERM", () => { + sdk + .shutdown() + .then(() => console.log("Tracing terminated")) + .catch((error) => console.log("Error terminating tracing", error)) + .finally(() => process.exit(0)) +})