diff options
| author | Saumit <justsaumit@protonmail.com> | 2025-09-27 02:14:26 +0530 |
|---|---|---|
| committer | Saumit <justsaumit@protonmail.com> | 2025-09-27 02:14:26 +0530 |
| commit | 82e03978b89938219958032efb1448cc76baa181 (patch) | |
| tree | 626f3e54d52ecd49be0ed3bee30abacc0453d081 /src/frontend/utils/telemetry | |
Initial snapshot - OpenTelemetry demo 2.1.3 -f
Diffstat (limited to 'src/frontend/utils/telemetry')
| -rw-r--r-- | src/frontend/utils/telemetry/FrontendTracer.ts | 72 | ||||
| -rw-r--r-- | src/frontend/utils/telemetry/Instrumentation.js | 41 | ||||
| -rw-r--r-- | src/frontend/utils/telemetry/InstrumentationMiddleware.ts | 40 | ||||
| -rw-r--r-- | src/frontend/utils/telemetry/SessionIdProcessor.ts | 27 |
4 files changed, 180 insertions, 0 deletions
diff --git a/src/frontend/utils/telemetry/FrontendTracer.ts b/src/frontend/utils/telemetry/FrontendTracer.ts new file mode 100644 index 0000000..d52412c --- /dev/null +++ b/src/frontend/utils/telemetry/FrontendTracer.ts @@ -0,0 +1,72 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +import { CompositePropagator, W3CBaggagePropagator, W3CTraceContextPropagator } from '@opentelemetry/core'; +import { WebTracerProvider } from '@opentelemetry/sdk-trace-web'; +import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'; +import { registerInstrumentations } from '@opentelemetry/instrumentation'; +import { getWebAutoInstrumentations } from '@opentelemetry/auto-instrumentations-web'; +import { resourceFromAttributes, detectResources } from '@opentelemetry/resources'; +import { browserDetector } from '@opentelemetry/opentelemetry-browser-detector'; +import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions'; +import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'; +import { SessionIdProcessor } from './SessionIdProcessor'; + +const { + NEXT_PUBLIC_OTEL_SERVICE_NAME = '', + NEXT_PUBLIC_OTEL_EXPORTER_OTLP_TRACES_ENDPOINT = '', + IS_SYNTHETIC_REQUEST = '', +} = typeof window !== 'undefined' ? window.ENV : {}; + +const FrontendTracer = async () => { + const { ZoneContextManager } = await import('@opentelemetry/context-zone'); + + let resource = resourceFromAttributes({ + [ATTR_SERVICE_NAME]: NEXT_PUBLIC_OTEL_SERVICE_NAME, + }); + const detectedResources = detectResources({detectors: [browserDetector]}); + resource = resource.merge(detectedResources); + + const provider = new WebTracerProvider({ + resource, + spanProcessors: [ + new SessionIdProcessor(), + new BatchSpanProcessor( + new OTLPTraceExporter({ + url: NEXT_PUBLIC_OTEL_EXPORTER_OTLP_TRACES_ENDPOINT || 'http://localhost:4318/v1/traces', + }), + { + scheduledDelayMillis: 500, + } + ), + ], + }); + + const contextManager = new ZoneContextManager(); + + provider.register({ + contextManager, + propagator: new CompositePropagator({ + propagators: [ + new W3CBaggagePropagator(), + new W3CTraceContextPropagator()], + }), + }); + + registerInstrumentations({ + tracerProvider: provider, + instrumentations: [ + getWebAutoInstrumentations({ + '@opentelemetry/instrumentation-fetch': { + propagateTraceHeaderCorsUrls: /.*/, + clearTimingResources: true, + applyCustomAttributesOnSpan(span) { + span.setAttribute('app.synthetic_request', IS_SYNTHETIC_REQUEST); + }, + }, + }), + ], + }); +}; + +export default FrontendTracer; diff --git a/src/frontend/utils/telemetry/Instrumentation.js b/src/frontend/utils/telemetry/Instrumentation.js new file mode 100644 index 0000000..39b0b85 --- /dev/null +++ b/src/frontend/utils/telemetry/Instrumentation.js @@ -0,0 +1,41 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +const opentelemetry = require('@opentelemetry/sdk-node'); +const {getNodeAutoInstrumentations} = require('@opentelemetry/auto-instrumentations-node'); +const {OTLPTraceExporter} = require('@opentelemetry/exporter-trace-otlp-grpc'); +const {OTLPMetricExporter} = require('@opentelemetry/exporter-metrics-otlp-grpc'); +const {PeriodicExportingMetricReader} = require('@opentelemetry/sdk-metrics'); +const {alibabaCloudEcsDetector} = require('@opentelemetry/resource-detector-alibaba-cloud'); +const {awsEc2Detector, awsEksDetector} = require('@opentelemetry/resource-detector-aws'); +const {containerDetector} = require('@opentelemetry/resource-detector-container'); +const {gcpDetector} = require('@opentelemetry/resource-detector-gcp'); +const {envDetector, hostDetector, osDetector, processDetector} = require('@opentelemetry/resources'); + +const sdk = new opentelemetry.NodeSDK({ + traceExporter: new OTLPTraceExporter(), + instrumentations: [ + getNodeAutoInstrumentations({ + // disable fs instrumentation to reduce noise + '@opentelemetry/instrumentation-fs': { + enabled: false, + }, + }) + ], + metricReader: new PeriodicExportingMetricReader({ + exporter: new OTLPMetricExporter(), + }), + resourceDetectors: [ + containerDetector, + envDetector, + hostDetector, + osDetector, + processDetector, + alibabaCloudEcsDetector, + awsEksDetector, + awsEc2Detector, + gcpDetector, + ], +}); + +sdk.start(); diff --git a/src/frontend/utils/telemetry/InstrumentationMiddleware.ts b/src/frontend/utils/telemetry/InstrumentationMiddleware.ts new file mode 100644 index 0000000..ed389af --- /dev/null +++ b/src/frontend/utils/telemetry/InstrumentationMiddleware.ts @@ -0,0 +1,40 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +import { NextApiHandler } from 'next'; +import {context, Exception, Span, SpanStatusCode, trace} from '@opentelemetry/api'; +import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; +import { metrics } from '@opentelemetry/api'; + +const meter = metrics.getMeter('frontend'); +const requestCounter = meter.createCounter('app.frontend.requests'); + +const InstrumentationMiddleware = (handler: NextApiHandler): NextApiHandler => { + return async (request, response) => { + const {method, url = ''} = request; + const [target] = url.split('?'); + + const span = trace.getSpan(context.active()) as Span; + + let httpStatus = 200; + try { + await runWithSpan(span, async () => handler(request, response)); + httpStatus = response.statusCode; + } catch (error) { + span.recordException(error as Exception); + span.setStatus({ code: SpanStatusCode.ERROR }); + httpStatus = 500; + throw error; + } finally { + requestCounter.add(1, { method, target, status: httpStatus }); + span.setAttribute(SemanticAttributes.HTTP_STATUS_CODE, httpStatus); + } + }; +}; + +async function runWithSpan(parentSpan: Span, fn: () => Promise<unknown>) { + const ctx = trace.setSpan(context.active(), parentSpan); + return await context.with(ctx, fn); +} + +export default InstrumentationMiddleware; diff --git a/src/frontend/utils/telemetry/SessionIdProcessor.ts b/src/frontend/utils/telemetry/SessionIdProcessor.ts new file mode 100644 index 0000000..cd89c0b --- /dev/null +++ b/src/frontend/utils/telemetry/SessionIdProcessor.ts @@ -0,0 +1,27 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +import { Context } from "@opentelemetry/api"; +import { ReadableSpan, Span, SpanProcessor } from "@opentelemetry/sdk-trace-web"; +import SessionGateway from "../../gateways/Session.gateway"; +import { AttributeNames } from "../enums/AttributeNames"; + +const { userId } = SessionGateway.getSession(); + +export class SessionIdProcessor implements SpanProcessor { + forceFlush(): Promise<void> { + return Promise.resolve(); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onStart(span: Span, parentContext: Context): void { + span.setAttribute(AttributeNames.SESSION_ID, userId); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-empty-function + onEnd(span: ReadableSpan): void {} + + shutdown(): Promise<void> { + return Promise.resolve(); + } +} |
