diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 9957c0e9..9bc69038 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -75,7 +75,6 @@ COPY --chown=node:node --from=builder /home/node/public ./public COPY --chown=node:node --from=builder /home/node/.next ./.next COPY --chown=node:node --from=builder /home/node/next.config.js ./next.config.js COPY --chown=node:node --from=builder /home/node/next-i18next.config.js ./next-i18next.config.js -COPY --chown=node:node --from=builder /home/node/open-telemetry.config.js ./open-telemetry.config.js COPY --chown=node:node --from=builder /home/node/package*.json ./ USER node diff --git a/frontend/next.config.js b/frontend/next.config.js index 13458b5e..664ca1b9 100644 --- a/frontend/next.config.js +++ b/frontend/next.config.js @@ -1,7 +1,5 @@ // @ts-check -require('./open-telemetry.config') - const { i18n } = require('./next-i18next.config') const { statSync } = require('fs') @@ -26,6 +24,9 @@ const nextConfig = { NEXT_PUBLIC_BUILD_VERSION: process.env.BUILD_VERSION ?? '00000000-0000-00000000', LOGGING_LEVEL: process.env.LOGGING_LEVEL ?? 'info', }, + experimental: { + instrumentationHook: true + }, generateBuildId: async () => (process.env.BUILD_ID ?? '0000'), headers: async () => ([{ source: '/:path*', headers: securityHeaders }]), i18n: { ...i18n, localeDetection: false }, diff --git a/frontend/open-telemetry.config.js b/frontend/src/instrumentation.node.ts similarity index 67% rename from frontend/open-telemetry.config.js rename to frontend/src/instrumentation.node.ts index e4cbe143..41ebd980 100644 --- a/frontend/open-telemetry.config.js +++ b/frontend/src/instrumentation.node.ts @@ -32,18 +32,18 @@ *
  • https://nextjs.org/docs/api-reference/edge-runtime
  • * */ +import { ExportResultCode } from '@opentelemetry/core' +import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-proto' +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto' +import { CompressionAlgorithm } from '@opentelemetry/otlp-exporter-base' +import { Resource, envDetector, hostDetector, osDetector, processDetector } from '@opentelemetry/resources' +import { AggregationTemporality, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics' +import { NodeSDK } from '@opentelemetry/sdk-node' +import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions' -const log = require('next/dist/build/output/log') +import { getLogger } from './logging/log-util' -const { ExportResultCode } = require('@opentelemetry/core') -const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-proto') -const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-proto') -const { CompressionAlgorithm } = require('@opentelemetry/otlp-exporter-base') -const { envDetector, hostDetector, osDetector, processDetector, Resource } = require('@opentelemetry/resources') -const { AggregationTemporality, MeterProvider, PeriodicExportingMetricReader, PushMetricExporter } = require('@opentelemetry/sdk-metrics') -const { SpanExporter } = require('@opentelemetry/sdk-trace-base') -const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions') -const { NodeSDK } = require('@opentelemetry/sdk-node') +const logger = getLogger('instrumentation.node') /** @returns {PushMetricExporter} */ function getMetricExporter() { @@ -54,23 +54,24 @@ function getMetricExporter() { throw new Error('OTEL_API_KEY must be configured when OTEL_METRICS_ENDPOINT is set') } - log.info(`Exporting metrics to ${process.env.OTEL_METRICS_ENDPOINT} every ${getMetricExportInterval()} ms`) + logger.info(`Exporting metrics to ${process.env.OTEL_METRICS_ENDPOINT} every ${getMetricExportInterval()} ms`) return new OTLPMetricExporter({ compression: CompressionAlgorithm.GZIP, - headers: { 'Authorization': `Api-Token ${process.env.OTEL_API_KEY}` }, + headers: { Authorization: `Api-Token ${process.env.OTEL_API_KEY}` }, temporalityPreference: AggregationTemporality.DELTA, - url: process.env.OTEL_METRICS_ENDPOINT + url: process.env.OTEL_METRICS_ENDPOINT, }) } - log.info('Metrics exporting is disabled; set OTEL_METRICS_ENDPOINT to enable.') + logger.info('Metrics exporting is disabled; set OTEL_METRICS_ENDPOINT to enable.') return { // a no-op PushMetricExporter implementation - export: (metrics, resultCallback) => resultCallback({ code: ExportResultCode.SUCCESS }), + export: (_: any, resultCallback: (arg0: { code: ExportResultCode }) => any) => + resultCallback({ code: ExportResultCode.SUCCESS }), forceFlush: async () => {}, - shutdown: async () => {} + shutdown: async () => {}, } } @@ -93,31 +94,32 @@ function getTraceExporter() { throw new Error('OTEL_API_KEY must be configured when OTEL_TRACES_ENDPOINT is set') } - log.info(`Exporting traces to ${process.env.OTEL_TRACES_ENDPOINT} every 30000 ms`) + logger.info(`Exporting traces to ${process.env.OTEL_TRACES_ENDPOINT} every 30000 ms`) return new OTLPTraceExporter({ compression: CompressionAlgorithm.GZIP, - headers: { 'Authorization': `Api-Token ${process.env.OTEL_API_KEY}` }, + headers: { Authorization: `Api-Token ${process.env.OTEL_API_KEY}` }, url: process.env.OTEL_TRACES_ENDPOINT, }) } - log.info('Traces exporting is disabled; set OTEL_TRACES_ENDPOINT to enable.') + logger.info('Traces exporting is disabled; set OTEL_TRACES_ENDPOINT to enable.') return { // a no-op SpanExporter implementation - export: (spans, resultCallback) => resultCallback({ code: ExportResultCode.SUCCESS }), - shutdown: async () => {} + export: (_: any, resultCallback: (arg0: { code: ExportResultCode }) => any) => + resultCallback({ code: ExportResultCode.SUCCESS }), + shutdown: async () => {}, } } -log.info(`Initializing OpenTelemetry SDK...`) +logger.info(`Initializing OpenTelemetry SDK...`) new NodeSDK({ metricReader: new PeriodicExportingMetricReader({ exporter: getMetricExporter(), exportIntervalMillis: getMetricExportInterval(), - exportTimeoutMillis: getMetricTimeout() + exportTimeoutMillis: getMetricTimeout(), }), resource: new Resource({ // Note: any attributes added here must be configured in Dynatrace under @@ -127,7 +129,7 @@ new NodeSDK({ // see: node_modules/@opentelemetry/semantic-conventions/build/src/trace/SemanticResourceAttributes.js [SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: process.env.OTEL_ENVIRONMENT ?? 'local', [SemanticResourceAttributes.SERVICE_NAME]: process.env.OTEL_SERVICE_NAME ?? 'seniors-journey', - [SemanticResourceAttributes.SERVICE_VERSION]: process.env.BUILD_VERSION ?? '00000000-0000-00000000' + [SemanticResourceAttributes.SERVICE_VERSION]: process.env.BUILD_VERSION ?? '00000000-0000-00000000', }), resourceDetectors: [envDetector, hostDetector, osDetector, processDetector], traceExporter: getTraceExporter(), diff --git a/frontend/src/instrumentation.ts b/frontend/src/instrumentation.ts new file mode 100644 index 00000000..d50abd7a --- /dev/null +++ b/frontend/src/instrumentation.ts @@ -0,0 +1,5 @@ +export async function register() { + if (process.env.NEXT_RUNTIME === 'nodejs') { + await import('./instrumentation.node') + } +}