From 15d61cc88ba0a170e4c43ced6ef2102877bbe582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Comeau?= Date: Thu, 15 Jun 2023 10:03:30 -0300 Subject: [PATCH] [frontend] open-telemetry setup --- frontend/Dockerfile | 1 - frontend/next.config.js | 5 +- .../instrumentation.node.ts} | 61 +++++++++---------- frontend/src/instrumentation.ts | 5 ++ 4 files changed, 38 insertions(+), 34 deletions(-) rename frontend/{open-telemetry.config.js => src/instrumentation.node.ts} (65%) create mode 100644 frontend/src/instrumentation.ts 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 65% rename from frontend/open-telemetry.config.js rename to frontend/src/instrumentation.node.ts index e4cbe143..09f6d46e 100644 --- a/frontend/open-telemetry.config.js +++ b/frontend/src/instrumentation.node.ts @@ -32,21 +32,21 @@ *
  • 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, PushMetricExporter } from '@opentelemetry/sdk-metrics' +import { NodeSDK } from '@opentelemetry/sdk-node' +import { SpanExporter } from '@opentelemetry/sdk-trace-base' +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() { +const getMetricExporter = (): PushMetricExporter => { const exportMetrics = process.env.OTEL_METRICS_ENDPOINT if (exportMetrics) { @@ -54,38 +54,36 @@ 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 () => {}, } } -/** @returns {number} */ function getMetricExportInterval() { return parseInt(process.env.OTEL_METRICS_EXPORT_INTERVAL_MILLIS ?? '60000') } -/** @returns {number} */ function getMetricTimeout() { return parseInt(process.env.OTEL_METRICS_EXPORT_TIMEOUT_MILLIS ?? '30000') } -/** @returns {SpanExporter} */ -function getTraceExporter() { +const getTraceExporter = (): SpanExporter => { const exportTraces = process.env.OTEL_TRACES_ENDPOINT if (exportTraces) { @@ -93,31 +91,31 @@ 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 () => {} + shutdown: async () => {}, } } -log.info(`Initializing OpenTelemetry SDK...`) +logger.info(`Initializing OpenTelemetry SDK...`) -new NodeSDK({ +const 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,8 +125,9 @@ 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(), -}).start() +}) +sdk.start() 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') + } +}