summaryrefslogtreecommitdiff
path: root/src/frontend/utils/telemetry
diff options
context:
space:
mode:
authorSaumit <justsaumit@protonmail.com>2025-09-27 02:14:26 +0530
committerSaumit <justsaumit@protonmail.com>2025-09-27 02:14:26 +0530
commit82e03978b89938219958032efb1448cc76baa181 (patch)
tree626f3e54d52ecd49be0ed3bee30abacc0453d081 /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.ts72
-rw-r--r--src/frontend/utils/telemetry/Instrumentation.js41
-rw-r--r--src/frontend/utils/telemetry/InstrumentationMiddleware.ts40
-rw-r--r--src/frontend/utils/telemetry/SessionIdProcessor.ts27
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();
+ }
+}