diff --git a/packages/opentelemetry/README.md b/packages/opentelemetry/README.md index d6edc42f..194a1110 100644 --- a/packages/opentelemetry/README.md +++ b/packages/opentelemetry/README.md @@ -8,8 +8,8 @@ An [OpenTelemetry](https://opentelemetry.io/docs/what-is-opentelemetry/) client * [Usage](#usage) * [Setup](#setup) - * [Automated setup with `--require`](#automated-setup-with---require) - * [Automated setup with `require()`](#automated-setup-with-require) + * [Automated setup with `--import`](#automated-setup-with---import) + * [Automated setup with `import`](#automated-setup-with-import) * [Manual setup](#manual-setup) * [Sending custom metrics](#sending-custom-metrics) * [Running in production](#running-in-production) @@ -59,17 +59,17 @@ npm install --save @dotcom-reliability-kit/opentelemetry You can set up OpenTelemetry in a number of ways, each has pros and cons which we'll outline in the sections below. -#### Automated setup with `--require` +#### Automated setup with `--import` -You can completely avoid code changes by setting up OpenTelemetry using the Node.js [`--require` command-line option](https://nodejs.org/api/cli.html#-r---require-module): +You can completely avoid code changes by setting up OpenTelemetry using the Node.js [`--import` command-line option](https://nodejs.org/api/cli.html#importmodule): ```sh -node --require @dotcom-reliability-kit/opentelemetry/setup ./my-app.js +node --import @dotcom-reliability-kit/opentelemetry/setup ./my-app.js ``` This will import our setup script _before_ any of your code. OpenTelemetry will be [configured](#configuration-options) with environment variables. -For environments where you can't modify the `node` command directly (e.g. AWS Lambda) you'll need to specify this using the `NODE_OPTIONS` environment variable set to `--require @dotcom-reliability-kit/opentelemetry/setup`. +For environments where you can't modify the `node` command directly (e.g. AWS Lambda) you'll need to specify this using the `NODE_OPTIONS` environment variable set to `--import @dotcom-reliability-kit/opentelemetry/setup`. @@ -86,20 +86,18 @@ For environments where you can't modify the `node` command directly (e.g. AWS La
    -
  • It may be easy to accidentally remove the `--require`
  • +
  • It may be easy to accidentally remove the `--import`
-#### Automated setup with `require()` +#### Automated setup with `import` -If you can't use `--require`, e.g. because your tooling won't allow it, then you can include the setup script directly in your code: +If you can't use `--import`, e.g. because your tooling won't allow it, then you can include the setup script directly in your code: ```js import '@dotcom-reliability-kit/opentelemetry/setup'; -// or -require('@dotcom-reliability-kit/opentelemetry/setup'); ``` OpenTelemetry will be [configured](#configuration-options) with environment variables. @@ -134,8 +132,6 @@ If you'd like to customise the OpenTelemetry config more and have control over w ```js import * as opentelemetry from '@dotcom-reliability-kit/opentelemetry'; -// or -const opentelemetry = require('@dotcom-reliability-kit/opentelemetry'); ``` Call the function, passing in [configuration options](#configuration-options): @@ -178,8 +174,6 @@ In your code, load in the `getMeter` function: ```js import { getMeter } from '@dotcom-reliability-kit/opentelemetry'; -// or -const { getMeter } = require('@dotcom-reliability-kit/opentelemetry'); ``` You can now use it in the same way as the built-in OpenTelemetry equivalent. For more information, see the [OpenTelemetry Meter documentation](https://opentelemetry.io/docs/specs/otel/metrics/api/#meter). @@ -249,7 +243,7 @@ Some details about how we're implementing OpenTelemetry. This is to help avoid a Depending on the way you set up OpenTelemetry, you can either configure it via environment variables or options passed into an object. -For automated setups ([here](#automated-setup-with---require) and [here](#automated-setup-with-require)) you'll need to use environment variables, e.g. +For automated setups ([here](#automated-setup-with---import) and [here](#automated-setup-with-import)) you'll need to use environment variables, e.g. ```sh EXAMPLE=true npm start diff --git a/packages/opentelemetry/lib/config/instrumentations.js b/packages/opentelemetry/lib/config/instrumentations.js index a00ff42e..7aec3b6e 100644 --- a/packages/opentelemetry/lib/config/instrumentations.js +++ b/packages/opentelemetry/lib/config/instrumentations.js @@ -1,6 +1,6 @@ -const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node'); -const { logRecoverableError } = require('@dotcom-reliability-kit/log-error'); -const { UserInputError } = require('@dotcom-reliability-kit/errors'); +import { UserInputError } from '@dotcom-reliability-kit/errors'; +import { logRecoverableError } from '@dotcom-reliability-kit/log-error'; +import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'; /** * @import { NodeSDKConfiguration } from '@opentelemetry/sdk-node' @@ -15,7 +15,7 @@ const IGNORED_REQUEST_PATHS = ['/__gtg', '/__health', '/favicon.ico']; * * @returns {NodeSDKConfiguration['instrumentations']} */ -exports.createInstrumentationConfig = function createInstrumentationConfig() { +export function createInstrumentationConfig() { return getNodeAutoInstrumentations({ '@opentelemetry/instrumentation-http': { ignoreIncomingRequestHook @@ -24,7 +24,7 @@ exports.createInstrumentationConfig = function createInstrumentationConfig() { enabled: false } }); -}; +} /** * NOTE: this is not a filter like you know it. The name gives us a clue: diff --git a/packages/opentelemetry/lib/config/metrics.js b/packages/opentelemetry/lib/config/metrics.js index ca594ef0..8fbd2e6d 100644 --- a/packages/opentelemetry/lib/config/metrics.js +++ b/packages/opentelemetry/lib/config/metrics.js @@ -1,8 +1,10 @@ -const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-proto'); -const { CompressionAlgorithm } = require('@opentelemetry/otlp-exporter-base'); -const { PeriodicExportingMetricReader } = require('@opentelemetry/sdk-node').metrics; -const { default: logger } = require('@dotcom-reliability-kit/logger'); -const { METRICS_USER_AGENT } = require('./user-agents'); +import logger from '@dotcom-reliability-kit/logger'; +import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-proto'; +import { CompressionAlgorithm } from '@opentelemetry/otlp-exporter-base'; +import { metrics } from '@opentelemetry/sdk-node'; +import { METRICS_USER_AGENT } from './user-agents.js'; + +const { PeriodicExportingMetricReader } = metrics; /** * @import { NodeSDKConfiguration } from '@opentelemetry/sdk-node' @@ -15,7 +17,7 @@ const { METRICS_USER_AGENT } = require('./user-agents'); * @param {MetricsOptions} options * @returns {Partial} */ -exports.createMetricsConfig = function createMetricsConfig(options) { +export function createMetricsConfig(options) { /** @type {Partial} */ const config = {}; @@ -53,4 +55,4 @@ exports.createMetricsConfig = function createMetricsConfig(options) { } return config; -}; +} diff --git a/packages/opentelemetry/lib/config/resource.js b/packages/opentelemetry/lib/config/resource.js index d003287b..af88e205 100644 --- a/packages/opentelemetry/lib/config/resource.js +++ b/packages/opentelemetry/lib/config/resource.js @@ -1,10 +1,9 @@ -const appInfo = require('@dotcom-reliability-kit/app-info').semanticConventions; -const { defaultResource, resourceFromAttributes } = require('@opentelemetry/sdk-node').resources; -const { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } = require('@opentelemetry/semantic-conventions'); +import { semanticConventions as appInfo } from '@dotcom-reliability-kit/app-info'; -/** - * @import { resources } from '@opentelemetry/sdk-node' - */ +import { resources } from '@opentelemetry/sdk-node'; +import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions'; + +const { defaultResource, resourceFromAttributes } = resources; // These are hard-coded because they're unstable and OpenTelemetry advices we do this: // https://github.com/open-telemetry/opentelemetry-js/blob/main/semantic-conventions/README.md#unstable-semconv @@ -18,7 +17,7 @@ const ATTR_SERVICE_INSTANCE_ID = 'service.instance.id'; * * @returns {resources.Resource} */ -exports.createResourceConfig = function createResourceConfig() { +export function createResourceConfig() { // We set OpenTelemetry resource attributes based on app data return defaultResource().merge( resourceFromAttributes({ @@ -30,4 +29,4 @@ exports.createResourceConfig = function createResourceConfig() { [ATTR_DEPLOYMENT_ENVIRONMENT]: appInfo.deployment.environment }) ); -}; +} diff --git a/packages/opentelemetry/lib/config/tracing.js b/packages/opentelemetry/lib/config/tracing.js index e12fd5cb..c43992c8 100644 --- a/packages/opentelemetry/lib/config/tracing.js +++ b/packages/opentelemetry/lib/config/tracing.js @@ -1,8 +1,9 @@ -const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-proto'); -const { NoopSpanProcessor, TraceIdRatioBasedSampler } = require('@opentelemetry/sdk-node').tracing; +import logger from '@dotcom-reliability-kit/logger'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto'; +import { tracing } from '@opentelemetry/sdk-node'; +import { TRACING_USER_AGENT } from './user-agents.js'; -const { default: logger } = require('@dotcom-reliability-kit/logger'); -const { TRACING_USER_AGENT } = require('./user-agents.js'); +const { NoopSpanProcessor, TraceIdRatioBasedSampler } = tracing; /** * @import { NodeSDKConfiguration } from '@opentelemetry/sdk-node' @@ -17,7 +18,7 @@ const DEFAULT_SAMPLE_PERCENTAGE = 5; * @param {TracingOptions} options * @returns {Partial} */ -exports.createTracingConfig = function createTracingConfig(options) { +export function createTracingConfig(options) { /** @type {Partial} */ const config = {}; @@ -61,4 +62,4 @@ exports.createTracingConfig = function createTracingConfig(options) { } return config; -}; +} diff --git a/packages/opentelemetry/lib/config/user-agents.js b/packages/opentelemetry/lib/config/user-agents.js index d335f2cf..5716fc6f 100644 --- a/packages/opentelemetry/lib/config/user-agents.js +++ b/packages/opentelemetry/lib/config/user-agents.js @@ -1,9 +1,13 @@ -const appInfo = require('@dotcom-reliability-kit/app-info'); -const packageJson = require('../../package.json'); -const metricExporterPackageJson = require('@opentelemetry/exporter-metrics-otlp-proto/package.json'); -const traceExporterPackageJson = require('@opentelemetry/exporter-trace-otlp-proto/package.json'); +import appInfo from '@dotcom-reliability-kit/app-info'; +import metricExporterPackageJson from '@opentelemetry/exporter-metrics-otlp-proto/package.json' with { + type: 'json' +}; +import traceExporterPackageJson from '@opentelemetry/exporter-trace-otlp-proto/package.json' with { + type: 'json' +}; +import packageJson from '../../package.json' with { type: 'json' }; const BASE_USER_AGENT = `FTSystem/${appInfo.systemCode} (${packageJson.name}/${packageJson.version})`; -exports.METRICS_USER_AGENT = `${BASE_USER_AGENT} (${metricExporterPackageJson.name}/${metricExporterPackageJson.version})`; -exports.TRACING_USER_AGENT = `${BASE_USER_AGENT} (${traceExporterPackageJson.name}/${traceExporterPackageJson.version})`; +export const METRICS_USER_AGENT = `${BASE_USER_AGENT} (${metricExporterPackageJson.name}/${metricExporterPackageJson.version})`; +export const TRACING_USER_AGENT = `${BASE_USER_AGENT} (${traceExporterPackageJson.name}/${traceExporterPackageJson.version})`; diff --git a/packages/opentelemetry/lib/config/views.js b/packages/opentelemetry/lib/config/views.js index b4aa37a6..faee0363 100644 --- a/packages/opentelemetry/lib/config/views.js +++ b/packages/opentelemetry/lib/config/views.js @@ -1,5 +1,7 @@ -const { AggregationType, InstrumentType } = require('@opentelemetry/sdk-node').metrics; -const { default: logger } = require('@dotcom-reliability-kit/logger'); +import logger from '@dotcom-reliability-kit/logger'; +import { metrics } from '@opentelemetry/sdk-node'; + +const { AggregationType, InstrumentType } = metrics; /** * @import { NodeSDKConfiguration } from '@opentelemetry/sdk-node' @@ -13,10 +15,7 @@ const { default: logger } = require('@dotcom-reliability-kit/logger'); * @param {ViewOptions} options * @returns {Partial} */ -exports.createViewConfig = function createViewConfig({ - httpClientDurationBuckets, - httpServerDurationBuckets -}) { +export function createViewConfig({ httpClientDurationBuckets, httpServerDurationBuckets }) { const views = [ ...buildHistogramView({ instrumentName: 'http.client.duration', @@ -30,7 +29,7 @@ exports.createViewConfig = function createViewConfig({ }) ]; return views.length ? { views } : {}; -}; +} /** * @param {object} options diff --git a/packages/opentelemetry/lib/index.js b/packages/opentelemetry/lib/index.js index 40ff7bcc..6eb1ac1d 100644 --- a/packages/opentelemetry/lib/index.js +++ b/packages/opentelemetry/lib/index.js @@ -1,11 +1,11 @@ -const { createInstrumentationConfig } = require('./config/instrumentations.js'); -const { createMetricsConfig } = require('./config/metrics.js'); -const { createResourceConfig } = require('./config/resource.js'); -const { createTracingConfig } = require('./config/tracing.js'); -const { createViewConfig } = require('./config/views.js'); -const { HostMetrics } = require('@opentelemetry/host-metrics'); -const opentelemetry = require('@opentelemetry/sdk-node'); -const { default: logger } = require('@dotcom-reliability-kit/logger'); +import logger from '@dotcom-reliability-kit/logger'; +import { HostMetrics } from '@opentelemetry/host-metrics'; +import opentelemetry from '@opentelemetry/sdk-node'; +import { createInstrumentationConfig } from './config/instrumentations.js'; +import { createMetricsConfig } from './config/metrics.js'; +import { createResourceConfig } from './config/resource.js'; +import { createTracingConfig } from './config/tracing.js'; +import { createViewConfig } from './config/views.js'; /** * @import { Instances, Options } from '@dotcom-reliability-kit/opentelemetry' @@ -25,7 +25,7 @@ let instances; * @param {Options} [options] * @returns {Instances} */ -function setupOpenTelemetry({ +export function setup({ authorizationHeader, logInternals, metrics: metricsOptions, @@ -92,7 +92,12 @@ function setupOpenTelemetry({ // This is a known issue, the workaround is to import 'https' to ensure // that the instrumented version is used by node-fetch. See: // https://github.com/open-telemetry/opentelemetry-js-contrib/issues/2440 - require('node:https'); + import('node:https').catch( + // We need a catch here to ensure we don't exit the process if this fails + // for some reason, however we don't care about it in the test coverage + /* node:coverage ignore next */ + () => {} + ); // Set up host metrics if we have a metrics endpoint if (metricsOptions?.endpoint) { @@ -114,7 +119,7 @@ function setupOpenTelemetry({ * @param {opentelemetry.api.MeterOptions} [options] * @returns {opentelemetry.api.Meter} */ -function getMeter(name, version, options) { +export function getMeter(name, version, options) { if (!instances) { throw Object.assign( new Error( @@ -128,5 +133,11 @@ function getMeter(name, version, options) { return opentelemetry.api.metrics.getMeter(name, version, options); } -exports.setup = setupOpenTelemetry; -exports.getMeter = getMeter; +/** + * Helper method for us to clear instances during testing. + * + * @private + */ +export function clearInstances() { + instances = undefined; +} diff --git a/packages/opentelemetry/lib/setup-from-env.js b/packages/opentelemetry/lib/setup-from-env.js new file mode 100644 index 00000000..b2e77f95 --- /dev/null +++ b/packages/opentelemetry/lib/setup-from-env.js @@ -0,0 +1,59 @@ +import { setup } from './index.js'; + +/** + * @import { MetricsOptions, TracingOptions, ViewOptions } from '@dotcom-reliability-kit/opentelemetry' + */ + +/** + * @param {NodeJS.ProcessEnv} env + */ +export function setupFromEnv(env) { + /** @type {TracingOptions | undefined} */ + let tracing; + if (env.OPENTELEMETRY_TRACING_ENDPOINT) { + tracing = { + authorizationHeader: env.OPENTELEMETRY_AUTHORIZATION_HEADER, + endpoint: env.OPENTELEMETRY_TRACING_ENDPOINT, + samplePercentage: env.OPENTELEMETRY_TRACING_SAMPLE_PERCENTAGE + ? Number(env.OPENTELEMETRY_TRACING_SAMPLE_PERCENTAGE) + : undefined + }; + } + + /** @type {MetricsOptions | undefined} */ + let metrics; + if (env.OPENTELEMETRY_METRICS_ENDPOINT) { + metrics = { + apiGatewayKey: env.OPENTELEMETRY_API_GATEWAY_KEY, + endpoint: env.OPENTELEMETRY_METRICS_ENDPOINT + }; + } + + /** + * @param {string} input + * @returns {number[]} + */ + function parseListOfNumbers(input) { + return input.split(',').map((item) => Number(item.trim())); + } + + /** @type {ViewOptions} */ + const views = {}; + if (env.OPENTELEMETRY_VIEWS_HTTP_SERVER_DURATION_BUCKETS) { + views.httpServerDurationBuckets = parseListOfNumbers( + env.OPENTELEMETRY_VIEWS_HTTP_SERVER_DURATION_BUCKETS + ); + } + if (env.OPENTELEMETRY_VIEWS_HTTP_CLIENT_DURATION_BUCKETS) { + views.httpClientDurationBuckets = parseListOfNumbers( + env.OPENTELEMETRY_VIEWS_HTTP_CLIENT_DURATION_BUCKETS + ); + } + + setup({ + logInternals: Boolean(env.OPENTELEMETRY_LOG_INTERNALS), + metrics, + tracing, + views + }); +} diff --git a/packages/opentelemetry/package.json b/packages/opentelemetry/package.json index 864e6cc2..7bd83a1f 100644 --- a/packages/opentelemetry/package.json +++ b/packages/opentelemetry/package.json @@ -1,6 +1,7 @@ { "name": "@dotcom-reliability-kit/opentelemetry", "version": "3.2.5", + "type": "module", "description": "An OpenTelemetry client that's preconfigured for drop-in use in FT apps.", "repository": { "type": "git", diff --git a/packages/opentelemetry/setup.js b/packages/opentelemetry/setup.js index 89375a5e..355e4919 100644 --- a/packages/opentelemetry/setup.js +++ b/packages/opentelemetry/setup.js @@ -1,54 +1,3 @@ -const opentelemetry = require('./lib/index.js'); +import { setupFromEnv } from './lib/setup-from-env.js'; -/** - * @import { MetricsOptions, TracingOptions, ViewOptions } from '@dotcom-reliability-kit/opentelemetry' - */ - -/** @type {TracingOptions | undefined} */ -let tracing; -if (process.env.OPENTELEMETRY_TRACING_ENDPOINT) { - tracing = { - authorizationHeader: process.env.OPENTELEMETRY_AUTHORIZATION_HEADER, - endpoint: process.env.OPENTELEMETRY_TRACING_ENDPOINT, - samplePercentage: process.env.OPENTELEMETRY_TRACING_SAMPLE_PERCENTAGE - ? Number(process.env.OPENTELEMETRY_TRACING_SAMPLE_PERCENTAGE) - : undefined - }; -} - -/** @type {MetricsOptions | undefined} */ -let metrics; -if (process.env.OPENTELEMETRY_METRICS_ENDPOINT) { - metrics = { - apiGatewayKey: process.env.OPENTELEMETRY_API_GATEWAY_KEY, - endpoint: process.env.OPENTELEMETRY_METRICS_ENDPOINT - }; -} - -/** - * @param {string} input - * @returns {number[]} - */ -function parseListOfNumbers(input) { - return input.split(',').map((item) => Number(item.trim())); -} - -/** @type {ViewOptions} */ -const views = {}; -if (process.env.OPENTELEMETRY_VIEWS_HTTP_SERVER_DURATION_BUCKETS) { - views.httpServerDurationBuckets = parseListOfNumbers( - process.env.OPENTELEMETRY_VIEWS_HTTP_SERVER_DURATION_BUCKETS - ); -} -if (process.env.OPENTELEMETRY_VIEWS_HTTP_CLIENT_DURATION_BUCKETS) { - views.httpClientDurationBuckets = parseListOfNumbers( - process.env.OPENTELEMETRY_VIEWS_HTTP_CLIENT_DURATION_BUCKETS - ); -} - -opentelemetry.setup({ - logInternals: Boolean(process.env.OPENTELEMETRY_LOG_INTERNALS), - metrics, - tracing, - views -}); +setupFromEnv(process.env); diff --git a/packages/opentelemetry/test/end-to-end/fixtures/app.js b/packages/opentelemetry/test/end-to-end/fixtures/app.js index e0ae0dab..db6a7e46 100644 --- a/packages/opentelemetry/test/end-to-end/fixtures/app.js +++ b/packages/opentelemetry/test/end-to-end/fixtures/app.js @@ -1,8 +1,8 @@ -const express = require('express'); -const { default: logger } = require('@dotcom-reliability-kit/logger'); +import logger from '@dotcom-reliability-kit/logger'; +// We set up OpenTelemetry via `--import` but this allows us to grab the same instances +import { setup } from '@dotcom-reliability-kit/opentelemetry'; +import express from 'express'; -// We set up OpenTelemetry via `--require` but this allows us to grab the same instances -const { setup } = require('@dotcom-reliability-kit/opentelemetry'); const { sdk } = setup(); const app = express(); diff --git a/packages/opentelemetry/test/end-to-end/fixtures/collector.js b/packages/opentelemetry/test/end-to-end/fixtures/collector.js index b41325ca..43d46f91 100644 --- a/packages/opentelemetry/test/end-to-end/fixtures/collector.js +++ b/packages/opentelemetry/test/end-to-end/fixtures/collector.js @@ -1,5 +1,5 @@ -const express = require('express'); -const { default: logger } = require('@dotcom-reliability-kit/logger'); +import logger from '@dotcom-reliability-kit/logger'; +import express from 'express'; const app = express(); diff --git a/packages/opentelemetry/test/end-to-end/index.spec.js b/packages/opentelemetry/test/end-to-end/index.spec.js index 92a5ae2e..a7c5a154 100644 --- a/packages/opentelemetry/test/end-to-end/index.spec.js +++ b/packages/opentelemetry/test/end-to-end/index.spec.js @@ -1,7 +1,7 @@ -const { after, before, describe, it } = require('node:test'); -const assert = require('node:assert/strict'); -const { fork } = require('node:child_process'); -const { setTimeout } = require('node:timers/promises'); +import assert from 'node:assert/strict'; +import { fork } from 'node:child_process'; +import { after, before, describe, it } from 'node:test'; +import { setTimeout } from 'node:timers/promises'; function waitForBaseUrl(childProcess) { return new Promise((resolve) => { @@ -35,7 +35,7 @@ describe('@dotcom-reliability-kit/opentelemetry end-to-end', () => { before(async () => { // Set up a mock collector - collector = fork(`${__dirname}/fixtures/collector.js`, { + collector = fork(`${import.meta.dirname}/fixtures/collector.js`, { env: { ...process.env, NODE_ENV: 'production', @@ -52,7 +52,7 @@ describe('@dotcom-reliability-kit/opentelemetry end-to-end', () => { collectorBaseUrl = await waitForBaseUrl(collector); // Set up a Node.js app that sends Opentelemetry metrics and traces - exporter = fork(`${__dirname}/fixtures/app.js`, { + exporter = fork(`${import.meta.dirname}/fixtures/app.js`, { env: { ...process.env, NODE_ENV: 'production', @@ -61,7 +61,7 @@ describe('@dotcom-reliability-kit/opentelemetry end-to-end', () => { OPENTELEMETRY_TRACING_SAMPLE_PERCENTAGE: '100', SYSTEM_CODE: 'mock-system' }, - execArgv: ['--require', '@dotcom-reliability-kit/opentelemetry/setup'], + execArgv: ['--import', '@dotcom-reliability-kit/opentelemetry/setup'], stdio: 'pipe' }); exporter.stdout.on('data', (chunk) => { diff --git a/packages/opentelemetry/test/unit/lib/config/instrumentations.spec.js b/packages/opentelemetry/test/unit/lib/config/instrumentations.spec.js index ac0dce5b..e37b9248 100644 --- a/packages/opentelemetry/test/unit/lib/config/instrumentations.spec.js +++ b/packages/opentelemetry/test/unit/lib/config/instrumentations.spec.js @@ -1,5 +1,5 @@ -const { before, beforeEach, describe, it, mock } = require('node:test'); -const assert = require('node:assert/strict'); +import assert from 'node:assert/strict'; +import { before, beforeEach, describe, it, mock } from 'node:test'; const getNodeAutoInstrumentations = mock.fn(() => 'mock-auto-instrumentations'); mock.module('@opentelemetry/auto-instrumentations-node', { @@ -12,7 +12,7 @@ mock.module('@dotcom-reliability-kit/log-error', { namedExports: { logRecoverabl const UserInputError = mock.fn(class UserInputError {}); mock.module('@dotcom-reliability-kit/errors', { namedExports: { UserInputError } }); -const { createInstrumentationConfig } = require('../../../../lib/config/instrumentations.js'); +const { createInstrumentationConfig } = await import('../../../../lib/config/instrumentations.js'); describe('@dotcom-reliability-kit/opentelemetry/lib/config/instrumentation', () => { it('exports a function', () => { diff --git a/packages/opentelemetry/test/unit/lib/config/metrics.spec.js b/packages/opentelemetry/test/unit/lib/config/metrics.spec.js index b6877cc9..f4bbddd7 100644 --- a/packages/opentelemetry/test/unit/lib/config/metrics.spec.js +++ b/packages/opentelemetry/test/unit/lib/config/metrics.spec.js @@ -1,12 +1,8 @@ -const { before, describe, it, mock } = require('node:test'); -const assert = require('node:assert/strict'); +import assert from 'node:assert/strict'; +import { before, describe, it, mock } from 'node:test'; const logger = { info: mock.fn() }; -mock.module('@dotcom-reliability-kit/logger', { - // NOTE: this is temporary while we're importing ESM into CommonJS. - // Should be switched back when we migrate opentelemetry to ESM. - namedExports: { default: logger } -}); +mock.module('@dotcom-reliability-kit/logger', { defaultExport: logger }); mock.module('../../../../lib/config/user-agents.js', { namedExports: { METRICS_USER_AGENT: 'mock-metrics-user-agent' } @@ -24,7 +20,7 @@ mock.module('@opentelemetry/otlp-exporter-base', { namedExports: { CompressionAlgorithm: { GZIP: 'gzip' } } }); -const { createMetricsConfig } = require('../../../../lib/config/metrics'); +const { createMetricsConfig } = await import('../../../../lib/config/metrics.js'); describe('@dotcom-reliability-kit/opentelemetry/lib/config/metrics', () => { it('exports a function', () => { diff --git a/packages/opentelemetry/test/unit/lib/config/resource.spec.js b/packages/opentelemetry/test/unit/lib/config/resource.spec.js index be47a754..bcd8238c 100644 --- a/packages/opentelemetry/test/unit/lib/config/resource.spec.js +++ b/packages/opentelemetry/test/unit/lib/config/resource.spec.js @@ -1,5 +1,5 @@ -const { beforeEach, describe, it, mock } = require('node:test'); -const assert = require('node:assert/strict'); +import assert from 'node:assert/strict'; +import { beforeEach, describe, it, mock } from 'node:test'; const appInfo = { semanticConventions: { @@ -19,12 +19,12 @@ const appInfo = { } } }; -mock.module('@dotcom-reliability-kit/app-info', { defaultExport: appInfo }); +mock.module('@dotcom-reliability-kit/app-info', { namedExports: appInfo }); const defaultResource = mock.fn(() => ({ merge: mock.fn(() => 'mock-merged-resource') })); const resourceFromAttributes = mock.fn(() => 'mock-resource'); mock.module('@opentelemetry/sdk-node', { - defaultExport: { resources: { defaultResource, resourceFromAttributes } } + namedExports: { resources: { defaultResource, resourceFromAttributes } } }); mock.module('@opentelemetry/semantic-conventions', { @@ -34,7 +34,7 @@ mock.module('@opentelemetry/semantic-conventions', { } }); -const { createResourceConfig } = require('../../../../lib/config/resource.js'); +const { createResourceConfig } = await import('../../../../lib/config/resource.js'); describe('@dotcom-reliability-kit/opentelemetry/lib/config/resource', () => { it('exports a function', () => { diff --git a/packages/opentelemetry/test/unit/lib/config/tracing.spec.js b/packages/opentelemetry/test/unit/lib/config/tracing.spec.js index ba9cac1b..fb847e4f 100644 --- a/packages/opentelemetry/test/unit/lib/config/tracing.spec.js +++ b/packages/opentelemetry/test/unit/lib/config/tracing.spec.js @@ -1,12 +1,8 @@ -const { before, describe, it, mock } = require('node:test'); -const assert = require('node:assert/strict'); +import assert from 'node:assert/strict'; +import { before, describe, it, mock } from 'node:test'; const logger = { info: mock.fn() }; -mock.module('@dotcom-reliability-kit/logger', { - // NOTE: this is temporary while we're importing ESM into CommonJS. - // Should be switched back when we migrate opentelemetry to ESM. - namedExports: { default: logger } -}); +mock.module('@dotcom-reliability-kit/logger', { defaultExport: logger }); mock.module('../../../../lib/config/user-agents.js', { namedExports: { TRACING_USER_AGENT: 'mock-tracing-user-agent' } @@ -21,7 +17,7 @@ mock.module('@opentelemetry/sdk-node', { namedExports: { tracing: { NoopSpanProcessor, TraceIdRatioBasedSampler } } }); -const { createTracingConfig } = require('../../../../lib/config/tracing.js'); +const { createTracingConfig } = await import('../../../../lib/config/tracing.js'); describe('@dotcom-reliability-kit/opentelemetry/lib/config/tracing', () => { it('exports a function', () => { diff --git a/packages/opentelemetry/test/unit/lib/config/user-agents.spec.js b/packages/opentelemetry/test/unit/lib/config/user-agents.spec.js index 48df2a20..fb4bf1ac 100644 --- a/packages/opentelemetry/test/unit/lib/config/user-agents.spec.js +++ b/packages/opentelemetry/test/unit/lib/config/user-agents.spec.js @@ -1,5 +1,5 @@ -const { describe, it, mock } = require('node:test'); -const assert = require('node:assert/strict'); +import assert from 'node:assert/strict'; +import { describe, it, mock } from 'node:test'; const appInfo = { systemCode: 'mock-system-code' }; mock.module('@dotcom-reliability-kit/app-info', { defaultExport: appInfo }); @@ -24,7 +24,9 @@ mock.module('@opentelemetry/exporter-trace-otlp-proto/package.json', { } }); -const { METRICS_USER_AGENT, TRACING_USER_AGENT } = require('../../../../lib/config/user-agents.js'); +const { METRICS_USER_AGENT, TRACING_USER_AGENT } = await import( + '../../../../lib/config/user-agents.js' +); describe('@dotcom-reliability-kit/opentelemetry/lib/config/resource', () => { describe('.METRICS_USER_AGENT', () => { diff --git a/packages/opentelemetry/test/unit/lib/config/views.spec.js b/packages/opentelemetry/test/unit/lib/config/views.spec.js index 99aa0ad0..ea501a96 100644 --- a/packages/opentelemetry/test/unit/lib/config/views.spec.js +++ b/packages/opentelemetry/test/unit/lib/config/views.spec.js @@ -1,20 +1,16 @@ -const { before, describe, it, mock } = require('node:test'); -const assert = require('node:assert/strict'); +import assert from 'node:assert/strict'; +import { before, describe, it, mock } from 'node:test'; const logger = { warn: mock.fn() }; -mock.module('@dotcom-reliability-kit/logger', { - // NOTE: this is temporary while we're importing ESM into CommonJS. - // Should be switched back when we migrate opentelemetry to ESM. - namedExports: { default: logger } -}); +mock.module('@dotcom-reliability-kit/logger', { defaultExport: logger }); const metrics = { AggregationType: { EXPLICIT_BUCKET_HISTOGRAM: 'mock-explicit-bucket' }, InstrumentType: { HISTOGRAM: 'mock-histogram' } }; -mock.module('@opentelemetry/sdk-node', { defaultExport: { metrics } }); +mock.module('@opentelemetry/sdk-node', { namedExports: { metrics } }); -const { createViewConfig } = require('../../../../lib/config/views'); +const { createViewConfig } = await import('../../../../lib/config/views.js'); describe('@dotcom-reliability-kit/opentelemetry/lib/config/views', () => { it('exports a function', () => { diff --git a/packages/opentelemetry/test/unit/lib/index.spec.js b/packages/opentelemetry/test/unit/lib/index.spec.js index 636f4895..83925dfc 100644 --- a/packages/opentelemetry/test/unit/lib/index.spec.js +++ b/packages/opentelemetry/test/unit/lib/index.spec.js @@ -1,101 +1,60 @@ -const { before, beforeEach, describe, it, mock } = require('node:test'); -const assert = require('node:assert/strict'); - -mock.module('@dotcom-reliability-kit/logger', { - // NOTE: this is temporary while we're importing ESM into CommonJS. - // Should be switched back when we migrate opentelemetry to ESM. - namedExports: { default: { createChildLogger: mock.fn(), warn: mock.fn() } } -}); - -mock.module('@opentelemetry/sdk-node', { - defaultExport: { - api: { - diag: { disable: mock.fn(), setLogger: mock.fn() }, - DiagLogLevel: {}, - metrics: { - getMeter: mock.fn(() => 'mock-meter'), - getMeterProvider: mock.fn(() => 'mock-meter-provider') - } - }, - NodeSDK: mock.fn( - class NodeSDK { - start = mock.fn(); - } - ) +import assert from 'node:assert/strict'; +import { before, beforeEach, describe, it, mock } from 'node:test'; + +const mockChildLogger = { + debug: mock.fn(), + error: mock.fn(), + info: mock.fn(), + verbose: mock.fn(), + warn: mock.fn() +}; +const logger = { createChildLogger: mock.fn(() => mockChildLogger), warn: mock.fn() }; +mock.module('@dotcom-reliability-kit/logger', { defaultExport: logger }); + +const api = { + diag: { disable: mock.fn(), setLogger: mock.fn() }, + DiagLogLevel: { + INFO: 'mock info log level' + }, + metrics: { + getMeter: mock.fn(() => 'mock-meter'), + getMeterProvider: mock.fn(() => 'mock-meter-provider') } -}); +}; +const NodeSDK = mock.fn( + class NodeSDK { + start = mock.fn(); + } +); +mock.module('@opentelemetry/sdk-node', { defaultExport: { api, NodeSDK } }); -mock.module('@opentelemetry/host-metrics', { - namedExports: { - HostMetrics: mock.fn( - class HostMetrics { - start = mock.fn(); - } - ) +const HostMetrics = mock.fn( + class HostMetrics { + start = mock.fn(); } -}); +); +mock.module('@opentelemetry/host-metrics', { namedExports: { HostMetrics } }); +const createInstrumentationConfig = mock.fn(() => 'mock-instrumentations'); mock.module('../../../lib/config/instrumentations.js', { - namedExports: { createInstrumentationConfig: mock.fn(() => 'mock-instrumentations') } -}); -mock.module('../../../lib/config/metrics.js', { - namedExports: { createMetricsConfig: mock.fn(() => ({ metrics: 'mock-metrics' })) } -}); -mock.module('../../../lib/config/resource.js', { - namedExports: { createResourceConfig: mock.fn(() => 'mock-resource') } -}); -mock.module('../../../lib/config/tracing.js', { - namedExports: { createTracingConfig: mock.fn(() => ({ tracing: 'mock-tracing' })) } -}); -mock.module('../../../lib/config/views.js', { - namedExports: { createViewConfig: mock.fn(() => ({ views: 'mock-views' })) } + namedExports: { createInstrumentationConfig } }); -describe('@dotcom-reliability-kit/opentelemetry', () => { - let api; - let createInstrumentationConfig; - let createMetricsConfig; - let createResourceConfig; - let createTracingConfig; - let createViewConfig; - let HostMetrics; - let logger; - let mockChildLogger; - let NodeSDK; - let opentelemetry; - - // Helper function to reload all modules. We need this because the setup - // method stores a global singleton so it's impossible to call it multiple - // times with different configuration values normally. We need to do this - // in the tests though - function reloadAllModules() { - createInstrumentationConfig = - require('../../../lib/config/instrumentations.js').createInstrumentationConfig; - createMetricsConfig = require('../../../lib/config/metrics.js').createMetricsConfig; - createResourceConfig = require('../../../lib/config/resource.js').createResourceConfig; - createTracingConfig = require('../../../lib/config/tracing.js').createTracingConfig; - createViewConfig = require('../../../lib/config/views.js').createViewConfig; - api = require('@opentelemetry/sdk-node').api; - HostMetrics = require('@opentelemetry/host-metrics').HostMetrics; - NodeSDK = require('@opentelemetry/sdk-node').NodeSDK; - logger = require('@dotcom-reliability-kit/logger').default; - - mockChildLogger = { - debug: mock.fn(), - error: mock.fn(), - info: mock.fn(), - verbose: mock.fn(), - warn: mock.fn() - }; - logger.createChildLogger.mock.mockImplementation(() => mockChildLogger); - api.DiagLogLevel.INFO = 'mock info log level'; - - delete require.cache[require.resolve('../../../lib/index.js')]; - opentelemetry = require('../../../lib/index.js'); - } +const createMetricsConfig = mock.fn(() => ({ metrics: 'mock-metrics' })); +mock.module('../../../lib/config/metrics.js', { namedExports: { createMetricsConfig } }); - before(reloadAllModules); +const createResourceConfig = mock.fn(() => 'mock-resource'); +mock.module('../../../lib/config/resource.js', { namedExports: { createResourceConfig } }); +const createTracingConfig = mock.fn(() => ({ tracing: 'mock-tracing' })); +mock.module('../../../lib/config/tracing.js', { namedExports: { createTracingConfig } }); + +const createViewConfig = mock.fn(() => ({ views: 'mock-views' })); +mock.module('../../../lib/config/views.js', { namedExports: { createViewConfig } }); + +const opentelemetry = await import('../../../lib/index.js'); + +describe('@dotcom-reliability-kit/opentelemetry', () => { describe('.setup(options)', () => { let instances; @@ -195,7 +154,7 @@ describe('@dotcom-reliability-kit/opentelemetry', () => { api.diag.disable.mock.resetCalls(); logger.createChildLogger.mock.resetCalls(); NodeSDK.mock.resetCalls(); - reloadAllModules(); + opentelemetry.clearInstances(); instances = opentelemetry.setup({ logInternals: true, tracing: { @@ -293,7 +252,7 @@ describe('@dotcom-reliability-kit/opentelemetry', () => { describe('when no metrics endpoint is set', () => { beforeEach(() => { HostMetrics.mock.resetCalls(); - reloadAllModules(); + opentelemetry.clearInstances(); instances = opentelemetry.setup({ tracing: { endpoint: 'mock-tracing-endpoint', @@ -314,7 +273,7 @@ describe('@dotcom-reliability-kit/opentelemetry', () => { describe('when no options are set', () => { beforeEach(() => { NodeSDK.mock.resetCalls(); - reloadAllModules(); + opentelemetry.clearInstances(); opentelemetry.setup(); }); @@ -335,7 +294,7 @@ describe('@dotcom-reliability-kit/opentelemetry', () => { describe('when an authorization header is passed into the root options (deprecated)', () => { beforeEach(() => { createTracingConfig.mock.resetCalls(); - reloadAllModules(); + opentelemetry.clearInstances(); opentelemetry.setup({ authorizationHeader: 'mock-authorization-header-root', tracing: { @@ -358,7 +317,7 @@ describe('@dotcom-reliability-kit/opentelemetry', () => { describe('when an authorization header is passed into the tracing options', () => { beforeEach(() => { createTracingConfig.mock.resetCalls(); - reloadAllModules(); + opentelemetry.clearInstances(); opentelemetry.setup({ tracing: { authorizationHeader: 'mock-authorization-header-tracing', @@ -381,7 +340,7 @@ describe('@dotcom-reliability-kit/opentelemetry', () => { describe('when an authorization header is passed into both the root options (deprecated) and tracing options', () => { beforeEach(() => { createTracingConfig.mock.resetCalls(); - reloadAllModules(); + opentelemetry.clearInstances(); opentelemetry.setup({ authorizationHeader: 'mock-authorization-header-root', tracing: { @@ -405,7 +364,7 @@ describe('@dotcom-reliability-kit/opentelemetry', () => { describe('when OTEL_ environment variables are defined', () => { beforeEach(() => { process.env.OTEL_MOCK = 'mock'; - reloadAllModules(); + opentelemetry.clearInstances(); opentelemetry.setup(); }); @@ -426,7 +385,7 @@ describe('@dotcom-reliability-kit/opentelemetry', () => { beforeEach(() => { NodeSDK.mock.resetCalls(); - reloadAllModules(); + opentelemetry.clearInstances(); returnValue1 = opentelemetry.setup({ tracing: { endpoint: 'mock-tracing-endpoint', @@ -463,7 +422,7 @@ describe('@dotcom-reliability-kit/opentelemetry', () => { let meter; beforeEach(() => { - reloadAllModules(); + opentelemetry.clearInstances(); opentelemetry.setup(); meter = opentelemetry.getMeter('mock-name', 'mock-version', 'mock-options'); }); @@ -480,7 +439,7 @@ describe('@dotcom-reliability-kit/opentelemetry', () => { describe('when OpenTelemetry has not been set up via Reliability Kit', () => { beforeEach(() => { - reloadAllModules(); + opentelemetry.clearInstances(); }); it('throws an error', () => { diff --git a/packages/opentelemetry/test/unit/lib/setup-from-env.spec.js b/packages/opentelemetry/test/unit/lib/setup-from-env.spec.js new file mode 100644 index 00000000..8b30c669 --- /dev/null +++ b/packages/opentelemetry/test/unit/lib/setup-from-env.spec.js @@ -0,0 +1,177 @@ +import assert from 'node:assert/strict'; +import { beforeEach, describe, it, mock } from 'node:test'; + +const opentelemetry = { setup: mock.fn() }; +mock.module('../../../lib/index.js', { namedExports: opentelemetry }); + +const { setupFromEnv } = await import('../../../lib/setup-from-env.js'); + +describe('@dotcom-reliability-kit/opentelemetry/lib/setup-from-env', () => { + beforeEach(() => { + opentelemetry.setup.mock.resetCalls(); + }); + + describe('.setupFromEnv(env)', () => { + it('calls opentelemetry.setup with the correct parameters', () => { + setupFromEnv({ + OPENTELEMETRY_TRACING_ENDPOINT: 'MOCK_TRACING_ENDPOINT', + OPENTELEMETRY_AUTHORIZATION_HEADER: 'MOCK_AUTH_HEADER', + OPENTELEMETRY_METRICS_ENDPOINT: 'MOCK_METRICS_ENDPOINT', + OPENTELEMETRY_API_GATEWAY_KEY: 'MOCK_API_GATEWAY_KEY' + }); + + assert.strictEqual(opentelemetry.setup.mock.callCount(), 1); + assert.partialDeepStrictEqual(opentelemetry.setup.mock.calls[0].arguments, [ + { + logInternals: false, + tracing: { + authorizationHeader: 'MOCK_AUTH_HEADER', + endpoint: 'MOCK_TRACING_ENDPOINT' + }, + metrics: { + apiGatewayKey: 'MOCK_API_GATEWAY_KEY', + endpoint: 'MOCK_METRICS_ENDPOINT' + }, + views: {} + } + ]); + }); + + describe('when no traces endpoint is specified', () => { + it('should not include tracing configuration', () => { + setupFromEnv({ + OPENTELEMETRY_METRICS_ENDPOINT: 'MOCK_METRICS_ENDPOINT', + OPENTELEMETRY_API_GATEWAY_KEY: 'MOCK_API_GATEWAY_KEY' + }); + + assert.strictEqual(opentelemetry.setup.mock.callCount(), 1); + assert.partialDeepStrictEqual(opentelemetry.setup.mock.calls[0].arguments, [ + { + logInternals: false, + metrics: { + apiGatewayKey: 'MOCK_API_GATEWAY_KEY', + endpoint: 'MOCK_METRICS_ENDPOINT' + }, + views: {} + } + ]); + }); + }); + + describe('when no metrics endpoint is specified', () => { + it('should not include metrics configuration', () => { + setupFromEnv({ + OPENTELEMETRY_TRACING_ENDPOINT: 'MOCK_TRACING_ENDPOINT', + OPENTELEMETRY_AUTHORIZATION_HEADER: 'MOCK_AUTH_HEADER' + }); + + assert.strictEqual(opentelemetry.setup.mock.callCount(), 1); + assert.partialDeepStrictEqual(opentelemetry.setup.mock.calls[0].arguments, [ + { + logInternals: false, + tracing: { + authorizationHeader: 'MOCK_AUTH_HEADER', + endpoint: 'MOCK_TRACING_ENDPOINT' + }, + views: {} + } + ]); + }); + }); + + describe('when an HTTP server duration bucket is specified', () => { + it('includes views configurations', () => { + setupFromEnv({ + OPENTELEMETRY_VIEWS_HTTP_SERVER_DURATION_BUCKETS: '1,2,3, 4 ,five' + }); + + assert.strictEqual(opentelemetry.setup.mock.callCount(), 1); + assert.partialDeepStrictEqual(opentelemetry.setup.mock.calls[0].arguments, [ + { + views: { + httpServerDurationBuckets: [1, 2, 3, 4, NaN] + } + } + ]); + }); + }); + + describe('when an HTTP client duration bucket is specified', () => { + it('includes views configurations', () => { + setupFromEnv({ + OPENTELEMETRY_VIEWS_HTTP_CLIENT_DURATION_BUCKETS: '1,2,3, 4 ,five' + }); + + assert.strictEqual(opentelemetry.setup.mock.callCount(), 1); + assert.partialDeepStrictEqual(opentelemetry.setup.mock.calls[0].arguments, [ + { + views: { + httpClientDurationBuckets: [1, 2, 3, 4, NaN] + } + } + ]); + }); + }); + + describe('when a sample rate is specified', () => { + it('calls OpenTelemetry with the given sample percentage as a number', () => { + setupFromEnv({ + OPENTELEMETRY_TRACING_ENDPOINT: 'MOCK_TRACING_ENDPOINT', + OPENTELEMETRY_TRACING_SAMPLE_PERCENTAGE: '50', + OPENTELEMETRY_AUTHORIZATION_HEADER: 'MOCK_AUTH_HEADER' + }); + + assert.strictEqual(opentelemetry.setup.mock.callCount(), 1); + assert.partialDeepStrictEqual(opentelemetry.setup.mock.calls[0].arguments, [ + { + logInternals: false, + tracing: { + authorizationHeader: 'MOCK_AUTH_HEADER', + endpoint: 'MOCK_TRACING_ENDPOINT', + samplePercentage: 50 + }, + views: {} + } + ]); + }); + }); + + describe('when a non-numeric sample rate is specified', () => { + it('calls OpenTelemetry with NaN as a percentage', () => { + setupFromEnv({ + OPENTELEMETRY_TRACING_ENDPOINT: 'MOCK_TRACING_ENDPOINT', + OPENTELEMETRY_TRACING_SAMPLE_PERCENTAGE: 'nope', + OPENTELEMETRY_AUTHORIZATION_HEADER: 'MOCK_AUTH_HEADER' + }); + + assert.strictEqual(opentelemetry.setup.mock.callCount(), 1); + assert.partialDeepStrictEqual(opentelemetry.setup.mock.calls[0].arguments, [ + { + logInternals: false, + tracing: { + authorizationHeader: 'MOCK_AUTH_HEADER', + endpoint: 'MOCK_TRACING_ENDPOINT', + samplePercentage: NaN + }, + views: {} + } + ]); + }); + }); + + describe('when internal logs are enabled', () => { + it('calls OpenTelemetry with the logInternal option set to true', () => { + setupFromEnv({ + OPENTELEMETRY_LOG_INTERNALS: 'true' + }); + + assert.strictEqual(opentelemetry.setup.mock.callCount(), 1); + assert.partialDeepStrictEqual(opentelemetry.setup.mock.calls[0].arguments, [ + { + logInternals: true + } + ]); + }); + }); + }); +}); diff --git a/packages/opentelemetry/test/unit/setup.spec.js b/packages/opentelemetry/test/unit/setup.spec.js index e7283fb4..fa895359 100644 --- a/packages/opentelemetry/test/unit/setup.spec.js +++ b/packages/opentelemetry/test/unit/setup.spec.js @@ -1,175 +1,17 @@ -const { beforeEach, describe, it, mock } = require('node:test'); -const assert = require('node:assert/strict'); +import assert from 'node:assert/strict'; +import { beforeEach, describe, it, mock } from 'node:test'; -describe('setup', () => { - let opentelemetry; - - beforeEach((test) => { - mock.restoreAll(); - opentelemetry = { setup: mock.fn() }; - test.mock.module('../../lib/index.js', { defaultExport: opentelemetry }); - }); - - it('calls opentelemetry.setup with the correct parameters', () => { - delete process.env.OPENTELEMETRY_LOG_INTERNALS; - process.env.OPENTELEMETRY_TRACING_ENDPOINT = 'MOCK_TRACING_ENDPOINT'; - process.env.OPENTELEMETRY_AUTHORIZATION_HEADER = 'MOCK_AUTH_HEADER'; - process.env.OPENTELEMETRY_METRICS_ENDPOINT = 'MOCK_METRICS_ENDPOINT'; - process.env.OPENTELEMETRY_API_GATEWAY_KEY = 'MOCK_API_GATEWAY_KEY'; - delete require.cache[require.resolve('@dotcom-reliability-kit/opentelemetry/setup')]; - require('@dotcom-reliability-kit/opentelemetry/setup'); - - assert.strictEqual(opentelemetry.setup.mock.callCount(), 1); - assert.partialDeepStrictEqual(opentelemetry.setup.mock.calls[0].arguments, [ - { - logInternals: false, - tracing: { - authorizationHeader: 'MOCK_AUTH_HEADER', - endpoint: 'MOCK_TRACING_ENDPOINT' - }, - metrics: { - apiGatewayKey: 'MOCK_API_GATEWAY_KEY', - endpoint: 'MOCK_METRICS_ENDPOINT' - }, - views: {} - } - ]); - }); - - describe('when no traces endpoint is specified', () => { - it('should not include tracing configuration', () => { - delete process.env.OPENTELEMETRY_TRACING_ENDPOINT; - process.env.OPENTELEMETRY_METRICS_ENDPOINT = 'MOCK_METRICS_ENDPOINT'; - delete require.cache[require.resolve('@dotcom-reliability-kit/opentelemetry/setup')]; - require('@dotcom-reliability-kit/opentelemetry/setup'); - - assert.strictEqual(opentelemetry.setup.mock.callCount(), 1); - assert.partialDeepStrictEqual(opentelemetry.setup.mock.calls[0].arguments, [ - { - logInternals: false, - metrics: { - apiGatewayKey: 'MOCK_API_GATEWAY_KEY', - endpoint: 'MOCK_METRICS_ENDPOINT' - }, - views: {} - } - ]); - }); - }); - - describe('when no metrics endpoint is specified', () => { - it('should not include metrics configuration', () => { - delete process.env.OPENTELEMETRY_METRICS_ENDPOINT; - process.env.OPENTELEMETRY_TRACING_ENDPOINT = 'MOCK_TRACING_ENDPOINT'; - delete require.cache[require.resolve('@dotcom-reliability-kit/opentelemetry/setup')]; - require('@dotcom-reliability-kit/opentelemetry/setup'); - - assert.strictEqual(opentelemetry.setup.mock.callCount(), 1); - assert.partialDeepStrictEqual(opentelemetry.setup.mock.calls[0].arguments, [ - { - logInternals: false, - tracing: { - authorizationHeader: 'MOCK_AUTH_HEADER', - endpoint: 'MOCK_TRACING_ENDPOINT' - }, - views: {} - } - ]); - }); - }); +const setupFromEnv = mock.fn(); +mock.module('../../lib/setup-from-env.js', { namedExports: { setupFromEnv } }); - describe('when an HTTP server duration bucket is specified', () => { - it('includes views configurations', () => { - process.env.OPENTELEMETRY_VIEWS_HTTP_SERVER_DURATION_BUCKETS = '1,2,3, 4 ,five'; - delete require.cache[require.resolve('@dotcom-reliability-kit/opentelemetry/setup')]; - require('@dotcom-reliability-kit/opentelemetry/setup'); - - assert.strictEqual(opentelemetry.setup.mock.callCount(), 1); - assert.partialDeepStrictEqual(opentelemetry.setup.mock.calls[0].arguments, [ - { - views: { - httpServerDurationBuckets: [1, 2, 3, 4, NaN] - } - } - ]); - delete process.env.OPENTELEMETRY_VIEWS_HTTP_SERVER_DURATION_BUCKETS; - }); - }); - - describe('when an HTTP client duration bucket is specified', () => { - it('includes views configurations', () => { - process.env.OPENTELEMETRY_VIEWS_HTTP_CLIENT_DURATION_BUCKETS = '1,2,3, 4 ,five'; - delete require.cache[require.resolve('@dotcom-reliability-kit/opentelemetry/setup')]; - require('@dotcom-reliability-kit/opentelemetry/setup'); - - assert.strictEqual(opentelemetry.setup.mock.callCount(), 1); - assert.partialDeepStrictEqual(opentelemetry.setup.mock.calls[0].arguments, [ - { - views: { - httpClientDurationBuckets: [1, 2, 3, 4, NaN] - } - } - ]); - delete process.env.OPENTELEMETRY_VIEWS_HTTP_CLIENT_DURATION_BUCKETS; - }); - }); - - describe('when a sample rate is specified', () => { - it('calls OpenTelemetry with the given sample percentage as a number', () => { - delete process.env.OPENTELEMETRY_METRICS_ENDPOINT; - process.env.OPENTELEMETRY_TRACING_SAMPLE_PERCENTAGE = '50'; - delete require.cache[require.resolve('@dotcom-reliability-kit/opentelemetry/setup')]; - require('@dotcom-reliability-kit/opentelemetry/setup'); - - assert.strictEqual(opentelemetry.setup.mock.callCount(), 1); - assert.partialDeepStrictEqual(opentelemetry.setup.mock.calls[0].arguments, [ - { - logInternals: false, - tracing: { - authorizationHeader: 'MOCK_AUTH_HEADER', - endpoint: 'MOCK_TRACING_ENDPOINT', - samplePercentage: 50 - }, - views: {} - } - ]); - }); - }); - - describe('when a non-numeric sample rate is specified', () => { - it('calls OpenTelemetry with NaN as a percentage', () => { - delete process.env.OPENTELEMETRY_METRICS_ENDPOINT; - process.env.OPENTELEMETRY_TRACING_SAMPLE_PERCENTAGE = 'nope'; - delete require.cache[require.resolve('@dotcom-reliability-kit/opentelemetry/setup')]; - require('@dotcom-reliability-kit/opentelemetry/setup'); - - assert.strictEqual(opentelemetry.setup.mock.callCount(), 1); - assert.partialDeepStrictEqual(opentelemetry.setup.mock.calls[0].arguments, [ - { - logInternals: false, - tracing: { - authorizationHeader: 'MOCK_AUTH_HEADER', - endpoint: 'MOCK_TRACING_ENDPOINT', - samplePercentage: NaN - }, - views: {} - } - ]); - }); +describe('setup', () => { + beforeEach(async () => { + mock.property(process, 'env', { MOCK_ENV: 'true' }); + await import('../../setup.js'); }); - describe('when internal logs are enabled', () => { - it('calls OpenTelemetry with the logInternal option set to true', () => { - process.env.OPENTELEMETRY_LOG_INTERNALS = 'true'; - delete require.cache[require.resolve('@dotcom-reliability-kit/opentelemetry/setup')]; - require('@dotcom-reliability-kit/opentelemetry/setup'); - - assert.strictEqual(opentelemetry.setup.mock.callCount(), 1); - assert.partialDeepStrictEqual(opentelemetry.setup.mock.calls[0].arguments, [ - { - logInternals: true - } - ]); - }); + it('calls setupFromEnv with proces.env', () => { + assert.strictEqual(setupFromEnv.mock.callCount(), 1); + assert.deepStrictEqual(setupFromEnv.mock.calls[0].arguments, [{ MOCK_ENV: 'true' }]); }); });