From 6b103356b2ee956fe0fbd810734a86b7984a880b Mon Sep 17 00:00:00 2001 From: Jorge Azevedo Date: Fri, 24 Jul 2026 12:45:31 +0100 Subject: [PATCH 01/10] Add automatic telemetry export --- Production.dockerfile | 2 +- docker-compose.yaml | 10 + .../__snapshots__/renderingStack.test.ts.snap | 129 ++++++ dotcom-rendering/cdk/lib/renderingStack.ts | 49 +++ dotcom-rendering/package.json | 8 + .../src/server/instrumentation.js | 27 ++ .../webpack/webpack.config.server.js | 17 + pnpm-lock.yaml | 408 +++++++++++++++++- pnpm-workspace.yaml | 1 + 9 files changed, 641 insertions(+), 10 deletions(-) create mode 100644 dotcom-rendering/src/server/instrumentation.js diff --git a/Production.dockerfile b/Production.dockerfile index 405bd27dc9b..82c48d7087e 100644 --- a/Production.dockerfile +++ b/Production.dockerfile @@ -28,4 +28,4 @@ ENV NODE_ENV=production # Expose the port that the application listens on EXPOSE 9000 -CMD ["node", "/app/server.js"] +CMD ["node", "--require", "/app/instrumentation.js", "/app/server.js"] diff --git a/docker-compose.yaml b/docker-compose.yaml index 3fd54642df3..fa85d71077a 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -16,6 +16,10 @@ services: # Explicitly tell AWS SDK where to find credentials AWS_SHARED_CREDENTIALS_FILE: /.aws/credentials + + # OpenTelemetry exporter endpoint (Jaeger) + OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4318 + OTEL_SERVICE_NAME: tag-page-rendering ports: - '9000:9000' @@ -51,3 +55,9 @@ services: command: | curl "https://www.theguardian.com/tone/minutebyminute.json?dcr=true" --silent > data.json && \ curl -X POST http://localhost:9000/TagPage -d @data.json -H "Content-Type: application/json" + + jaeger: + image: jaegertracing/jaeger:2.6.0 + ports: + - '16686:16686' + - '4318:4318' diff --git a/dotcom-rendering/cdk/lib/__snapshots__/renderingStack.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/renderingStack.test.ts.snap index 8d22049aef3..a0596d4a5d7 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/renderingStack.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/renderingStack.test.ts.snap @@ -8,6 +8,7 @@ exports[`The RenderingCDKStack matches the snapshot 1`] = ` "GuAllowPolicy", "GuAllowPolicy", "GuAllowPolicy", + "GuAllowPolicy", "GuVpcParameter", "GuSubnetListParameter", "GuSubnetListParameter", @@ -190,6 +191,30 @@ exports[`The RenderingCDKStack matches the snapshot 1`] = ` }, "Type": "AWS::IAM::Policy", }, + "AllowPolicyXRay6A9BDA0E": { + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords", + ], + "Effect": "Allow", + "Resource": "*", + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "AllowPolicyXRay6A9BDA0E", + "Roles": [ + { + "Ref": "InstanceRoleArticlerendering0D4717E8", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, "AutoScalingGroupArticlerenderingASG8488C3F1": { "Properties": { "HealthCheckGracePeriod": 120, @@ -1645,6 +1670,7 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE "GuAllowPolicy", "GuAllowPolicy", "GuAllowPolicy", + "GuAllowPolicy", "GuVpcParameter", "GuSubnetListParameter", "GuSubnetListParameter", @@ -1845,6 +1871,33 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE }, "Type": "AWS::IAM::Policy", }, + "AllowPolicyXRay6A9BDA0E": { + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords", + ], + "Effect": "Allow", + "Resource": "*", + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "AllowPolicyXRay6A9BDA0E", + "Roles": [ + { + "Ref": "InstanceRoleTagpagerendering171BC2F9", + }, + { + "Ref": "EcsTaskDefinitionTaskRoleB7B6D8DD", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, "AutoScalingGroupTagpagerenderingASG7F7E0748": { "Properties": { "HealthCheckGracePeriod": 120, @@ -2165,6 +2218,14 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE "Name": "GU_STACK", "Value": "frontend", }, + { + "Name": "OTEL_EXPORTER_OTLP_ENDPOINT", + "Value": "http://localhost:4318", + }, + { + "Name": "OTEL_SERVICE_NAME", + "Value": "tag-page-rendering", + }, ], "Essential": true, "Image": { @@ -2252,6 +2313,32 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE "ReadonlyRootFilesystem": true, "VersionConsistency": "disabled", }, + { + "Command": [ + "--config=/etc/ecs/ecs-default-config.yaml", + ], + "Cpu": 256, + "Essential": true, + "Image": "public.ecr.aws/aws-observability/aws-otel-collector:v0.49.0", + "LogConfiguration": { + "LogDriver": "awslogs", + "Options": { + "awslogs-group": { + "Ref": "EcsTaskDefinitionadotcollectorLogGroup9B99E580", + }, + "awslogs-region": "eu-west-1", + "awslogs-stream-prefix": "tag-page-rendering-adot", + }, + }, + "MemoryReservation": 256, + "Name": "adot-collector", + "PortMappings": [ + { + "ContainerPort": 4318, + "Protocol": "tcp", + }, + ], + }, ], "Cpu": "1024", "ExecutionRoleArn": { @@ -2400,6 +2487,19 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE ], }, }, + { + "Action": [ + "logs:CreateLogStream", + "logs:PutLogEvents", + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "EcsTaskDefinitionadotcollectorLogGroup9B99E580", + "Arn", + ], + }, + }, ], "Version": "2012-10-17", }, @@ -2481,6 +2581,35 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE }, "Type": "AWS::IAM::Role", }, + "EcsTaskDefinitionadotcollectorLogGroup9B99E580": { + "DeletionPolicy": "Retain", + "Properties": { + "Tags": [ + { + "Key": "App", + "Value": "tag-page-rendering", + }, + { + "Key": "gu:cdk:version", + "Value": "TEST", + }, + { + "Key": "gu:repo", + "Value": "guardian/dotcom-rendering", + }, + { + "Key": "Stack", + "Value": "frontend", + }, + { + "Key": "Stage", + "Value": "CODE", + }, + ], + }, + "Type": "AWS::Logs::LogGroup", + "UpdateReplacePolicy": "Retain", + }, "GetDistributablePolicyTagpagerendering346128E3": { "Properties": { "PolicyDocument": { diff --git a/dotcom-rendering/cdk/lib/renderingStack.ts b/dotcom-rendering/cdk/lib/renderingStack.ts index f436001ba02..6d9bbe503d4 100644 --- a/dotcom-rendering/cdk/lib/renderingStack.ts +++ b/dotcom-rendering/cdk/lib/renderingStack.ts @@ -17,6 +17,9 @@ import { Metric, Unit } from 'aws-cdk-lib/aws-cloudwatch'; import { SnsAction } from 'aws-cdk-lib/aws-cloudwatch-actions'; import type { InstanceType } from 'aws-cdk-lib/aws-ec2'; import { Peer } from 'aws-cdk-lib/aws-ec2'; +import type { CfnService } from 'aws-cdk-lib/aws-ecs'; +import { ContainerImage, LogDrivers } from 'aws-cdk-lib/aws-ecs'; +import { ClusterSettings } from 'aws-cdk-lib/aws-ecs/mixins'; import { Subscription, SubscriptionProtocol, Topic } from 'aws-cdk-lib/aws-sns'; import { StringParameter } from 'aws-cdk-lib/aws-ssm'; import { getUserData } from './userData'; @@ -238,6 +241,13 @@ export class RenderingCDKStack extends CDKStack { actions: ['cloudwatch:*', 'logs:*'], resources: ['*'], }), + new GuAllowPolicy(this, 'AllowPolicyXRay', { + actions: [ + 'xray:PutTraceSegments', + 'xray:PutTelemetryRecords', + ], + resources: ['*'], + }), new GuAllowPolicy(this, 'AllowPolicyDescribeDecryptKms', { actions: ['kms:Decrypt', 'kms:DescribeKey'], resources: [ @@ -301,6 +311,8 @@ export class RenderingCDKStack extends CDKStack { GU_STAGE: stage, GU_APP: guApp, GU_STACK: guStack, + OTEL_EXPORTER_OTLP_ENDPOINT: 'http://localhost:4318', + OTEL_SERVICE_NAME: guApp, }; for (const [key, value] of Object.entries(ecsEnvVars)) { @@ -309,6 +321,43 @@ export class RenderingCDKStack extends CDKStack { value, ); } + // ADOT (AWS Distro for OpenTelemetry) sidecar to receive OTLP + // traces from the app and forward them to AWS X-Ray. + app.ecsService?.taskDefinition.addContainer('adot-collector', { + image: ContainerImage.fromRegistry( + 'public.ecr.aws/aws-observability/aws-otel-collector:v0.49.0', + ), + command: ['--config=/etc/ecs/ecs-default-config.yaml'], + logging: LogDrivers.awsLogs({ + streamPrefix: `${guApp}-adot`, + }), + portMappings: [{ containerPort: 4318 }], + cpu: 256, + memoryReservationMiB: 256, + }); + + // TODO make these changes at the pattern level in GuCDK + if (app.ecsService) { + app.ecsService.cluster.with( + new ClusterSettings([ + { name: 'containerInsights', value: 'enhanced' }, + ]), + ); + + const cfnService = app.ecsService.node + .defaultChild as CfnService; + cfnService.addPropertyOverride('Monitoring', { + MetricConfigurations: [ + { + MetricNames: [ + 'CPUUtilization', + 'MemoryUtilization', + ], + ResolutionSeconds: 20, + }, + ], + }); + } } /** diff --git a/dotcom-rendering/package.json b/dotcom-rendering/package.json index 0efffba19ba..585ae2daea4 100644 --- a/dotcom-rendering/package.json +++ b/dotcom-rendering/package.json @@ -46,6 +46,13 @@ "@guardian/source-development-kitchen": "29.1.0", "@guardian/support-dotcom-components": "11.0.0", "@guardian/tsconfig": "catalog:", + "@opentelemetry/exporter-trace-otlp-http": "0.220.0", + "@opentelemetry/instrumentation": "0.220.0", + "@opentelemetry/instrumentation-express": "0.68.0", + "@opentelemetry/instrumentation-http": "0.220.0", + "@opentelemetry/resources": "2.9.0", + "@opentelemetry/sdk-trace-base": "2.9.0", + "@opentelemetry/sdk-trace-node": "2.9.0", "@playwright/test": "1.60.0", "@sentry/browser": "10.65.0", "@storybook/addon-a11y": "10.5.7", @@ -129,6 +136,7 @@ "lz-string": "1.5.0", "mockdate": "3.0.5", "node-fetch": "3.3.2", + "opentelemetry-webpack-plugin-node": "4.5.0", "postcss-styled-syntax": "0.7.1", "preact": "10.15.1", "preact-render-to-string": "6.0.2", diff --git a/dotcom-rendering/src/server/instrumentation.js b/dotcom-rendering/src/server/instrumentation.js new file mode 100644 index 00000000000..85717496a2e --- /dev/null +++ b/dotcom-rendering/src/server/instrumentation.js @@ -0,0 +1,27 @@ +const { + OTLPTraceExporter, +} = require('@opentelemetry/exporter-trace-otlp-http'); +const { registerInstrumentations } = require('@opentelemetry/instrumentation'); +const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http'); +const { resourceFromAttributes } = require('@opentelemetry/resources'); +const { BatchSpanProcessor } = require('@opentelemetry/sdk-trace-base'); +const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node'); + +const provider = new NodeTracerProvider({ + resource: resourceFromAttributes({ + 'service.name': process.env.OTEL_SERVICE_NAME || 'dotcom-rendering', + }), + spanProcessors: [new BatchSpanProcessor(new OTLPTraceExporter())], +}); + +provider.register(); + +registerInstrumentations({ + instrumentations: [new HttpInstrumentation()], +}); + +process.on('SIGTERM', () => { + provider.shutdown().catch((err) => { + console.error('OpenTelemetry shutdown failed', err); + }); +}); diff --git a/dotcom-rendering/webpack/webpack.config.server.js b/dotcom-rendering/webpack/webpack.config.server.js index bb3282477b8..db2272866a6 100644 --- a/dotcom-rendering/webpack/webpack.config.server.js +++ b/dotcom-rendering/webpack/webpack.config.server.js @@ -1,5 +1,11 @@ // @ts-check const path = require('path'); +const { + ExpressInstrumentation, +} = require('@opentelemetry/instrumentation-express'); +const { + OpenTelemetryWebpackPlugin, +} = require('opentelemetry-webpack-plugin-node'); const nodeExternals = require('webpack-node-externals'); const swcConfig = require('./.swcrc.json'); const { svgr } = require('./svg.cjs'); @@ -27,6 +33,7 @@ const swcLoader = [ module.exports = { entry: { server: './src/server/server.ts', + instrumentation: './src/server/instrumentation.js', }, output: { filename: `[name].js`, @@ -89,4 +96,14 @@ module.exports = { }, ], }, + plugins: [ + // Patches express at build time so instrumentation works in the bundle + ...(DEV + ? [] + : [ + new OpenTelemetryWebpackPlugin({ + instrumentations: [new ExpressInstrumentation()], + }), + ]), + ], }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cf88e9e59dd..b62b7420e1b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -219,13 +219,13 @@ importers: version: link:../config '@sveltejs/adapter-auto': specifier: 7.0.1 - version: 7.0.1(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.10(@typescript-eslint/types@8.59.2))(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)))(svelte@5.56.10(@typescript-eslint/types@8.59.2))(typescript@6.0.3)(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2))) + version: 7.0.1(@sveltejs/kit@2.70.3(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.10(@typescript-eslint/types@8.59.2))(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)))(svelte@5.56.10(@typescript-eslint/types@8.59.2))(typescript@6.0.3)(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2))) '@sveltejs/adapter-static': specifier: 3.0.10 - version: 3.0.10(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.10(@typescript-eslint/types@8.59.2))(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)))(svelte@5.56.10(@typescript-eslint/types@8.59.2))(typescript@6.0.3)(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2))) + version: 3.0.10(@sveltejs/kit@2.70.3(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.10(@typescript-eslint/types@8.59.2))(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)))(svelte@5.56.10(@typescript-eslint/types@8.59.2))(typescript@6.0.3)(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2))) '@sveltejs/kit': specifier: 2.70.3 - version: 2.70.3(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.10(@typescript-eslint/types@8.59.2))(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)))(svelte@5.56.10(@typescript-eslint/types@8.59.2))(typescript@6.0.3)(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)) + version: 2.70.3(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.10(@typescript-eslint/types@8.59.2))(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)))(svelte@5.56.10(@typescript-eslint/types@8.59.2))(typescript@6.0.3)(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)) '@sveltejs/vite-plugin-svelte': specifier: 7.3.0 version: 7.3.0(svelte@5.56.10(@typescript-eslint/types@8.59.2))(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)) @@ -368,6 +368,27 @@ importers: '@guardian/tsconfig': specifier: 'catalog:' version: 1.0.1 + '@opentelemetry/exporter-trace-otlp-http': + specifier: 0.220.0 + version: 0.220.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': + specifier: 0.220.0 + version: 0.220.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation-express': + specifier: 0.68.0 + version: 0.68.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation-http': + specifier: 0.220.0 + version: 0.220.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': + specifier: 2.9.0 + version: 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': + specifier: 2.9.0 + version: 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-node': + specifier: 2.9.0 + version: 2.9.0(@opentelemetry/api@1.9.1) '@playwright/test': specifier: 1.60.0 version: 1.60.0 @@ -617,6 +638,9 @@ importers: node-fetch: specifier: 3.3.2 version: 3.3.2 + opentelemetry-webpack-plugin-node: + specifier: 4.5.0 + version: 4.5.0(@opentelemetry/api@1.9.1)(@swc/core@1.15.46)(clean-css@5.3.3)(esbuild@0.28.2)(html-minifier-terser@7.2.0)(postcss@8.5.25)(webpack-cli@7.2.2) postcss-styled-syntax: specifier: 0.7.1 version: 0.7.1(postcss@8.5.25) @@ -3201,6 +3225,118 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@opentelemetry/api-logs@0.200.0': + resolution: {integrity: sha512-IKJBQxh91qJ+3ssRly5hYEJ8NDHu9oY/B1PXVSCWf7zytmYO9RNLB0Ox9XQ/fJ8m6gY6Q6NtBWlmXfaXt5Uc4Q==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/api-logs@0.220.0': + resolution: {integrity: sha512-CmVa4ImJ+ynfrPMNaAXHET6Bhb44SwzmfyVJFq9ni2jgXJR/l7C6gfVFddNmHP+ZOkP9cf4f9DBe68qVLTHc9w==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/api@1.9.1': + resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/context-async-hooks@2.9.0': + resolution: {integrity: sha512-OQ0vzvbZBiUhjqLnUaoNfYmP8553Crr3aggB4y0ZUi815mZ7idpdJXQmoKdeBKJelYttoBlLSSHubmyw3wvX4w==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/core@2.8.0': + resolution: {integrity: sha512-hd1Lfh8p545nNz+jq1Ejfz+Mn1hyLuxYn1YzTfFNrxr8urEWMNQLPf1Th8kjOH+HxwawCrtgBp8JpBUR4ZSgww==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/core@2.9.0': + resolution: {integrity: sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/exporter-trace-otlp-http@0.220.0': + resolution: {integrity: sha512-/+ExB3lRkf+erv4PnoywyL7RHKITidxtUpUTS55k7OQ0dB42S7gEF1gry7swb9MSm1hYLUhJg4QQh9W8SpwwqA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-express@0.68.0': + resolution: {integrity: sha512-3ffjIQUFNVP94lLLHlBEgNaomCoP0BLH36Gxmkk3/WKX+1530QKVAnZTleYdM3RVU2EeQFtWSmFMAyCLglqO3w==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation-http@0.220.0': + resolution: {integrity: sha512-Szt4dO2Boz2CDr38DaSw/lnqwhwKl+IAdgNGEGgSm2Anb+fwPtIAGmIwkhsLLN69QQZQE96JxjMKYY4rlRkYKw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation@0.200.0': + resolution: {integrity: sha512-pmPlzfJd+vvgaZd/reMsC8RWgTXn2WY1OWT5RT42m3aOn5532TozwXNDhg1vzqJ+jnvmkREcdLr27ebJEQt0Jg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation@0.220.0': + resolution: {integrity: sha512-xQx3E2WxP1mDvKzxLxX+CTCtNLa560YJZ3087qYHerl2YmiKpv7AH+dAy7vmx+eVrZ5BwhfWUAVoKOoxCNHcpw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-exporter-base@0.220.0': + resolution: {integrity: sha512-CXYo8UD5Mn9YbgebO2EL4wejtA+gxLmLiu6HCk2KH2BR7XhFN6/6p1UlCb23DYCjeYkndevLHuejCCN1yx4+OQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-transformer@0.220.0': + resolution: {integrity: sha512-lXGrv7KXZ0gNH9SVNUaa6vv6phVYGvJxfXAlMbzbakiXru75f5MZl8Z7oqiMMQD77riVHJCFlQvbZs/VVN2/4A==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/resources@2.9.0': + resolution: {integrity: sha512-jyA5MBLQ+Dkl3+JsZkUoUvL7yHvU64kLsvpXKarWm6347Sl1t1bXFTFykUePNpT5WH5pm9a2Qtt03iIYQhZ1Fg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-logs@0.220.0': + resolution: {integrity: sha512-WywcTkQtv2iNmt+6y5Kcd4rzvx9bLVsBa2Nwcmg01IUaBTkTow3W4d9KE5vNBpEDtb9tp21WcRBY/lANRrApYA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.4.0 <1.10.0' + + '@opentelemetry/sdk-metrics@2.9.0': + resolution: {integrity: sha512-Xx8RGS4H5XEBl01WuCreMIpiah9cCXMbSkeuIePPdD2cUpq/vUzYmj8E/MK1OsbOc93FuAD4jfn2WOacKwLn7Q==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.9.0 <1.10.0' + + '@opentelemetry/sdk-trace-base@2.9.0': + resolution: {integrity: sha512-cp9zmTl62R8PJrpvFcmc8N2JQU/xfa0S+61q511Nji+QxCfZ8Ifvg7H27G8cANe4crg4RTrWsVvanHiXjSp6ag==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-trace-node@2.9.0': + resolution: {integrity: sha512-ec9a7ps37huy5itYk0MalaZdSLlM6AXWp/FhtEjgMpp5leEGojBDvAl/UWttQnkMZOvFHKzRESn8TD3yKTF5nQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/sdk-trace@2.9.0': + resolution: {integrity: sha512-sGA19HvtrrSKYsseHphluH6j3p6Xa3fqc7c7y8f/7mYWejc1lyDFcpSdD1kYa50HCLUeEo4zA5bW0pniaPszuw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/semantic-conventions@1.43.0': + resolution: {integrity: sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==} + engines: {node: '>=14'} + '@oxc-parser/binding-android-arm-eabi@0.127.0': resolution: {integrity: sha512-0LC7ye4hvqbIKxAzThzvswgHLFu2AURKzYLeSVvLdu2TBOYWQDmHnTqPLeA597BcUCxiLqLsS4CJ5uoI5WYWCQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -4745,6 +4881,9 @@ packages: '@types/serve-static@2.2.0': resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} + '@types/shimmer@1.2.0': + resolution: {integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==} + '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} @@ -5072,6 +5211,11 @@ packages: acorn-globals@7.0.1: resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} + acorn-import-attributes@1.9.5: + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -5578,6 +5722,9 @@ packages: cjs-module-lexer@1.4.3: resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + cjs-module-lexer@2.2.1: + resolution: {integrity: sha512-Ca8swihM+/4yKecYHY52kgJd300hi2lADU/a1RxNTRe+RJ9jvqQlESpbz9DnG9mowez8qwXHB8qYdIUw9e+F5Q==} + clean-css@5.3.3: resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} engines: {node: '>= 10.0'} @@ -6627,6 +6774,9 @@ packages: resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} engines: {node: '>=12.20.0'} + forwarded-parse@2.1.2: + resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==} + forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -7032,6 +7182,13 @@ packages: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} + import-in-the-middle@1.15.0: + resolution: {integrity: sha512-bpQy+CrsRmYmoPMAE/0G33iwRqwW4ouqdRg8jgbH3aKuCtOc8lxgmYXg2dMM92CRiGP660EtBcymH/eVUpCSaA==} + + import-in-the-middle@3.3.3: + resolution: {integrity: sha512-AiohS3H80sXO6owEltjGX+glb7qXaDhBoJb9XcQVH4UI207xu/bDLUcadVKp7Qe576reg9yr/PXZjV5qx8gfbA==} + engines: {node: '>=18'} + import-local@3.2.0: resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} engines: {node: '>=8'} @@ -7335,6 +7492,9 @@ packages: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} + javascript-stringify@2.1.0: + resolution: {integrity: sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==} + jest-changed-files@29.7.0: resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -7929,6 +8089,9 @@ packages: mockdate@3.0.5: resolution: {integrity: sha512-iniQP4rj1FhBdBYS/+eQv7j1tadJ9lJtdzgOpvsOHng/GbcDh2Fhdeq+ZRldrPYdXvCyfFUmFeEwEGXZB5I/AQ==} + module-details-from-path@1.0.4: + resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} + mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -8112,6 +8275,12 @@ packages: resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} hasBin: true + opentelemetry-node-bundler-plugin-utils@0.8.0: + resolution: {integrity: sha512-GyZS++IonGwAJ37kQZPTcEDai+cc6iWFrERFb7gaZwZyXscTh7N2Hy8uA/UYsMGQPNBydbLF6k/OUKof63rGYg==} + + opentelemetry-webpack-plugin-node@4.5.0: + resolution: {integrity: sha512-2wGh1xUCkw2WkwISveejXwyy0xufZkJPmNKTkFzoy95BTkWU55fCsA69lC+yclwoPpxfm0YQFpsyxABYfZgU0A==} + optionator@0.9.3: resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} engines: {node: '>= 0.8.0'} @@ -8629,6 +8798,14 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + require-in-the-middle@7.5.2: + resolution: {integrity: sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==} + engines: {node: '>=8.6.0'} + + require-in-the-middle@8.0.1: + resolution: {integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==} + engines: {node: '>=9.3.0 || >=8.10.0 <9.0.0'} + requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} @@ -8860,6 +9037,9 @@ packages: resolution: {integrity: sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==} engines: {node: '>= 0.4'} + shimmer@1.2.1: + resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} + side-channel-list@1.0.0: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} engines: {node: '>= 0.4'} @@ -13159,6 +13339,138 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.15.0 + '@opentelemetry/api-logs@0.200.0': + dependencies: + '@opentelemetry/api': 1.9.1 + + '@opentelemetry/api-logs@0.220.0': + dependencies: + '@opentelemetry/api': 1.9.1 + + '@opentelemetry/api@1.9.1': {} + + '@opentelemetry/context-async-hooks@2.9.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + + '@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/semantic-conventions': 1.43.0 + + '@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/semantic-conventions': 1.43.0 + + '@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.220.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.220.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace': 2.9.0(@opentelemetry/api@1.9.1) + + '@opentelemetry/instrumentation-express@0.68.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.43.0 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation-http@0.220.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.43.0 + forwarded-parse: 2.1.2 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation@0.200.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/api-logs': 0.200.0 + '@types/shimmer': 1.2.0 + import-in-the-middle: 1.15.0 + require-in-the-middle: 7.5.2 + shimmer: 1.2.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/api-logs': 0.220.0 + import-in-the-middle: 3.3.3 + require-in-the-middle: 8.0.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/otlp-exporter-base@0.220.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.220.0(@opentelemetry/api@1.9.1) + + '@opentelemetry/otlp-transformer@0.220.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/api-logs': 0.220.0 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-logs': 0.220.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-metrics': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace': 2.9.0(@opentelemetry/api@1.9.1) + + '@opentelemetry/resources@2.9.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.43.0 + + '@opentelemetry/sdk-logs@0.220.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/api-logs': 0.220.0 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.43.0 + + '@opentelemetry/sdk-metrics@2.9.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) + + '@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.43.0 + + '@opentelemetry/sdk-trace-node@2.9.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/context-async-hooks': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.1) + + '@opentelemetry/sdk-trace@2.9.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.43.0 + + '@opentelemetry/semantic-conventions@1.43.0': {} + '@oxc-parser/binding-android-arm-eabi@0.127.0': optional: true @@ -14181,7 +14493,7 @@ snapshots: flat-cache: 3.2.0 micromatch: 4.0.8 react-docgen-typescript: 2.4.0(typescript@6.0.3) - tslib: 2.6.2 + tslib: 2.8.1 typescript: 6.0.3 webpack: 5.109.2(@swc/core@1.15.46)(clean-css@5.3.3)(esbuild@0.28.2)(html-minifier-terser@7.2.0)(postcss@8.5.25)(webpack-cli@7.2.2) transitivePeerDependencies: @@ -14255,15 +14567,15 @@ snapshots: dependencies: acorn: 8.18.0 - '@sveltejs/adapter-auto@7.0.1(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.10(@typescript-eslint/types@8.59.2))(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)))(svelte@5.56.10(@typescript-eslint/types@8.59.2))(typescript@6.0.3)(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)))': + '@sveltejs/adapter-auto@7.0.1(@sveltejs/kit@2.70.3(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.10(@typescript-eslint/types@8.59.2))(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)))(svelte@5.56.10(@typescript-eslint/types@8.59.2))(typescript@6.0.3)(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)))': dependencies: - '@sveltejs/kit': 2.70.3(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.10(@typescript-eslint/types@8.59.2))(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)))(svelte@5.56.10(@typescript-eslint/types@8.59.2))(typescript@6.0.3)(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)) + '@sveltejs/kit': 2.70.3(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.10(@typescript-eslint/types@8.59.2))(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)))(svelte@5.56.10(@typescript-eslint/types@8.59.2))(typescript@6.0.3)(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)) - '@sveltejs/adapter-static@3.0.10(@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.10(@typescript-eslint/types@8.59.2))(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)))(svelte@5.56.10(@typescript-eslint/types@8.59.2))(typescript@6.0.3)(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)))': + '@sveltejs/adapter-static@3.0.10(@sveltejs/kit@2.70.3(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.10(@typescript-eslint/types@8.59.2))(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)))(svelte@5.56.10(@typescript-eslint/types@8.59.2))(typescript@6.0.3)(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)))': dependencies: - '@sveltejs/kit': 2.70.3(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.10(@typescript-eslint/types@8.59.2))(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)))(svelte@5.56.10(@typescript-eslint/types@8.59.2))(typescript@6.0.3)(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)) + '@sveltejs/kit': 2.70.3(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.10(@typescript-eslint/types@8.59.2))(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)))(svelte@5.56.10(@typescript-eslint/types@8.59.2))(typescript@6.0.3)(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)) - '@sveltejs/kit@2.70.3(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.10(@typescript-eslint/types@8.59.2))(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)))(svelte@5.56.10(@typescript-eslint/types@8.59.2))(typescript@6.0.3)(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2))': + '@sveltejs/kit@2.70.3(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.56.10(@typescript-eslint/types@8.59.2))(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2)))(svelte@5.56.10(@typescript-eslint/types@8.59.2))(typescript@6.0.3)(vite@6.4.3(@types/node@24.12.4)(terser@5.49.2))': dependencies: '@standard-schema/spec': 1.1.0 '@sveltejs/acorn-typescript': 1.0.13(acorn@8.18.0) @@ -14281,6 +14593,7 @@ snapshots: svelte: 5.56.10(@typescript-eslint/types@8.59.2) vite: 6.4.3(@types/node@24.12.4)(terser@5.49.2)(tsx@4.6.2) optionalDependencies: + '@opentelemetry/api': 1.9.1 typescript: 6.0.3 '@sveltejs/load-config@0.2.3': {} @@ -14779,6 +15092,8 @@ snapshots: '@types/http-errors': 2.0.5 '@types/node': 24.12.4 + '@types/shimmer@1.2.0': {} + '@types/stack-utils@2.0.3': {} '@types/tough-cookie@4.0.2': {} @@ -15209,6 +15524,10 @@ snapshots: acorn: 8.18.0 acorn-walk: 8.3.5 + acorn-import-attributes@1.9.5(acorn@8.18.0): + dependencies: + acorn: 8.18.0 + acorn-jsx@5.3.2(acorn@8.16.0): dependencies: acorn: 8.16.0 @@ -15713,6 +16032,8 @@ snapshots: cjs-module-lexer@1.4.3: {} + cjs-module-lexer@2.2.1: {} + clean-css@5.3.3: dependencies: source-map: 0.6.1 @@ -17031,6 +17352,8 @@ snapshots: dependencies: fetch-blob: 3.2.0 + forwarded-parse@2.1.2: {} + forwarded@0.2.0: {} fresh@2.0.0: {} @@ -17507,6 +17830,19 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 + import-in-the-middle@1.15.0: + dependencies: + acorn: 8.18.0 + acorn-import-attributes: 1.9.5(acorn@8.18.0) + cjs-module-lexer: 1.4.3 + module-details-from-path: 1.0.4 + + import-in-the-middle@3.3.3: + dependencies: + cjs-module-lexer: 2.2.1 + es-module-lexer: 2.3.1 + module-details-from-path: 1.0.4 + import-local@3.2.0: dependencies: pkg-dir: 4.2.0 @@ -17791,6 +18127,8 @@ snapshots: has-symbols: 1.1.0 set-function-name: 2.0.2 + javascript-stringify@2.1.0: {} + jest-changed-files@29.7.0: dependencies: execa: 5.1.1 @@ -18667,6 +19005,8 @@ snapshots: mockdate@3.0.5: {} + module-details-from-path@1.0.4: {} + mri@1.2.0: {} mrmime@2.0.1: {} @@ -18838,6 +19178,39 @@ snapshots: opener@1.5.2: {} + opentelemetry-node-bundler-plugin-utils@0.8.0(@opentelemetry/api@1.9.1): + dependencies: + '@opentelemetry/instrumentation': 0.200.0(@opentelemetry/api@1.9.1) + javascript-stringify: 2.1.0 + semver: 7.8.5 + tslib: 2.8.1 + transitivePeerDependencies: + - '@opentelemetry/api' + - supports-color + + opentelemetry-webpack-plugin-node@4.5.0(@opentelemetry/api@1.9.1)(@swc/core@1.15.46)(clean-css@5.3.3)(esbuild@0.28.2)(html-minifier-terser@7.2.0)(postcss@8.5.25)(webpack-cli@7.2.2): + dependencies: + '@opentelemetry/instrumentation': 0.200.0(@opentelemetry/api@1.9.1) + opentelemetry-node-bundler-plugin-utils: 0.8.0(@opentelemetry/api@1.9.1) + tslib: 2.8.1 + webpack: 5.109.2(@swc/core@1.15.46)(clean-css@5.3.3)(esbuild@0.28.2)(html-minifier-terser@7.2.0)(postcss@8.5.25)(webpack-cli@7.2.2) + transitivePeerDependencies: + - '@minify-html/node' + - '@opentelemetry/api' + - '@swc/core' + - '@swc/css' + - '@swc/html' + - clean-css + - cssnano + - csso + - esbuild + - html-minifier-terser + - lightningcss + - postcss + - supports-color + - uglify-js + - webpack-cli + optionator@0.9.3: dependencies: '@aashutoshrathi/word-wrap': 1.2.6 @@ -19425,6 +19798,21 @@ snapshots: require-from-string@2.0.2: {} + require-in-the-middle@7.5.2: + dependencies: + debug: 4.4.3 + module-details-from-path: 1.0.4 + resolve: 1.22.12 + transitivePeerDependencies: + - supports-color + + require-in-the-middle@8.0.1: + dependencies: + debug: 4.4.3 + module-details-from-path: 1.0.4 + transitivePeerDependencies: + - supports-color + requires-port@1.0.0: {} resolve-alpn@1.2.1: {} @@ -19732,6 +20120,8 @@ snapshots: shell-quote@1.9.0: {} + shimmer@1.2.1: {} + side-channel-list@1.0.0: dependencies: es-errors: 1.3.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 015cdc8597e..11a4a71230c 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,6 +6,7 @@ allowBuilds: '@swc/core': true esbuild: true nice-napi: true + protobufjs: true unrs-resolver: false # This postinstall is just a workaround for an npm issue so we don't need it https://github.com/unrs/unrs-resolver/pull/66 blockExoticSubdeps: true catalog: From 7e5c4d5ea09d27f1e9917f23ef20ba7cd91f8646 Mon Sep 17 00:00:00 2001 From: Jorge Azevedo Date: Sat, 15 Aug 2026 00:44:36 +0100 Subject: [PATCH 02/10] Remove auto instrumentation to avoid webpack plugin --- .../__snapshots__/renderingStack.test.ts.snap | 81 ++++------ dotcom-rendering/cdk/lib/renderingStack.ts | 19 ++- dotcom-rendering/package.json | 3 +- .../src/server/instrumentation.js | 2 + dotcom-rendering/src/server/server.prod.ts | 22 ++- .../webpack/webpack.config.server.js | 16 -- pnpm-lock.yaml | 145 +----------------- pnpm-workspace.yaml | 1 - 8 files changed, 66 insertions(+), 223 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/renderingStack.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/renderingStack.test.ts.snap index a0596d4a5d7..f9dacfc8578 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/renderingStack.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/renderingStack.test.ts.snap @@ -8,7 +8,6 @@ exports[`The RenderingCDKStack matches the snapshot 1`] = ` "GuAllowPolicy", "GuAllowPolicy", "GuAllowPolicy", - "GuAllowPolicy", "GuVpcParameter", "GuSubnetListParameter", "GuSubnetListParameter", @@ -191,30 +190,6 @@ exports[`The RenderingCDKStack matches the snapshot 1`] = ` }, "Type": "AWS::IAM::Policy", }, - "AllowPolicyXRay6A9BDA0E": { - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "xray:PutTraceSegments", - "xray:PutTelemetryRecords", - ], - "Effect": "Allow", - "Resource": "*", - }, - ], - "Version": "2012-10-17", - }, - "PolicyName": "AllowPolicyXRay6A9BDA0E", - "Roles": [ - { - "Ref": "InstanceRoleArticlerendering0D4717E8", - }, - ], - }, - "Type": "AWS::IAM::Policy", - }, "AutoScalingGroupArticlerenderingASG8488C3F1": { "Properties": { "HealthCheckGracePeriod": 120, @@ -1670,7 +1645,6 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE "GuAllowPolicy", "GuAllowPolicy", "GuAllowPolicy", - "GuAllowPolicy", "GuVpcParameter", "GuSubnetListParameter", "GuSubnetListParameter", @@ -1871,33 +1845,6 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE }, "Type": "AWS::IAM::Policy", }, - "AllowPolicyXRay6A9BDA0E": { - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "xray:PutTraceSegments", - "xray:PutTelemetryRecords", - ], - "Effect": "Allow", - "Resource": "*", - }, - ], - "Version": "2012-10-17", - }, - "PolicyName": "AllowPolicyXRay6A9BDA0E", - "Roles": [ - { - "Ref": "InstanceRoleTagpagerendering171BC2F9", - }, - { - "Ref": "EcsTaskDefinitionTaskRoleB7B6D8DD", - }, - ], - }, - "Type": "AWS::IAM::Policy", - }, "AutoScalingGroupTagpagerenderingASG7F7E0748": { "Properties": { "HealthCheckGracePeriod": 120, @@ -2033,6 +1980,7 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE }, "EcsService81FC6EF6": { "DependsOn": [ + "EcsTaskDefinitionTaskRoleDefaultPolicy1FD2F057", "EcsTaskDefinitionTaskRoleB7B6D8DD", "ListenerTagpagerenderingDeterministicRouteToEc2Rule1C5C95A4", "ListenerTagpagerenderingDeterministicRouteToEcsRuleAB05C756", @@ -2128,6 +2076,7 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE }, "EcsServiceTaskCountTarget02FCCE22": { "DependsOn": [ + "EcsTaskDefinitionTaskRoleDefaultPolicy1FD2F057", "EcsTaskDefinitionTaskRoleB7B6D8DD", ], "Properties": { @@ -2318,7 +2267,7 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE "--config=/etc/ecs/ecs-default-config.yaml", ], "Cpu": 256, - "Essential": true, + "Essential": false, "Image": "public.ecr.aws/aws-observability/aws-otel-collector:v0.49.0", "LogConfiguration": { "LogDriver": "awslogs", @@ -2581,6 +2530,30 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE }, "Type": "AWS::IAM::Role", }, + "EcsTaskDefinitionTaskRoleDefaultPolicy1FD2F057": { + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords", + ], + "Effect": "Allow", + "Resource": "*", + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "EcsTaskDefinitionTaskRoleDefaultPolicy1FD2F057", + "Roles": [ + { + "Ref": "EcsTaskDefinitionTaskRoleB7B6D8DD", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, "EcsTaskDefinitionadotcollectorLogGroup9B99E580": { "DeletionPolicy": "Retain", "Properties": { diff --git a/dotcom-rendering/cdk/lib/renderingStack.ts b/dotcom-rendering/cdk/lib/renderingStack.ts index 6d9bbe503d4..e60bc4340b0 100644 --- a/dotcom-rendering/cdk/lib/renderingStack.ts +++ b/dotcom-rendering/cdk/lib/renderingStack.ts @@ -20,6 +20,7 @@ import { Peer } from 'aws-cdk-lib/aws-ec2'; import type { CfnService } from 'aws-cdk-lib/aws-ecs'; import { ContainerImage, LogDrivers } from 'aws-cdk-lib/aws-ecs'; import { ClusterSettings } from 'aws-cdk-lib/aws-ecs/mixins'; +import { PolicyStatement } from 'aws-cdk-lib/aws-iam'; import { Subscription, SubscriptionProtocol, Topic } from 'aws-cdk-lib/aws-sns'; import { StringParameter } from 'aws-cdk-lib/aws-ssm'; import { getUserData } from './userData'; @@ -241,13 +242,6 @@ export class RenderingCDKStack extends CDKStack { actions: ['cloudwatch:*', 'logs:*'], resources: ['*'], }), - new GuAllowPolicy(this, 'AllowPolicyXRay', { - actions: [ - 'xray:PutTraceSegments', - 'xray:PutTelemetryRecords', - ], - resources: ['*'], - }), new GuAllowPolicy(this, 'AllowPolicyDescribeDecryptKms', { actions: ['kms:Decrypt', 'kms:DescribeKey'], resources: [ @@ -323,11 +317,22 @@ export class RenderingCDKStack extends CDKStack { } // ADOT (AWS Distro for OpenTelemetry) sidecar to receive OTLP // traces from the app and forward them to AWS X-Ray. + app.ecsService?.taskDefinition.addToTaskRolePolicy( + new PolicyStatement({ + actions: [ + 'xray:PutTraceSegments', + 'xray:PutTelemetryRecords', + ], + resources: ['*'], + }), + ); app.ecsService?.taskDefinition.addContainer('adot-collector', { image: ContainerImage.fromRegistry( 'public.ecr.aws/aws-observability/aws-otel-collector:v0.49.0', ), command: ['--config=/etc/ecs/ecs-default-config.yaml'], + // Losing traces must never take the app down + essential: false, logging: LogDrivers.awsLogs({ streamPrefix: `${guApp}-adot`, }), diff --git a/dotcom-rendering/package.json b/dotcom-rendering/package.json index 585ae2daea4..59f8110d044 100644 --- a/dotcom-rendering/package.json +++ b/dotcom-rendering/package.json @@ -46,9 +46,9 @@ "@guardian/source-development-kitchen": "29.1.0", "@guardian/support-dotcom-components": "11.0.0", "@guardian/tsconfig": "catalog:", + "@opentelemetry/api": "1.9.1", "@opentelemetry/exporter-trace-otlp-http": "0.220.0", "@opentelemetry/instrumentation": "0.220.0", - "@opentelemetry/instrumentation-express": "0.68.0", "@opentelemetry/instrumentation-http": "0.220.0", "@opentelemetry/resources": "2.9.0", "@opentelemetry/sdk-trace-base": "2.9.0", @@ -136,7 +136,6 @@ "lz-string": "1.5.0", "mockdate": "3.0.5", "node-fetch": "3.3.2", - "opentelemetry-webpack-plugin-node": "4.5.0", "postcss-styled-syntax": "0.7.1", "preact": "10.15.1", "preact-render-to-string": "6.0.2", diff --git a/dotcom-rendering/src/server/instrumentation.js b/dotcom-rendering/src/server/instrumentation.js index 85717496a2e..0b350f5c0ab 100644 --- a/dotcom-rendering/src/server/instrumentation.js +++ b/dotcom-rendering/src/server/instrumentation.js @@ -7,6 +7,8 @@ const { resourceFromAttributes } = require('@opentelemetry/resources'); const { BatchSpanProcessor } = require('@opentelemetry/sdk-trace-base'); const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node'); +// NodeTracerProvider reads OTEL_EXPORTER_OTLP_ENDPOINT from the environment, +// but not OTEL_SERVICE_NAME, so the resource has to be set explicitly. const provider = new NodeTracerProvider({ resource: resourceFromAttributes({ 'service.name': process.env.OTEL_SERVICE_NAME || 'dotcom-rendering', diff --git a/dotcom-rendering/src/server/server.prod.ts b/dotcom-rendering/src/server/server.prod.ts index 8f1bcdea678..f8c4f091a42 100644 --- a/dotcom-rendering/src/server/server.prod.ts +++ b/dotcom-rendering/src/server/server.prod.ts @@ -1,3 +1,4 @@ +import { context, trace } from '@opentelemetry/api'; import compression from 'compression'; import type { ErrorRequestHandler, Request, Response } from 'express'; import express from 'express'; @@ -32,12 +33,23 @@ import { logger } from './lib/logging'; import { requestLoggerMiddleware } from './lib/logging-middleware'; import { recordError } from './lib/logging-store'; +// Hand-rolled because @opentelemetry/instrumentation-express patches express at +// require time, which webpack has already inlined by the time the server runs. +const tracer = trace.getTracer('dotcom-rendering'); + export const prodServer = (): void => { logger.info('dotcom-rendering is GO.'); const app = express(); - app.use(express.json({ limit: '50mb' })); + const parseJson = express.json({ limit: '50mb' }); + app.use((req, res, next) => { + const span = tracer.startSpan('json parsing'); + parseJson(req, res, (error?: unknown) => { + span.end(); + next(error); + }); + }); app.use(requestLoggerMiddleware); app.use(compression()); app.use(responseHeaderMiddleware); @@ -53,6 +65,14 @@ export const prodServer = (): void => { app.use('/assets', express.static(__dirname)); } + // Made the active span so that spans added inside handlers nest beneath it + app.use((req, res, next) => { + const span = tracer.startSpan('request handler'); + // `close` rather than `finish` so the span ends even if the client disconnects + res.on('close', () => span.end()); + context.with(trace.setSpan(context.active(), span), next); + }); + app.post('/Article', handleArticle); app.post('/Interactive', handleInteractive); app.post('/Blocks', handleBlocks); diff --git a/dotcom-rendering/webpack/webpack.config.server.js b/dotcom-rendering/webpack/webpack.config.server.js index db2272866a6..579bef6c95b 100644 --- a/dotcom-rendering/webpack/webpack.config.server.js +++ b/dotcom-rendering/webpack/webpack.config.server.js @@ -1,11 +1,5 @@ // @ts-check const path = require('path'); -const { - ExpressInstrumentation, -} = require('@opentelemetry/instrumentation-express'); -const { - OpenTelemetryWebpackPlugin, -} = require('opentelemetry-webpack-plugin-node'); const nodeExternals = require('webpack-node-externals'); const swcConfig = require('./.swcrc.json'); const { svgr } = require('./svg.cjs'); @@ -96,14 +90,4 @@ module.exports = { }, ], }, - plugins: [ - // Patches express at build time so instrumentation works in the bundle - ...(DEV - ? [] - : [ - new OpenTelemetryWebpackPlugin({ - instrumentations: [new ExpressInstrumentation()], - }), - ]), - ], }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b62b7420e1b..de296323a88 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -368,15 +368,15 @@ importers: '@guardian/tsconfig': specifier: 'catalog:' version: 1.0.1 + '@opentelemetry/api': + specifier: 1.9.1 + version: 1.9.1 '@opentelemetry/exporter-trace-otlp-http': specifier: 0.220.0 version: 0.220.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': specifier: 0.220.0 version: 0.220.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-express': - specifier: 0.68.0 - version: 0.68.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-http': specifier: 0.220.0 version: 0.220.0(@opentelemetry/api@1.9.1) @@ -638,9 +638,6 @@ importers: node-fetch: specifier: 3.3.2 version: 3.3.2 - opentelemetry-webpack-plugin-node: - specifier: 4.5.0 - version: 4.5.0(@opentelemetry/api@1.9.1)(@swc/core@1.15.46)(clean-css@5.3.3)(esbuild@0.28.2)(html-minifier-terser@7.2.0)(postcss@8.5.25)(webpack-cli@7.2.2) postcss-styled-syntax: specifier: 0.7.1 version: 0.7.1(postcss@8.5.25) @@ -3225,10 +3222,6 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@opentelemetry/api-logs@0.200.0': - resolution: {integrity: sha512-IKJBQxh91qJ+3ssRly5hYEJ8NDHu9oY/B1PXVSCWf7zytmYO9RNLB0Ox9XQ/fJ8m6gY6Q6NtBWlmXfaXt5Uc4Q==} - engines: {node: '>=8.0.0'} - '@opentelemetry/api-logs@0.220.0': resolution: {integrity: sha512-CmVa4ImJ+ynfrPMNaAXHET6Bhb44SwzmfyVJFq9ni2jgXJR/l7C6gfVFddNmHP+ZOkP9cf4f9DBe68qVLTHc9w==} engines: {node: '>=8.0.0'} @@ -3243,12 +3236,6 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@2.8.0': - resolution: {integrity: sha512-hd1Lfh8p545nNz+jq1Ejfz+Mn1hyLuxYn1YzTfFNrxr8urEWMNQLPf1Th8kjOH+HxwawCrtgBp8JpBUR4ZSgww==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@2.9.0': resolution: {integrity: sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==} engines: {node: ^18.19.0 || >=20.6.0} @@ -3261,24 +3248,12 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-express@0.68.0': - resolution: {integrity: sha512-3ffjIQUFNVP94lLLHlBEgNaomCoP0BLH36Gxmkk3/WKX+1530QKVAnZTleYdM3RVU2EeQFtWSmFMAyCLglqO3w==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-http@0.220.0': resolution: {integrity: sha512-Szt4dO2Boz2CDr38DaSw/lnqwhwKl+IAdgNGEGgSm2Anb+fwPtIAGmIwkhsLLN69QQZQE96JxjMKYY4rlRkYKw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation@0.200.0': - resolution: {integrity: sha512-pmPlzfJd+vvgaZd/reMsC8RWgTXn2WY1OWT5RT42m3aOn5532TozwXNDhg1vzqJ+jnvmkREcdLr27ebJEQt0Jg==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation@0.220.0': resolution: {integrity: sha512-xQx3E2WxP1mDvKzxLxX+CTCtNLa560YJZ3087qYHerl2YmiKpv7AH+dAy7vmx+eVrZ5BwhfWUAVoKOoxCNHcpw==} engines: {node: ^18.19.0 || >=20.6.0} @@ -4881,9 +4856,6 @@ packages: '@types/serve-static@2.2.0': resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} - '@types/shimmer@1.2.0': - resolution: {integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==} - '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} @@ -5211,11 +5183,6 @@ packages: acorn-globals@7.0.1: resolution: {integrity: sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==} - acorn-import-attributes@1.9.5: - resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} - peerDependencies: - acorn: ^8 - acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -7182,9 +7149,6 @@ packages: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} - import-in-the-middle@1.15.0: - resolution: {integrity: sha512-bpQy+CrsRmYmoPMAE/0G33iwRqwW4ouqdRg8jgbH3aKuCtOc8lxgmYXg2dMM92CRiGP660EtBcymH/eVUpCSaA==} - import-in-the-middle@3.3.3: resolution: {integrity: sha512-AiohS3H80sXO6owEltjGX+glb7qXaDhBoJb9XcQVH4UI207xu/bDLUcadVKp7Qe576reg9yr/PXZjV5qx8gfbA==} engines: {node: '>=18'} @@ -7492,9 +7456,6 @@ packages: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} - javascript-stringify@2.1.0: - resolution: {integrity: sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==} - jest-changed-files@29.7.0: resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -8275,12 +8236,6 @@ packages: resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} hasBin: true - opentelemetry-node-bundler-plugin-utils@0.8.0: - resolution: {integrity: sha512-GyZS++IonGwAJ37kQZPTcEDai+cc6iWFrERFb7gaZwZyXscTh7N2Hy8uA/UYsMGQPNBydbLF6k/OUKof63rGYg==} - - opentelemetry-webpack-plugin-node@4.5.0: - resolution: {integrity: sha512-2wGh1xUCkw2WkwISveejXwyy0xufZkJPmNKTkFzoy95BTkWU55fCsA69lC+yclwoPpxfm0YQFpsyxABYfZgU0A==} - optionator@0.9.3: resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} engines: {node: '>= 0.8.0'} @@ -8798,10 +8753,6 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} - require-in-the-middle@7.5.2: - resolution: {integrity: sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==} - engines: {node: '>=8.6.0'} - require-in-the-middle@8.0.1: resolution: {integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==} engines: {node: '>=9.3.0 || >=8.10.0 <9.0.0'} @@ -9037,9 +8988,6 @@ packages: resolution: {integrity: sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==} engines: {node: '>= 0.4'} - shimmer@1.2.1: - resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} - side-channel-list@1.0.0: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} engines: {node: '>= 0.4'} @@ -13339,10 +13287,6 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.15.0 - '@opentelemetry/api-logs@0.200.0': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs@0.220.0': dependencies: '@opentelemetry/api': 1.9.1 @@ -13353,11 +13297,6 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/semantic-conventions': 1.43.0 - '@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -13372,15 +13311,6 @@ snapshots: '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-express@0.68.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) - '@opentelemetry/semantic-conventions': 1.43.0 - transitivePeerDependencies: - - supports-color - '@opentelemetry/instrumentation-http@0.220.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -13391,17 +13321,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation@0.200.0(@opentelemetry/api@1.9.1)': - dependencies: - '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs': 0.200.0 - '@types/shimmer': 1.2.0 - import-in-the-middle: 1.15.0 - require-in-the-middle: 7.5.2 - shimmer: 1.2.1 - transitivePeerDependencies: - - supports-color - '@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 @@ -15092,8 +15011,6 @@ snapshots: '@types/http-errors': 2.0.5 '@types/node': 24.12.4 - '@types/shimmer@1.2.0': {} - '@types/stack-utils@2.0.3': {} '@types/tough-cookie@4.0.2': {} @@ -15524,10 +15441,6 @@ snapshots: acorn: 8.18.0 acorn-walk: 8.3.5 - acorn-import-attributes@1.9.5(acorn@8.18.0): - dependencies: - acorn: 8.18.0 - acorn-jsx@5.3.2(acorn@8.16.0): dependencies: acorn: 8.16.0 @@ -17830,13 +17743,6 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 - import-in-the-middle@1.15.0: - dependencies: - acorn: 8.18.0 - acorn-import-attributes: 1.9.5(acorn@8.18.0) - cjs-module-lexer: 1.4.3 - module-details-from-path: 1.0.4 - import-in-the-middle@3.3.3: dependencies: cjs-module-lexer: 2.2.1 @@ -18127,8 +18033,6 @@ snapshots: has-symbols: 1.1.0 set-function-name: 2.0.2 - javascript-stringify@2.1.0: {} - jest-changed-files@29.7.0: dependencies: execa: 5.1.1 @@ -19178,39 +19082,6 @@ snapshots: opener@1.5.2: {} - opentelemetry-node-bundler-plugin-utils@0.8.0(@opentelemetry/api@1.9.1): - dependencies: - '@opentelemetry/instrumentation': 0.200.0(@opentelemetry/api@1.9.1) - javascript-stringify: 2.1.0 - semver: 7.8.5 - tslib: 2.8.1 - transitivePeerDependencies: - - '@opentelemetry/api' - - supports-color - - opentelemetry-webpack-plugin-node@4.5.0(@opentelemetry/api@1.9.1)(@swc/core@1.15.46)(clean-css@5.3.3)(esbuild@0.28.2)(html-minifier-terser@7.2.0)(postcss@8.5.25)(webpack-cli@7.2.2): - dependencies: - '@opentelemetry/instrumentation': 0.200.0(@opentelemetry/api@1.9.1) - opentelemetry-node-bundler-plugin-utils: 0.8.0(@opentelemetry/api@1.9.1) - tslib: 2.8.1 - webpack: 5.109.2(@swc/core@1.15.46)(clean-css@5.3.3)(esbuild@0.28.2)(html-minifier-terser@7.2.0)(postcss@8.5.25)(webpack-cli@7.2.2) - transitivePeerDependencies: - - '@minify-html/node' - - '@opentelemetry/api' - - '@swc/core' - - '@swc/css' - - '@swc/html' - - clean-css - - cssnano - - csso - - esbuild - - html-minifier-terser - - lightningcss - - postcss - - supports-color - - uglify-js - - webpack-cli - optionator@0.9.3: dependencies: '@aashutoshrathi/word-wrap': 1.2.6 @@ -19798,14 +19669,6 @@ snapshots: require-from-string@2.0.2: {} - require-in-the-middle@7.5.2: - dependencies: - debug: 4.4.3 - module-details-from-path: 1.0.4 - resolve: 1.22.12 - transitivePeerDependencies: - - supports-color - require-in-the-middle@8.0.1: dependencies: debug: 4.4.3 @@ -20120,8 +19983,6 @@ snapshots: shell-quote@1.9.0: {} - shimmer@1.2.1: {} - side-channel-list@1.0.0: dependencies: es-errors: 1.3.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 11a4a71230c..015cdc8597e 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,7 +6,6 @@ allowBuilds: '@swc/core': true esbuild: true nice-napi: true - protobufjs: true unrs-resolver: false # This postinstall is just a workaround for an npm issue so we don't need it https://github.com/unrs/unrs-resolver/pull/66 blockExoticSubdeps: true catalog: From 21b10e6d274f727e034d9fcdc6bc9cc060e52afe Mon Sep 17 00:00:00 2001 From: Jorge Azevedo Date: Sat, 15 Aug 2026 03:45:09 +0100 Subject: [PATCH 03/10] Align instrumentation implementation with official docs --- dotcom-rendering/package.json | 9 +- .../src/server/instrumentation.js | 29 - .../src/server/instrumentation.ts | 25 + .../webpack/webpack.config.server.js | 2 +- pnpm-lock.yaml | 503 ++++++++++++++---- pnpm-workspace.yaml | 1 + 6 files changed, 442 insertions(+), 127 deletions(-) delete mode 100644 dotcom-rendering/src/server/instrumentation.js create mode 100644 dotcom-rendering/src/server/instrumentation.ts diff --git a/dotcom-rendering/package.json b/dotcom-rendering/package.json index 59f8110d044..ce1367ecd5e 100644 --- a/dotcom-rendering/package.json +++ b/dotcom-rendering/package.json @@ -47,12 +47,9 @@ "@guardian/support-dotcom-components": "11.0.0", "@guardian/tsconfig": "catalog:", "@opentelemetry/api": "1.9.1", - "@opentelemetry/exporter-trace-otlp-http": "0.220.0", - "@opentelemetry/instrumentation": "0.220.0", - "@opentelemetry/instrumentation-http": "0.220.0", - "@opentelemetry/resources": "2.9.0", - "@opentelemetry/sdk-trace-base": "2.9.0", - "@opentelemetry/sdk-trace-node": "2.9.0", + "@opentelemetry/exporter-trace-otlp-http": "0.219.0", + "@opentelemetry/instrumentation-http": "0.219.0", + "@opentelemetry/sdk-node": "0.219.0", "@playwright/test": "1.60.0", "@sentry/browser": "10.65.0", "@storybook/addon-a11y": "10.5.7", diff --git a/dotcom-rendering/src/server/instrumentation.js b/dotcom-rendering/src/server/instrumentation.js deleted file mode 100644 index 0b350f5c0ab..00000000000 --- a/dotcom-rendering/src/server/instrumentation.js +++ /dev/null @@ -1,29 +0,0 @@ -const { - OTLPTraceExporter, -} = require('@opentelemetry/exporter-trace-otlp-http'); -const { registerInstrumentations } = require('@opentelemetry/instrumentation'); -const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http'); -const { resourceFromAttributes } = require('@opentelemetry/resources'); -const { BatchSpanProcessor } = require('@opentelemetry/sdk-trace-base'); -const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node'); - -// NodeTracerProvider reads OTEL_EXPORTER_OTLP_ENDPOINT from the environment, -// but not OTEL_SERVICE_NAME, so the resource has to be set explicitly. -const provider = new NodeTracerProvider({ - resource: resourceFromAttributes({ - 'service.name': process.env.OTEL_SERVICE_NAME || 'dotcom-rendering', - }), - spanProcessors: [new BatchSpanProcessor(new OTLPTraceExporter())], -}); - -provider.register(); - -registerInstrumentations({ - instrumentations: [new HttpInstrumentation()], -}); - -process.on('SIGTERM', () => { - provider.shutdown().catch((err) => { - console.error('OpenTelemetry shutdown failed', err); - }); -}); diff --git a/dotcom-rendering/src/server/instrumentation.ts b/dotcom-rendering/src/server/instrumentation.ts new file mode 100644 index 00000000000..e66e2bf2165 --- /dev/null +++ b/dotcom-rendering/src/server/instrumentation.ts @@ -0,0 +1,25 @@ +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; +import { HttpInstrumentation } from '@opentelemetry/instrumentation-http'; +// The OTel packages are pinned to 0.219: 0.220+ breaks webpack production builds until the fix +// in webpack/webpack#21623 ships. See open-telemetry/opentelemetry-js#6981. +import { NodeSDK } from '@opentelemetry/sdk-node'; + +// This code is boilerplate. +// - OpenTelemetry recommends using the NodeSDK https://opentelemetry.io/docs/languages/js/getting-started/nodejs/ +// - The NodeSDK README has a instrumentation.ts that matches our use case better https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/opentelemetry-sdk-node/README.md +// Note: we can't use auto instrumentation as it doesn't work natively with webpack +const sdk = new NodeSDK({ + traceExporter: new OTLPTraceExporter(), + instrumentations: [new HttpInstrumentation()], +}); + +sdk.start(); + +process.on('SIGTERM', () => { + sdk.shutdown() + .then( + () => console.log('SDK shut down successfully'), + (err) => console.log('Error shutting down SDK', err), + ) + .finally(() => process.exit(0)); +}); diff --git a/dotcom-rendering/webpack/webpack.config.server.js b/dotcom-rendering/webpack/webpack.config.server.js index 579bef6c95b..d0e8d3ae160 100644 --- a/dotcom-rendering/webpack/webpack.config.server.js +++ b/dotcom-rendering/webpack/webpack.config.server.js @@ -27,7 +27,7 @@ const swcLoader = [ module.exports = { entry: { server: './src/server/server.ts', - instrumentation: './src/server/instrumentation.js', + instrumentation: './src/server/instrumentation.ts', }, output: { filename: `[name].js`, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index de296323a88..19f525ebb09 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -372,23 +372,14 @@ importers: specifier: 1.9.1 version: 1.9.1 '@opentelemetry/exporter-trace-otlp-http': - specifier: 0.220.0 - version: 0.220.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': - specifier: 0.220.0 - version: 0.220.0(@opentelemetry/api@1.9.1) + specifier: 0.219.0 + version: 0.219.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-http': - specifier: 0.220.0 - version: 0.220.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': - specifier: 2.9.0 - version: 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base': - specifier: 2.9.0 - version: 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-node': - specifier: 2.9.0 - version: 2.9.0(@opentelemetry/api@1.9.1) + specifier: 0.219.0 + version: 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-node': + specifier: 0.219.0 + version: 0.219.0(@opentelemetry/api@1.9.1) '@playwright/test': specifier: 1.60.0 version: 1.60.0 @@ -2641,6 +2632,15 @@ packages: '@noble/hashes': optional: true + '@grpc/grpc-js@1.14.4': + resolution: {integrity: sha512-k9Dj3DV/itK9D06Y8f190Qgop7/Ui+D0njFV3LHMPwPT75DpXLQohE9Wmz0QElrJnzsjB7KPWiKJbOl7IPDArQ==} + engines: {node: '>=12.10.0'} + + '@grpc/proto-loader@0.8.1': + resolution: {integrity: sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==} + engines: {node: '>=6'} + hasBin: true + '@guardian/braze-components@23.0.2': resolution: {integrity: sha512-j1YwU78FKb1+5xL2Fh8TtJnKHXA46GW8rxTN71u7V5PKnnLCiW0BYPAMyRYCXm9czPnsoyj9ptEOpYKpiuTDZw==} engines: {node: '24.2'} @@ -2942,6 +2942,9 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@js-sdsl/ordered-map@4.4.2': + resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} + '@jsonjoy.com/base64@1.1.2': resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==} engines: {node: '>=10.0'} @@ -3222,91 +3225,175 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@opentelemetry/api-logs@0.220.0': - resolution: {integrity: sha512-CmVa4ImJ+ynfrPMNaAXHET6Bhb44SwzmfyVJFq9ni2jgXJR/l7C6gfVFddNmHP+ZOkP9cf4f9DBe68qVLTHc9w==} + '@opentelemetry/api-logs@0.219.0': + resolution: {integrity: sha512-FFx7YnaYJlIjqWW/AG/yAZ0L/NEY724PipXXXQLdtZPbLwBGbUMTGL1i/esI56TWfTUXxhLfpgrnWJCG8aUJyg==} engines: {node: '>=8.0.0'} '@opentelemetry/api@1.9.1': resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==} engines: {node: '>=8.0.0'} - '@opentelemetry/context-async-hooks@2.9.0': - resolution: {integrity: sha512-OQ0vzvbZBiUhjqLnUaoNfYmP8553Crr3aggB4y0ZUi815mZ7idpdJXQmoKdeBKJelYttoBlLSSHubmyw3wvX4w==} + '@opentelemetry/configuration@0.219.0': + resolution: {integrity: sha512-wXZUYv4ngu43nA4WEhuXNacm46LW+17LRM8nKyIhBzroRA24PBYjMnakwzR/w777nFUB5xlgsYTTeuXxumZM1Q==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + + '@opentelemetry/context-async-hooks@2.8.0': + resolution: {integrity: sha512-/3FIraneMcng67SUJCxvyInk/oxzwsxyadufk0wwfOBLf5wqtAGX4MoQASwSbndBPeARzBryUM9Azr5kHIdWLw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@2.9.0': - resolution: {integrity: sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==} + '@opentelemetry/core@2.8.0': + resolution: {integrity: sha512-hd1Lfh8p545nNz+jq1Ejfz+Mn1hyLuxYn1YzTfFNrxr8urEWMNQLPf1Th8kjOH+HxwawCrtgBp8JpBUR4ZSgww==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/exporter-trace-otlp-http@0.220.0': - resolution: {integrity: sha512-/+ExB3lRkf+erv4PnoywyL7RHKITidxtUpUTS55k7OQ0dB42S7gEF1gry7swb9MSm1hYLUhJg4QQh9W8SpwwqA==} + '@opentelemetry/exporter-logs-otlp-grpc@0.219.0': + resolution: {integrity: sha512-7SvzDCIclHWAcCwZ1MTOLcwn4BVNPGI3QxS/DJraPNe1TTL+4TvUBq5zeQV8tsnYvtDN7wKW2qocVmaCP2l7sQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-http@0.220.0': - resolution: {integrity: sha512-Szt4dO2Boz2CDr38DaSw/lnqwhwKl+IAdgNGEGgSm2Anb+fwPtIAGmIwkhsLLN69QQZQE96JxjMKYY4rlRkYKw==} + '@opentelemetry/exporter-logs-otlp-http@0.219.0': + resolution: {integrity: sha512-mhl2HL6GmZI8b8PwPfqMws/5ovJfbRTxwc9Y5agVVHiQ+e5SL1btsFr/kJDgt7YCexDtsUn5HAreHQO9szFS0A==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation@0.220.0': - resolution: {integrity: sha512-xQx3E2WxP1mDvKzxLxX+CTCtNLa560YJZ3087qYHerl2YmiKpv7AH+dAy7vmx+eVrZ5BwhfWUAVoKOoxCNHcpw==} + '@opentelemetry/exporter-logs-otlp-proto@0.219.0': + resolution: {integrity: sha512-Ayw4Gf71PS9jhBVaYywa4WsajnqfDehMkTdVH3TSAVHqPcsAv/AhH/wTNRYNt99szeYr6Gbd/D6RjZD77wAxHg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/otlp-exporter-base@0.220.0': - resolution: {integrity: sha512-CXYo8UD5Mn9YbgebO2EL4wejtA+gxLmLiu6HCk2KH2BR7XhFN6/6p1UlCb23DYCjeYkndevLHuejCCN1yx4+OQ==} + '@opentelemetry/exporter-metrics-otlp-grpc@0.219.0': + resolution: {integrity: sha512-6LaaSrPxK5L55bXevWajvOMxGOpNm0n12tG53TeZaUeNzXwLPg6d2KCC1zAlGsojan+xRG71mA4Qqs9K2VVrKQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/otlp-transformer@0.220.0': - resolution: {integrity: sha512-lXGrv7KXZ0gNH9SVNUaa6vv6phVYGvJxfXAlMbzbakiXru75f5MZl8Z7oqiMMQD77riVHJCFlQvbZs/VVN2/4A==} + '@opentelemetry/exporter-metrics-otlp-http@0.219.0': + resolution: {integrity: sha512-6CaDRbMVHZSDWzNXwrR8y/H4B/Z1eMNnkHiPQlTx3Ojz2OHY4X/aff/UC4P/3pHUQSuTfi3oh2UsPPZppw+Vrg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/resources@2.9.0': - resolution: {integrity: sha512-jyA5MBLQ+Dkl3+JsZkUoUvL7yHvU64kLsvpXKarWm6347Sl1t1bXFTFykUePNpT5WH5pm9a2Qtt03iIYQhZ1Fg==} + '@opentelemetry/exporter-metrics-otlp-proto@0.219.0': + resolution: {integrity: sha512-DUS7XyIiEnoeccQUvuKy0G2/YqeKhpN8FVIrGbrLNIVMj10yeIFLRzRv0tibCI2kXXvlTTABVexGAk78wHk2ug==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-prometheus@0.219.0': + resolution: {integrity: sha512-TxOnJ85eWJY5JyOJsNMXiRTYlkDcOv0u3KbXEzWCc+tUS9sjL/BC6BcdxZ0B9r2OFVqsrZFXUzSD2sZUy42Ucw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-trace-otlp-grpc@0.219.0': + resolution: {integrity: sha512-BkDNv1UD6BscW19MxbAxVmSYSSFuyeqR6buV2/HTYqA7GrR0EbTFzqG6h86T3PtXmpdbsWjMGLDdjG2rikG27Q==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-trace-otlp-http@0.219.0': + resolution: {integrity: sha512-9t6SvBXXBEjOBcIzgozvBbd3jWrv3Gt3ngGhl1fhdZ/zRc7oZDVOFEqbi2zlBpW9BXhgDMKv422J0DL/3iQWfw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-trace-otlp-proto@0.219.0': + resolution: {integrity: sha512-lF/LUBfhOFmxJa+SQsLN7ziV4MHa2pyKgOM6JNehSOfU+npjM4gwm9oIKEJrzrWcexMcqydiyoFy0XCb1Ql3wQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/exporter-zipkin@2.8.0': + resolution: {integrity: sha512-Mj84UkEa17BK2o903VTXW3wM8CrSZexGs4tRGVZVIMM9ni1T6TuGx5IrRfoWKAbshx42D5/kc7YV+axypLPYyA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + + '@opentelemetry/instrumentation-http@0.219.0': + resolution: {integrity: sha512-nNt1fqpyah/OKjNHdEOu8xLwISppRU2qJuF8aR+fCcftVwdFkPgtworBLA+TI1HU2iF508jcQBF2gerWczJAXg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/instrumentation@0.219.0': + resolution: {integrity: sha512-X5t7I8GyIO9rmGHwoedZLREpQqrF1WW2nxzNNym6HOKpFiE+rvqV3ngC0xcZVO2YwIGf3KKmRdWrYwdwz3H9RQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-exporter-base@0.219.0': + resolution: {integrity: sha512-zvIxQX/AZUVKDU+hCuYx+7UkiP7GRdnk1ZbFQRYzHvYp47cAWR4j3IhoPhV9KaeXEv2xdGq3IA6PnpzDmLcmSA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-grpc-exporter-base@0.219.0': + resolution: {integrity: sha512-iIk/s8QQu39zpTrRRmsW/Eg3SE2+Hg8tLWepr2FLRgmwUpNd0IpCTLJEHJ77hpt4hgIS8MAh44UYI4xQPZwWlw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-transformer@0.219.0': + resolution: {integrity: sha512-aaYKAyXhw9VchKZVGOopD3Gw/kPsyrX2c6IQ0AW32mTjqmZOh5Y6Gf5OYqTNqVktAeBjmFinhyFaCwW6GYK9YQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/propagator-b3@2.8.0': + resolution: {integrity: sha512-SazlvuSKi5533rPHTW2TwBwdMakhjZST4SYs0YauuvfGDkT13KbG1gJS75hV0uWVeevhtVP9sAIlaZLTHdSbMg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/propagator-jaeger@2.8.0': + resolution: {integrity: sha512-Xnz9zZvvQzUw+9DrOn0MomR7BxFCkA2pcfXBQuHC28ndJpSbjLs7knzYb05kw5SyCjSsEWombkZMgGcJSk8JVg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/resources@2.8.0': + resolution: {integrity: sha512-qmXQ27ilDbUK/vGMqwL8D4/rhn76C+sherM4wTbjlfknR8Nvfc/hCxjRJPhkzZzUsPiNg16SA31NxMabwttRjg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/sdk-logs@0.220.0': - resolution: {integrity: sha512-WywcTkQtv2iNmt+6y5Kcd4rzvx9bLVsBa2Nwcmg01IUaBTkTow3W4d9KE5vNBpEDtb9tp21WcRBY/lANRrApYA==} + '@opentelemetry/sdk-logs@0.219.0': + resolution: {integrity: sha512-s6lTKRakaPClvKoWHRChxnXjDMkM/TQ30ff78jN6EBGf7MI7VzANE5PU3f4z9qDUudWjvZjOLHG0rBnBKYvoXA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.4.0 <1.10.0' - '@opentelemetry/sdk-metrics@2.9.0': - resolution: {integrity: sha512-Xx8RGS4H5XEBl01WuCreMIpiah9cCXMbSkeuIePPdD2cUpq/vUzYmj8E/MK1OsbOc93FuAD4jfn2WOacKwLn7Q==} + '@opentelemetry/sdk-metrics@2.8.0': + resolution: {integrity: sha512-UDBGaj6W0Rgy5rTTaoxs8gVGF/aGkAKyjurJv7se6wjRxJu7FoquTLT/vt54DZfo4crbprYfhX/SOK9+BPw1qg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.9.0 <1.10.0' - '@opentelemetry/sdk-trace-base@2.9.0': - resolution: {integrity: sha512-cp9zmTl62R8PJrpvFcmc8N2JQU/xfa0S+61q511Nji+QxCfZ8Ifvg7H27G8cANe4crg4RTrWsVvanHiXjSp6ag==} + '@opentelemetry/sdk-node@0.219.0': + resolution: {integrity: sha512-NWLpWLEb8gV3+JBHYoIrktbM385wyHpRJoh3J/4Q52d4PR+AlPMNGJT3DzBUrDSUEVbKAXoHR+EDAPxtiNcj8g==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/sdk-trace-node@2.9.0': - resolution: {integrity: sha512-ec9a7ps37huy5itYk0MalaZdSLlM6AXWp/FhtEjgMpp5leEGojBDvAl/UWttQnkMZOvFHKzRESn8TD3yKTF5nQ==} + '@opentelemetry/sdk-trace-base@2.8.0': + resolution: {integrity: sha512-mhU4jp+vW0mGbFRd+GeXHvmfA4aDqWjBjLC3pE5XMpLs0IE2ryYb019Ts2AQrOq67gaTF25D91+fgvEHDZEnuQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/sdk-trace@2.9.0': - resolution: {integrity: sha512-sGA19HvtrrSKYsseHphluH6j3p6Xa3fqc7c7y8f/7mYWejc1lyDFcpSdD1kYa50HCLUeEo4zA5bW0pniaPszuw==} + '@opentelemetry/sdk-trace-node@2.8.0': + resolution: {integrity: sha512-nZt9OGufioAc3AfoLTqA9bsAeaMJAictYDdI2VcNQ+PmT+3rfKjAZDZvgPfd8VPX0O5Bw1hdQF6kDK8VSpZiWg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: - '@opentelemetry/api': '>=1.3.0 <1.10.0' + '@opentelemetry/api': '>=1.0.0 <1.10.0' '@opentelemetry/semantic-conventions@1.43.0': resolution: {integrity: sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==} @@ -3597,6 +3684,33 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.5': + resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==} + + '@protobufjs/eventemitter@1.1.1': + resolution: {integrity: sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==} + + '@protobufjs/fetch@1.1.1': + resolution: {integrity: sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.2': + resolution: {integrity: sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==} + '@rollup/plugin-commonjs@29.0.0': resolution: {integrity: sha512-U2YHaxR2cU/yAiwKJtJRhnyLk7cifnQw0zUpISsocBDoHDJn+HTV74ABqnwr5bEgWUwFZC9oFL6wLe21lHu5eQ==} engines: {node: '>=16.0.0 || 14 >= 14.17'} @@ -7753,6 +7867,9 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} @@ -7773,6 +7890,9 @@ packages: resolution: {integrity: sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==} engines: {node: '>=8.0'} + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -8554,6 +8674,10 @@ packages: property-information@6.4.0: resolution: {integrity: sha512-9t5qARVofg2xQqKtytzt+lZ4d1Qvj8t5B8fEwXK6qOfgRLgH/b13QlgEyDh033NOS31nXeFbYv7CLUDG1CeifQ==} + protobufjs@7.6.5: + resolution: {integrity: sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==} + engines: {node: '>=12.0.0'} + protocols@2.0.2: resolution: {integrity: sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==} @@ -12644,6 +12768,18 @@ snapshots: '@exodus/bytes@1.15.1': {} + '@grpc/grpc-js@1.14.4': + dependencies: + '@grpc/proto-loader': 0.8.1 + '@js-sdsl/ordered-map': 4.4.2 + + '@grpc/proto-loader@0.8.1': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.6.5 + yargs: 17.7.2 + '@guardian/braze-components@23.0.2(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@guardian/libs@32.0.0(@guardian/ophan-tracker-js@5.1.1)(tslib@2.6.2)(typescript@6.0.3))(@guardian/source@12.2.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1)(tslib@2.6.2)(typescript@6.0.3))(react@18.3.1)': dependencies: '@emotion/react': 11.14.0(@types/react@18.3.1)(react@18.3.1) @@ -13034,6 +13170,8 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@js-sdsl/ordered-map@4.4.2': {} + '@jsonjoy.com/base64@1.1.2(tslib@2.6.2)': dependencies: tslib: 2.6.2 @@ -13287,106 +13425,251 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.15.0 - '@opentelemetry/api-logs@0.220.0': + '@opentelemetry/api-logs@0.219.0': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/api@1.9.1': {} - '@opentelemetry/context-async-hooks@2.9.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/configuration@0.219.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + yaml: 2.3.4 - '@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/context-async-hooks@2.8.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + + '@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/semantic-conventions': 1.43.0 - '@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/exporter-logs-otlp-grpc@0.219.0(@opentelemetry/api@1.9.1)': dependencies: + '@grpc/grpc-js': 1.14.4 '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-exporter-base': 0.220.0(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-transformer': 0.220.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-grpc-exporter-base': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-logs': 0.219.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation-http@0.220.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/exporter-logs-otlp-http@0.219.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) + '@opentelemetry/api-logs': 0.219.0 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-logs': 0.219.0(@opentelemetry/api@1.9.1) + + '@opentelemetry/exporter-logs-otlp-proto@0.219.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/api-logs': 0.219.0 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-logs': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.8.0(@opentelemetry/api@1.9.1) + + '@opentelemetry/exporter-metrics-otlp-grpc@0.219.0(@opentelemetry/api@1.9.1)': + dependencies: + '@grpc/grpc-js': 1.14.4 + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-metrics-otlp-http': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-grpc-exporter-base': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-metrics': 2.8.0(@opentelemetry/api@1.9.1) + + '@opentelemetry/exporter-metrics-otlp-http@0.219.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-metrics': 2.8.0(@opentelemetry/api@1.9.1) + + '@opentelemetry/exporter-metrics-otlp-proto@0.219.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-metrics-otlp-http': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-metrics': 2.8.0(@opentelemetry/api@1.9.1) + + '@opentelemetry/exporter-prometheus@0.219.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-metrics': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.43.0 + + '@opentelemetry/exporter-trace-otlp-grpc@0.219.0(@opentelemetry/api@1.9.1)': + dependencies: + '@grpc/grpc-js': 1.14.4 + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-grpc-exporter-base': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.8.0(@opentelemetry/api@1.9.1) + + '@opentelemetry/exporter-trace-otlp-http@0.219.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.8.0(@opentelemetry/api@1.9.1) + + '@opentelemetry/exporter-trace-otlp-proto@0.219.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.8.0(@opentelemetry/api@1.9.1) + + '@opentelemetry/exporter-zipkin@2.8.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.43.0 + + '@opentelemetry/instrumentation-http@0.219.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.219.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.43.0 forwarded-parse: 2.1.2 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation@0.219.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs': 0.220.0 + '@opentelemetry/api-logs': 0.219.0 import-in-the-middle: 3.3.3 require-in-the-middle: 8.0.1 transitivePeerDependencies: - supports-color - '@opentelemetry/otlp-exporter-base@0.220.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/otlp-exporter-base@0.219.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.219.0(@opentelemetry/api@1.9.1) + + '@opentelemetry/otlp-grpc-exporter-base@0.219.0(@opentelemetry/api@1.9.1)': + dependencies: + '@grpc/grpc-js': 1.14.4 + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.219.0(@opentelemetry/api@1.9.1) + + '@opentelemetry/otlp-transformer@0.219.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/api-logs': 0.219.0 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-logs': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-metrics': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.8.0(@opentelemetry/api@1.9.1) + + '@opentelemetry/propagator-b3@2.8.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-transformer': 0.220.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/otlp-transformer@0.220.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/propagator-jaeger@2.8.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs': 0.220.0 - '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-logs': 0.220.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-metrics': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources@2.9.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/resources@2.8.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.43.0 - '@opentelemetry/sdk-logs@0.220.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/sdk-logs@0.219.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/api-logs': 0.220.0 - '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/api-logs': 0.219.0 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.43.0 - '@opentelemetry/sdk-metrics@2.9.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/sdk-metrics@2.8.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/sdk-node@0.219.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/api-logs': 0.219.0 + '@opentelemetry/configuration': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/context-async-hooks': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-logs-otlp-grpc': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-logs-otlp-http': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-logs-otlp-proto': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-metrics-otlp-grpc': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-metrics-otlp-http': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-metrics-otlp-proto': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-prometheus': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-trace-otlp-grpc': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-trace-otlp-http': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-trace-otlp-proto': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/exporter-zipkin': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-grpc-exporter-base': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/propagator-b3': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/propagator-jaeger': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-logs': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-metrics': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-node': 2.8.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': 1.43.0 + transitivePeerDependencies: + - supports-color - '@opentelemetry/sdk-trace-node@2.9.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/context-async-hooks': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.43.0 - '@opentelemetry/sdk-trace@2.9.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/sdk-trace-node@2.8.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/semantic-conventions': 1.43.0 + '@opentelemetry/context-async-hooks': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.8.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions@1.43.0': {} @@ -13621,6 +13904,26 @@ snapshots: '@polka/url@1.0.0-next.29': {} + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.5': {} + + '@protobufjs/eventemitter@1.1.1': {} + + '@protobufjs/fetch@1.1.1': + dependencies: + '@protobufjs/aspromise': 1.1.2 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.2': {} + '@rollup/plugin-commonjs@29.0.0(rollup@4.59.0)': dependencies: '@rollup/pluginutils': 5.3.0(rollup@4.59.0) @@ -18558,6 +18861,8 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash.camelcase@4.3.0: {} + lodash.debounce@4.0.8: {} lodash.merge@4.6.2: {} @@ -18584,6 +18889,8 @@ snapshots: transitivePeerDependencies: - supports-color + long@5.3.2: {} + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -19412,6 +19719,20 @@ snapshots: property-information@6.4.0: {} + protobufjs@7.6.5: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.5 + '@protobufjs/eventemitter': 1.1.1 + '@protobufjs/fetch': 1.1.1 + '@protobufjs/float': 1.0.2 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.2 + '@types/node': 24.12.4 + long: 5.3.2 + protocols@2.0.2: {} proxy-addr@2.0.7: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 015cdc8597e..0768bbacdf2 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,6 +6,7 @@ allowBuilds: '@swc/core': true esbuild: true nice-napi: true + protobufjs: false # Only generates CLI/codegen artefacts we don't use unrs-resolver: false # This postinstall is just a workaround for an npm issue so we don't need it https://github.com/unrs/unrs-resolver/pull/66 blockExoticSubdeps: true catalog: From c322a8e2d073c6cbdaf9ab2e232da659c2cc65e1 Mon Sep 17 00:00:00 2001 From: Jorge Azevedo Date: Sat, 15 Aug 2026 16:53:50 +0100 Subject: [PATCH 04/10] Align the cdk implementation with AWS reference code --- .../__snapshots__/renderingStack.test.ts.snap | 42 +++++--- dotcom-rendering/cdk/lib/renderingStack.ts | 99 ++++++++++++++----- dotcom-rendering/package.json | 2 +- .../src/server/instrumentation.ts | 6 +- pnpm-lock.yaml | 2 +- pnpm-workspace.yaml | 2 +- 6 files changed, 109 insertions(+), 44 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/renderingStack.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/renderingStack.test.ts.snap index f9dacfc8578..bc99a76f6c6 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/renderingStack.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/renderingStack.test.ts.snap @@ -2125,6 +2125,12 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE "Properties": { "ContainerDefinitions": [ { + "DependsOn": [ + { + "Condition": "START", + "ContainerName": "aws-otel-collector", + }, + ], "DockerLabels": { "RiffRaffDeploymentId": { "Ref": "RiffRaffDeploymentId", @@ -2268,25 +2274,28 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE ], "Cpu": 256, "Essential": false, + "HealthCheck": { + "Command": [ + "CMD-SHELL", + "/healthcheck", + ], + "Interval": 5, + "Retries": 2, + "Timeout": 3, + }, "Image": "public.ecr.aws/aws-observability/aws-otel-collector:v0.49.0", "LogConfiguration": { "LogDriver": "awslogs", "Options": { "awslogs-group": { - "Ref": "EcsTaskDefinitionadotcollectorLogGroup9B99E580", + "Ref": "EcsTaskDefinitionawsotelcollectorLogGroup4477B845", }, "awslogs-region": "eu-west-1", - "awslogs-stream-prefix": "tag-page-rendering-adot", + "awslogs-stream-prefix": "ecs", }, }, - "MemoryReservation": 256, - "Name": "adot-collector", - "PortMappings": [ - { - "ContainerPort": 4318, - "Protocol": "tcp", - }, - ], + "Memory": 512, + "Name": "aws-otel-collector", }, ], "Cpu": "1024", @@ -2444,7 +2453,7 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE "Effect": "Allow", "Resource": { "Fn::GetAtt": [ - "EcsTaskDefinitionadotcollectorLogGroup9B99E580", + "EcsTaskDefinitionawsotelcollectorLogGroup4477B845", "Arn", ], }, @@ -2536,8 +2545,17 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE "Statement": [ { "Action": [ + "logs:PutLogEvents", + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:DescribeLogStreams", + "logs:DescribeLogGroups", + "logs:PutRetentionPolicy", "xray:PutTraceSegments", "xray:PutTelemetryRecords", + "xray:GetSamplingRules", + "xray:GetSamplingTargets", + "xray:GetSamplingStatisticSummaries", ], "Effect": "Allow", "Resource": "*", @@ -2554,7 +2572,7 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE }, "Type": "AWS::IAM::Policy", }, - "EcsTaskDefinitionadotcollectorLogGroup9B99E580": { + "EcsTaskDefinitionawsotelcollectorLogGroup4477B845": { "DeletionPolicy": "Retain", "Properties": { "Tags": [ diff --git a/dotcom-rendering/cdk/lib/renderingStack.ts b/dotcom-rendering/cdk/lib/renderingStack.ts index e60bc4340b0..ad41e83da7a 100644 --- a/dotcom-rendering/cdk/lib/renderingStack.ts +++ b/dotcom-rendering/cdk/lib/renderingStack.ts @@ -18,7 +18,11 @@ import { SnsAction } from 'aws-cdk-lib/aws-cloudwatch-actions'; import type { InstanceType } from 'aws-cdk-lib/aws-ec2'; import { Peer } from 'aws-cdk-lib/aws-ec2'; import type { CfnService } from 'aws-cdk-lib/aws-ecs'; -import { ContainerImage, LogDrivers } from 'aws-cdk-lib/aws-ecs'; +import { + ContainerDependencyCondition, + ContainerImage, + LogDrivers, +} from 'aws-cdk-lib/aws-ecs'; import { ClusterSettings } from 'aws-cdk-lib/aws-ecs/mixins'; import { PolicyStatement } from 'aws-cdk-lib/aws-iam'; import { Subscription, SubscriptionProtocol, Topic } from 'aws-cdk-lib/aws-sns'; @@ -315,31 +319,74 @@ export class RenderingCDKStack extends CDKStack { value, ); } - // ADOT (AWS Distro for OpenTelemetry) sidecar to receive OTLP - // traces from the app and forward them to AWS X-Ray. - app.ecsService?.taskDefinition.addToTaskRolePolicy( - new PolicyStatement({ - actions: [ - 'xray:PutTraceSegments', - 'xray:PutTelemetryRecords', - ], - resources: ['*'], - }), - ); - app.ecsService?.taskDefinition.addContainer('adot-collector', { - image: ContainerImage.fromRegistry( - 'public.ecr.aws/aws-observability/aws-otel-collector:v0.49.0', - ), - command: ['--config=/etc/ecs/ecs-default-config.yaml'], - // Losing traces must never take the app down - essential: false, - logging: LogDrivers.awsLogs({ - streamPrefix: `${guApp}-adot`, - }), - portMappings: [{ containerPort: 4318 }], - cpu: 256, - memoryReservationMiB: 256, - }); + + /** + * The ADOT (AWS Distro for OpenTelemetry) Collector, run as a + * sidecar. The app exports OTLP traces to it over localhost, and it + * forwards them to AWS X-Ray. + * + * Transcribed from the collector's own `awsvpc` sidecar deployment + * template, at the version we run. + * + * @see https://github.com/aws-observability/aws-otel-collector/blob/v0.49.0/deployment-template/ecs/aws-otel-fargate-sidecar-deployment-cfn.yaml + * @see https://aws-otel.github.io/docs/setup/ecs + */ + if (app.ecsService) { + const { taskDefinition } = app.ecsService; + + const collector = taskDefinition.addContainer( + 'aws-otel-collector', + { + // Pinned, where the template uses `latest`, so that deploys are immutable + image: ContainerImage.fromRegistry( + 'public.ecr.aws/aws-observability/aws-otel-collector:v0.49.0', + ), + command: ['--config=/etc/ecs/ecs-default-config.yaml'], //TODO: link to config.yaml in the web + cpu: 256, + memoryLimitMiB: 512, + logging: LogDrivers.awsLogs({ streamPrefix: 'ecs' }), + healthCheck: { + command: ['/healthcheck'], + interval: Duration.seconds(5), + retries: 2, + timeout: Duration.seconds(3), + }, + // In the template the collector is the task; here it is a + // sidecar, and must not take the rendering app down with it + essential: false, + }, + ); + + taskDefinition.defaultContainer?.addContainerDependencies({ + container: collector, + condition: ContainerDependencyCondition.START, + }); + + /** + * `AWSDistroOpenTelemetryPolicy`, less `ssm:GetParameters`, which + * is only used when `--config` names an SSM parameter. + * + * @see https://aws-otel.github.io/docs/setup/permissions + */ + taskDefinition.addToTaskRolePolicy( + new PolicyStatement({ + actions: [ + 'logs:PutLogEvents', + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:DescribeLogStreams', + 'logs:DescribeLogGroups', + 'logs:PutRetentionPolicy', + 'xray:PutTraceSegments', + 'xray:PutTelemetryRecords', + 'xray:GetSamplingRules', + 'xray:GetSamplingTargets', + 'xray:GetSamplingStatisticSummaries', + ], + resources: ['*'], + }), + ); + } // TODO make these changes at the pattern level in GuCDK if (app.ecsService) { diff --git a/dotcom-rendering/package.json b/dotcom-rendering/package.json index ce1367ecd5e..1fcabc13754 100644 --- a/dotcom-rendering/package.json +++ b/dotcom-rendering/package.json @@ -47,7 +47,7 @@ "@guardian/support-dotcom-components": "11.0.0", "@guardian/tsconfig": "catalog:", "@opentelemetry/api": "1.9.1", - "@opentelemetry/exporter-trace-otlp-http": "0.219.0", + "@opentelemetry/exporter-trace-otlp-proto": "0.219.0", "@opentelemetry/instrumentation-http": "0.219.0", "@opentelemetry/sdk-node": "0.219.0", "@playwright/test": "1.60.0", diff --git a/dotcom-rendering/src/server/instrumentation.ts b/dotcom-rendering/src/server/instrumentation.ts index e66e2bf2165..6314992dbf8 100644 --- a/dotcom-rendering/src/server/instrumentation.ts +++ b/dotcom-rendering/src/server/instrumentation.ts @@ -1,4 +1,4 @@ -import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto'; import { HttpInstrumentation } from '@opentelemetry/instrumentation-http'; // The OTel packages are pinned to 0.219: 0.220+ breaks webpack production builds until the fix // in webpack/webpack#21623 ships. See open-telemetry/opentelemetry-js#6981. @@ -6,8 +6,8 @@ import { NodeSDK } from '@opentelemetry/sdk-node'; // This code is boilerplate. // - OpenTelemetry recommends using the NodeSDK https://opentelemetry.io/docs/languages/js/getting-started/nodejs/ -// - The NodeSDK README has a instrumentation.ts that matches our use case better https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/opentelemetry-sdk-node/README.md -// Note: we can't use auto instrumentation as it doesn't work natively with webpack +// - The NodeSDK README has an example instrumentation.ts that matches our use case better https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/opentelemetry-sdk-node/README.md +// Note: we can't use auto instrumentation as it doesn't work natively with webpack, so we call the only module that works which is HttpInstrumentation const sdk = new NodeSDK({ traceExporter: new OTLPTraceExporter(), instrumentations: [new HttpInstrumentation()], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 19f525ebb09..4df2fed78b4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -371,7 +371,7 @@ importers: '@opentelemetry/api': specifier: 1.9.1 version: 1.9.1 - '@opentelemetry/exporter-trace-otlp-http': + '@opentelemetry/exporter-trace-otlp-proto': specifier: 0.219.0 version: 0.219.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation-http': diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0768bbacdf2..d414be8fd19 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -6,7 +6,7 @@ allowBuilds: '@swc/core': true esbuild: true nice-napi: true - protobufjs: false # Only generates CLI/codegen artefacts we don't use + protobufjs: false # Its postinstall only prints a version-scheme warning, it builds nothing unrs-resolver: false # This postinstall is just a workaround for an npm issue so we don't need it https://github.com/unrs/unrs-resolver/pull/66 blockExoticSubdeps: true catalog: From 80de697c406da5e0c9c02ac0dbb42a0300f88a21 Mon Sep 17 00:00:00 2001 From: Jorge Azevedo Date: Sat, 15 Aug 2026 17:35:23 +0100 Subject: [PATCH 05/10] Fix health check and remove noisy traces --- .../cdk/lib/__snapshots__/renderingStack.test.ts.snap | 2 +- dotcom-rendering/cdk/lib/renderingStack.ts | 8 ++++++-- dotcom-rendering/src/server/instrumentation.ts | 10 +++++++++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/renderingStack.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/renderingStack.test.ts.snap index bc99a76f6c6..047adfe8e84 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/renderingStack.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/renderingStack.test.ts.snap @@ -2276,7 +2276,7 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE "Essential": false, "HealthCheck": { "Command": [ - "CMD-SHELL", + "CMD", "/healthcheck", ], "Interval": 5, diff --git a/dotcom-rendering/cdk/lib/renderingStack.ts b/dotcom-rendering/cdk/lib/renderingStack.ts index ad41e83da7a..a27f0d5e8fa 100644 --- a/dotcom-rendering/cdk/lib/renderingStack.ts +++ b/dotcom-rendering/cdk/lib/renderingStack.ts @@ -330,6 +330,9 @@ export class RenderingCDKStack extends CDKStack { * * @see https://github.com/aws-observability/aws-otel-collector/blob/v0.49.0/deployment-template/ecs/aws-otel-fargate-sidecar-deployment-cfn.yaml * @see https://aws-otel.github.io/docs/setup/ecs + * + * The default config.yaml used is also available on github + * @see https://github.com/aws-observability/aws-otel-collector/blob/0771477f9db2879afad3ae3ff7811b5264a151a8/config/ecs/ecs-default-config.yaml */ if (app.ecsService) { const { taskDefinition } = app.ecsService; @@ -341,12 +344,13 @@ export class RenderingCDKStack extends CDKStack { image: ContainerImage.fromRegistry( 'public.ecr.aws/aws-observability/aws-otel-collector:v0.49.0', ), - command: ['--config=/etc/ecs/ecs-default-config.yaml'], //TODO: link to config.yaml in the web + command: ['--config=/etc/ecs/ecs-default-config.yaml'], cpu: 256, memoryLimitMiB: 512, logging: LogDrivers.awsLogs({ streamPrefix: 'ecs' }), healthCheck: { - command: ['/healthcheck'], + // `CMD`, not `CMD-SHELL`, as the image is distroless and has no shell + command: ['CMD', '/healthcheck'], interval: Duration.seconds(5), retries: 2, timeout: Duration.seconds(3), diff --git a/dotcom-rendering/src/server/instrumentation.ts b/dotcom-rendering/src/server/instrumentation.ts index 6314992dbf8..c92293938d4 100644 --- a/dotcom-rendering/src/server/instrumentation.ts +++ b/dotcom-rendering/src/server/instrumentation.ts @@ -10,7 +10,15 @@ import { NodeSDK } from '@opentelemetry/sdk-node'; // Note: we can't use auto instrumentation as it doesn't work natively with webpack, so we call the only module that works which is HttpInstrumentation const sdk = new NodeSDK({ traceExporter: new OTLPTraceExporter(), - instrumentations: [new HttpInstrumentation()], + instrumentations: [ + new HttpInstrumentation({ + // The load balancer polls this every few seconds + ignoreIncomingRequestHook: (request) => + request.url === '/_healthcheck', + // Rendering makes no outbound calls, so all we'd trace is AWS SDK background chatter + ignoreOutgoingRequestHook: () => true, + }), + ], }); sdk.start(); From 2abeac707c7959003ebb90e29397701794501947 Mon Sep 17 00:00:00 2001 From: Jorge Azevedo Date: Sat, 15 Aug 2026 19:42:18 +0100 Subject: [PATCH 06/10] Refactor span code so it matches documentation --- dotcom-rendering/src/server/server.prod.ts | 43 +++++++++++++++------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/dotcom-rendering/src/server/server.prod.ts b/dotcom-rendering/src/server/server.prod.ts index f8c4f091a42..d39e0c0df00 100644 --- a/dotcom-rendering/src/server/server.prod.ts +++ b/dotcom-rendering/src/server/server.prod.ts @@ -1,6 +1,11 @@ -import { context, trace } from '@opentelemetry/api'; +import { trace } from '@opentelemetry/api'; import compression from 'compression'; -import type { ErrorRequestHandler, Request, Response } from 'express'; +import type { + ErrorRequestHandler, + Request, + RequestHandler, + Response, +} from 'express'; import express from 'express'; import { NotRenderableInDCR } from '../lib/errors/not-renderable-in-dcr'; import { handleAllEditorialNewslettersPage } from './handler.allEditorialNewslettersPage.web'; @@ -33,8 +38,12 @@ import { logger } from './lib/logging'; import { requestLoggerMiddleware } from './lib/logging-middleware'; import { recordError } from './lib/logging-store'; -// Hand-rolled because @opentelemetry/instrumentation-express patches express at -// require time, which webpack has already inlined by the time the server runs. +// Spans are hand-made because @opentelemetry/instrumentation-express hooks +// `require`, and webpack has inlined express into the server bundle. +// There is a community plugin for webpack that enables this auto-instrumentation +// but we'd prefer to avoid using it. +// @see https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/opentelemetry-instrumentation/README.md#limitations +// @see https://opentelemetry.io/docs/languages/js/instrumentation/#acquiring-a-tracer const tracer = trace.getTracer('dotcom-rendering'); export const prodServer = (): void => { @@ -42,14 +51,17 @@ export const prodServer = (): void => { const app = express(); - const parseJson = express.json({ limit: '50mb' }); - app.use((req, res, next) => { - const span = tracer.startSpan('json parsing'); - parseJson(req, res, (error?: unknown) => { + // Starts a span just before calling express.json, and ends it on its callback + const expressJson = express.json({ limit: '50mb' }); + const expressJsonWrapper: RequestHandler = (req, res, next) => { + const span = tracer.startSpan('express.json'); + expressJson(req, res, (error) => { span.end(); next(error); }); - }); + }; + + app.use(expressJsonWrapper); app.use(requestLoggerMiddleware); app.use(compression()); app.use(responseHeaderMiddleware); @@ -65,13 +77,16 @@ export const prodServer = (): void => { app.use('/assets', express.static(__dirname)); } - // Made the active span so that spans added inside handlers nest beneath it - app.use((req, res, next) => { + // Starts a span until the http connection is closed + // Because it's the last middleware before the request handler, + // the span will effectively "wrap" the request handler + const requestHandlerWrapper: RequestHandler = (req, res, next) => { const span = tracer.startSpan('request handler'); - // `close` rather than `finish` so the span ends even if the client disconnects res.on('close', () => span.end()); - context.with(trace.setSpan(context.active(), span), next); - }); + next(); + }; + + app.use(requestHandlerWrapper); app.post('/Article', handleArticle); app.post('/Interactive', handleInteractive); From 291441c8a83350bf4f138a91328a13b3e62ababd Mon Sep 17 00:00:00 2001 From: Jorge Azevedo Date: Sun, 16 Aug 2026 20:01:41 +0100 Subject: [PATCH 07/10] Remove Jaeger --- docker-compose.yaml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index fa85d71077a..3fd54642df3 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -16,10 +16,6 @@ services: # Explicitly tell AWS SDK where to find credentials AWS_SHARED_CREDENTIALS_FILE: /.aws/credentials - - # OpenTelemetry exporter endpoint (Jaeger) - OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4318 - OTEL_SERVICE_NAME: tag-page-rendering ports: - '9000:9000' @@ -55,9 +51,3 @@ services: command: | curl "https://www.theguardian.com/tone/minutebyminute.json?dcr=true" --silent > data.json && \ curl -X POST http://localhost:9000/TagPage -d @data.json -H "Content-Type: application/json" - - jaeger: - image: jaegertracing/jaeger:2.6.0 - ports: - - '16686:16686' - - '4318:4318' From 5d1d25ec048ff916831ad856697a67ebde3dc5d5 Mon Sep 17 00:00:00 2001 From: Jorge Azevedo Date: Sun, 16 Aug 2026 21:30:55 +0100 Subject: [PATCH 08/10] Tweak comments --- dotcom-rendering/cdk/lib/renderingStack.ts | 9 ++------- dotcom-rendering/src/server/instrumentation.ts | 11 +++++++---- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/dotcom-rendering/cdk/lib/renderingStack.ts b/dotcom-rendering/cdk/lib/renderingStack.ts index a27f0d5e8fa..d22b862b6c6 100644 --- a/dotcom-rendering/cdk/lib/renderingStack.ts +++ b/dotcom-rendering/cdk/lib/renderingStack.ts @@ -325,9 +325,6 @@ export class RenderingCDKStack extends CDKStack { * sidecar. The app exports OTLP traces to it over localhost, and it * forwards them to AWS X-Ray. * - * Transcribed from the collector's own `awsvpc` sidecar deployment - * template, at the version we run. - * * @see https://github.com/aws-observability/aws-otel-collector/blob/v0.49.0/deployment-template/ecs/aws-otel-fargate-sidecar-deployment-cfn.yaml * @see https://aws-otel.github.io/docs/setup/ecs * @@ -340,7 +337,6 @@ export class RenderingCDKStack extends CDKStack { const collector = taskDefinition.addContainer( 'aws-otel-collector', { - // Pinned, where the template uses `latest`, so that deploys are immutable image: ContainerImage.fromRegistry( 'public.ecr.aws/aws-observability/aws-otel-collector:v0.49.0', ), @@ -349,14 +345,13 @@ export class RenderingCDKStack extends CDKStack { memoryLimitMiB: 512, logging: LogDrivers.awsLogs({ streamPrefix: 'ecs' }), healthCheck: { - // `CMD`, not `CMD-SHELL`, as the image is distroless and has no shell command: ['CMD', '/healthcheck'], interval: Duration.seconds(5), retries: 2, timeout: Duration.seconds(3), }, - // In the template the collector is the task; here it is a - // sidecar, and must not take the rendering app down with it + // If resources are constrained this container can be + // taken down to give room to the main app essential: false, }, ); diff --git a/dotcom-rendering/src/server/instrumentation.ts b/dotcom-rendering/src/server/instrumentation.ts index c92293938d4..d4e4423d43a 100644 --- a/dotcom-rendering/src/server/instrumentation.ts +++ b/dotcom-rendering/src/server/instrumentation.ts @@ -4,10 +4,13 @@ import { HttpInstrumentation } from '@opentelemetry/instrumentation-http'; // in webpack/webpack#21623 ships. See open-telemetry/opentelemetry-js#6981. import { NodeSDK } from '@opentelemetry/sdk-node'; -// This code is boilerplate. -// - OpenTelemetry recommends using the NodeSDK https://opentelemetry.io/docs/languages/js/getting-started/nodejs/ -// - The NodeSDK README has an example instrumentation.ts that matches our use case better https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/opentelemetry-sdk-node/README.md -// Note: we can't use auto instrumentation as it doesn't work natively with webpack, so we call the only module that works which is HttpInstrumentation +// This code is mostly boilerplate taken from the Node SDK README +// +// Note: we can't use auto instrumentation as it doesn't work natively with webpack, +// so we call the only module that works which is HttpInstrumentation +// +// @see https://opentelemetry.io/docs/languages/js/getting-started/nodejs/ +// @see https://github.com/open-telemetry/opentelemetry-js/blob/main/experimental/packages/opentelemetry-sdk-node/README.md const sdk = new NodeSDK({ traceExporter: new OTLPTraceExporter(), instrumentations: [ From 463fd9ef3a1fe0cf086afe49839bd683b09c969f Mon Sep 17 00:00:00 2001 From: Justin Rowles Date: Mon, 24 Aug 2026 11:43:41 +0100 Subject: [PATCH 09/10] Add cloudwatch logging on OTEL failure --- dotcom-rendering/src/server/instrumentation.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/dotcom-rendering/src/server/instrumentation.ts b/dotcom-rendering/src/server/instrumentation.ts index d4e4423d43a..23ae07649fd 100644 --- a/dotcom-rendering/src/server/instrumentation.ts +++ b/dotcom-rendering/src/server/instrumentation.ts @@ -1,8 +1,26 @@ +import { diag, DiagLogLevel } from '@opentelemetry/api'; import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto'; import { HttpInstrumentation } from '@opentelemetry/instrumentation-http'; // The OTel packages are pinned to 0.219: 0.220+ breaks webpack production builds until the fix // in webpack/webpack#21623 ships. See open-telemetry/opentelemetry-js#6981. import { NodeSDK } from '@opentelemetry/sdk-node'; +import { logger } from './lib/logging'; + +// Route OTel's internal diagnostics through DCR's logger so that export failures appear in CloudWatch rather than +// being silently ignored. +// +// This is important because the OTLP exporter will silently fail if it can't reach the endpoint, which can happen if +// the (nonessential) endpoint is down. +diag.setLogger( + { + error: (message, ...args) => logger.error(message, ...args), + warn: (message, ...args) => logger.warn(message, ...args), + info: (message, ...args) => logger.info(message, ...args), + debug: (message, ...args) => logger.debug(message, ...args), + verbose: (message, ...args) => logger.debug(message, ...args), + }, + DiagLogLevel.WARN, // WARN and above only — ERROR covers export failures +); // This code is mostly boilerplate taken from the Node SDK README // From 3129ba35124184862f1a06c8593ee7f0947b80bf Mon Sep 17 00:00:00 2001 From: Justin Rowles Date: Mon, 24 Aug 2026 11:50:40 +0100 Subject: [PATCH 10/10] revert cdk changes for this pr --- .../__snapshots__/renderingStack.test.ts.snap | 120 ------------------ dotcom-rendering/cdk/lib/renderingStack.ts | 100 --------------- 2 files changed, 220 deletions(-) diff --git a/dotcom-rendering/cdk/lib/__snapshots__/renderingStack.test.ts.snap b/dotcom-rendering/cdk/lib/__snapshots__/renderingStack.test.ts.snap index 047adfe8e84..8d22049aef3 100644 --- a/dotcom-rendering/cdk/lib/__snapshots__/renderingStack.test.ts.snap +++ b/dotcom-rendering/cdk/lib/__snapshots__/renderingStack.test.ts.snap @@ -1980,7 +1980,6 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE }, "EcsService81FC6EF6": { "DependsOn": [ - "EcsTaskDefinitionTaskRoleDefaultPolicy1FD2F057", "EcsTaskDefinitionTaskRoleB7B6D8DD", "ListenerTagpagerenderingDeterministicRouteToEc2Rule1C5C95A4", "ListenerTagpagerenderingDeterministicRouteToEcsRuleAB05C756", @@ -2076,7 +2075,6 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE }, "EcsServiceTaskCountTarget02FCCE22": { "DependsOn": [ - "EcsTaskDefinitionTaskRoleDefaultPolicy1FD2F057", "EcsTaskDefinitionTaskRoleB7B6D8DD", ], "Properties": { @@ -2125,12 +2123,6 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE "Properties": { "ContainerDefinitions": [ { - "DependsOn": [ - { - "Condition": "START", - "ContainerName": "aws-otel-collector", - }, - ], "DockerLabels": { "RiffRaffDeploymentId": { "Ref": "RiffRaffDeploymentId", @@ -2173,14 +2165,6 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE "Name": "GU_STACK", "Value": "frontend", }, - { - "Name": "OTEL_EXPORTER_OTLP_ENDPOINT", - "Value": "http://localhost:4318", - }, - { - "Name": "OTEL_SERVICE_NAME", - "Value": "tag-page-rendering", - }, ], "Essential": true, "Image": { @@ -2268,35 +2252,6 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE "ReadonlyRootFilesystem": true, "VersionConsistency": "disabled", }, - { - "Command": [ - "--config=/etc/ecs/ecs-default-config.yaml", - ], - "Cpu": 256, - "Essential": false, - "HealthCheck": { - "Command": [ - "CMD", - "/healthcheck", - ], - "Interval": 5, - "Retries": 2, - "Timeout": 3, - }, - "Image": "public.ecr.aws/aws-observability/aws-otel-collector:v0.49.0", - "LogConfiguration": { - "LogDriver": "awslogs", - "Options": { - "awslogs-group": { - "Ref": "EcsTaskDefinitionawsotelcollectorLogGroup4477B845", - }, - "awslogs-region": "eu-west-1", - "awslogs-stream-prefix": "ecs", - }, - }, - "Memory": 512, - "Name": "aws-otel-collector", - }, ], "Cpu": "1024", "ExecutionRoleArn": { @@ -2445,19 +2400,6 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE ], }, }, - { - "Action": [ - "logs:CreateLogStream", - "logs:PutLogEvents", - ], - "Effect": "Allow", - "Resource": { - "Fn::GetAtt": [ - "EcsTaskDefinitionawsotelcollectorLogGroup4477B845", - "Arn", - ], - }, - }, ], "Version": "2012-10-17", }, @@ -2539,68 +2481,6 @@ exports[`The RenderingCDKStack matches the snapshot for Tag Page Rendering CODE }, "Type": "AWS::IAM::Role", }, - "EcsTaskDefinitionTaskRoleDefaultPolicy1FD2F057": { - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "logs:PutLogEvents", - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:DescribeLogStreams", - "logs:DescribeLogGroups", - "logs:PutRetentionPolicy", - "xray:PutTraceSegments", - "xray:PutTelemetryRecords", - "xray:GetSamplingRules", - "xray:GetSamplingTargets", - "xray:GetSamplingStatisticSummaries", - ], - "Effect": "Allow", - "Resource": "*", - }, - ], - "Version": "2012-10-17", - }, - "PolicyName": "EcsTaskDefinitionTaskRoleDefaultPolicy1FD2F057", - "Roles": [ - { - "Ref": "EcsTaskDefinitionTaskRoleB7B6D8DD", - }, - ], - }, - "Type": "AWS::IAM::Policy", - }, - "EcsTaskDefinitionawsotelcollectorLogGroup4477B845": { - "DeletionPolicy": "Retain", - "Properties": { - "Tags": [ - { - "Key": "App", - "Value": "tag-page-rendering", - }, - { - "Key": "gu:cdk:version", - "Value": "TEST", - }, - { - "Key": "gu:repo", - "Value": "guardian/dotcom-rendering", - }, - { - "Key": "Stack", - "Value": "frontend", - }, - { - "Key": "Stage", - "Value": "CODE", - }, - ], - }, - "Type": "AWS::Logs::LogGroup", - "UpdateReplacePolicy": "Retain", - }, "GetDistributablePolicyTagpagerendering346128E3": { "Properties": { "PolicyDocument": { diff --git a/dotcom-rendering/cdk/lib/renderingStack.ts b/dotcom-rendering/cdk/lib/renderingStack.ts index d22b862b6c6..f436001ba02 100644 --- a/dotcom-rendering/cdk/lib/renderingStack.ts +++ b/dotcom-rendering/cdk/lib/renderingStack.ts @@ -17,14 +17,6 @@ import { Metric, Unit } from 'aws-cdk-lib/aws-cloudwatch'; import { SnsAction } from 'aws-cdk-lib/aws-cloudwatch-actions'; import type { InstanceType } from 'aws-cdk-lib/aws-ec2'; import { Peer } from 'aws-cdk-lib/aws-ec2'; -import type { CfnService } from 'aws-cdk-lib/aws-ecs'; -import { - ContainerDependencyCondition, - ContainerImage, - LogDrivers, -} from 'aws-cdk-lib/aws-ecs'; -import { ClusterSettings } from 'aws-cdk-lib/aws-ecs/mixins'; -import { PolicyStatement } from 'aws-cdk-lib/aws-iam'; import { Subscription, SubscriptionProtocol, Topic } from 'aws-cdk-lib/aws-sns'; import { StringParameter } from 'aws-cdk-lib/aws-ssm'; import { getUserData } from './userData'; @@ -309,8 +301,6 @@ export class RenderingCDKStack extends CDKStack { GU_STAGE: stage, GU_APP: guApp, GU_STACK: guStack, - OTEL_EXPORTER_OTLP_ENDPOINT: 'http://localhost:4318', - OTEL_SERVICE_NAME: guApp, }; for (const [key, value] of Object.entries(ecsEnvVars)) { @@ -319,96 +309,6 @@ export class RenderingCDKStack extends CDKStack { value, ); } - - /** - * The ADOT (AWS Distro for OpenTelemetry) Collector, run as a - * sidecar. The app exports OTLP traces to it over localhost, and it - * forwards them to AWS X-Ray. - * - * @see https://github.com/aws-observability/aws-otel-collector/blob/v0.49.0/deployment-template/ecs/aws-otel-fargate-sidecar-deployment-cfn.yaml - * @see https://aws-otel.github.io/docs/setup/ecs - * - * The default config.yaml used is also available on github - * @see https://github.com/aws-observability/aws-otel-collector/blob/0771477f9db2879afad3ae3ff7811b5264a151a8/config/ecs/ecs-default-config.yaml - */ - if (app.ecsService) { - const { taskDefinition } = app.ecsService; - - const collector = taskDefinition.addContainer( - 'aws-otel-collector', - { - image: ContainerImage.fromRegistry( - 'public.ecr.aws/aws-observability/aws-otel-collector:v0.49.0', - ), - command: ['--config=/etc/ecs/ecs-default-config.yaml'], - cpu: 256, - memoryLimitMiB: 512, - logging: LogDrivers.awsLogs({ streamPrefix: 'ecs' }), - healthCheck: { - command: ['CMD', '/healthcheck'], - interval: Duration.seconds(5), - retries: 2, - timeout: Duration.seconds(3), - }, - // If resources are constrained this container can be - // taken down to give room to the main app - essential: false, - }, - ); - - taskDefinition.defaultContainer?.addContainerDependencies({ - container: collector, - condition: ContainerDependencyCondition.START, - }); - - /** - * `AWSDistroOpenTelemetryPolicy`, less `ssm:GetParameters`, which - * is only used when `--config` names an SSM parameter. - * - * @see https://aws-otel.github.io/docs/setup/permissions - */ - taskDefinition.addToTaskRolePolicy( - new PolicyStatement({ - actions: [ - 'logs:PutLogEvents', - 'logs:CreateLogGroup', - 'logs:CreateLogStream', - 'logs:DescribeLogStreams', - 'logs:DescribeLogGroups', - 'logs:PutRetentionPolicy', - 'xray:PutTraceSegments', - 'xray:PutTelemetryRecords', - 'xray:GetSamplingRules', - 'xray:GetSamplingTargets', - 'xray:GetSamplingStatisticSummaries', - ], - resources: ['*'], - }), - ); - } - - // TODO make these changes at the pattern level in GuCDK - if (app.ecsService) { - app.ecsService.cluster.with( - new ClusterSettings([ - { name: 'containerInsights', value: 'enhanced' }, - ]), - ); - - const cfnService = app.ecsService.node - .defaultChild as CfnService; - cfnService.addPropertyOverride('Monitoring', { - MetricConfigurations: [ - { - MetricNames: [ - 'CPUUtilization', - 'MemoryUtilization', - ], - ResolutionSeconds: 20, - }, - ], - }); - } } /**