summaryrefslogtreecommitdiff
path: root/src/react-native-app/hooks/useTracer.ts
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/react-native-app/hooks/useTracer.ts
Initial snapshot - OpenTelemetry demo 2.1.3 -f
Diffstat (limited to 'src/react-native-app/hooks/useTracer.ts')
-rw-r--r--src/react-native-app/hooks/useTracer.ts125
1 files changed, 125 insertions, 0 deletions
diff --git a/src/react-native-app/hooks/useTracer.ts b/src/react-native-app/hooks/useTracer.ts
new file mode 100644
index 0000000..39d560a
--- /dev/null
+++ b/src/react-native-app/hooks/useTracer.ts
@@ -0,0 +1,125 @@
+// 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 { XMLHttpRequestInstrumentation } from "@opentelemetry/instrumentation-xml-http-request";
+import { FetchInstrumentation } from "@opentelemetry/instrumentation-fetch";
+import { registerInstrumentations } from "@opentelemetry/instrumentation";
+import { resourceFromAttributes } from "@opentelemetry/resources";
+import {
+ ATTR_DEVICE_ID,
+ ATTR_OS_NAME,
+ ATTR_OS_VERSION,
+ ATTR_SERVICE_NAME,
+ ATTR_SERVICE_VERSION,
+} from "@opentelemetry/semantic-conventions/incubating";
+import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
+import { useEffect, useState } from "react";
+import {
+ getDeviceId,
+ getSystemVersion,
+ getVersion,
+} from "react-native-device-info";
+import { Platform } from "react-native";
+import { SessionIdProcessor } from "@/utils/SessionIdProcessor";
+import getFrontendProxyURL from "@/utils/Settings";
+
+export const setupTracerProvider = (proxyURL: string) => {
+ // TODO Should add a resource detector for React Native that provides this automatically
+ const resource = resourceFromAttributes({
+ [ATTR_SERVICE_NAME]: "react-native-app",
+ [ATTR_OS_NAME]: Platform.OS,
+ [ATTR_OS_VERSION]: getSystemVersion(),
+ [ATTR_SERVICE_VERSION]: getVersion(),
+ [ATTR_DEVICE_ID]: getDeviceId(),
+ });
+
+ // TODO Not obvious that the WebTracerProvider can be used for React Native, might be useful to have a thin
+ // ReactNativeTracerProvider on top of it (or BasicTracerProvider) that makes this clear. Could also add some
+ // protection against browser specific functionality being added to WebTracerProvider that breaks functionality
+ // for React Native.
+ // Alternatively could offer a TracerProvider that exposed a JS interface on top of the OTEL Android and Swift SDKS,
+ // giving developers the option of collecting telemetry at the native mobile layer
+ return new WebTracerProvider({
+ resource,
+ spanProcessors: [
+ new BatchSpanProcessor(
+ new OTLPTraceExporter({
+ url: `${proxyURL}/otlp-http/v1/traces`,
+ }),
+ {
+ scheduledDelayMillis: 500,
+ },
+ ),
+ // TODO introduce a React Native session processor package that could be used here, taking into account mobile
+ // specific considerations for the session such as putting the app into the background
+ new SessionIdProcessor(),
+ ],
+ });
+}
+
+const Tracer = async () => {
+ const proxyURL = await getFrontendProxyURL();
+ const provider = setupTracerProvider(proxyURL);
+
+ provider.register({
+ propagator: new CompositePropagator({
+ propagators: [
+ new W3CBaggagePropagator(),
+ new W3CTraceContextPropagator(),
+ ],
+ }),
+ });
+
+ registerInstrumentations({
+ instrumentations: [
+ // TODO Some tiptoeing required here, propagateTraceHeaderCorsUrls is required to make the instrumentation
+ // work in the context of a mobile app even though we are not making CORS requests. `clearTimingResources` must
+ // be turned off to avoid using the web-only Performance API
+ // Overall wrapping or forking this and providing a React Native specific auto instrumentation will ease
+ // integration and make it less error-prone
+ new FetchInstrumentation({
+ propagateTraceHeaderCorsUrls: /.*/,
+ clearTimingResources: false,
+ }),
+
+ // The React Native implementation of fetch is simply a polyfill on top of XMLHttpRequest:
+ // https://github.com/facebook/react-native/blob/7ccc5934d0f341f9bc8157f18913a7b340f5db2d/packages/react-native/Libraries/Network/fetch.js#L17
+ // Because of this when making requests using `fetch` there will an additional span created for the underlying
+ // request made with XMLHttpRequest. Since in this demo calls to /api/ are made using fetch, turn off
+ // instrumentation for that path to avoid the extra spans.
+ new XMLHttpRequestInstrumentation({
+ ignoreUrls: [/\/api\/.*/],
+ }),
+ ],
+ });
+};
+
+export interface TracerResult {
+ loaded: boolean;
+}
+
+// TODO providing a wrapper similar to this that uses hooks over the full JS OTEL API would be nice to have for both
+// React Native and React development
+export const useTracer = (): TracerResult => {
+ const [loaded, setLoaded] = useState<boolean>(false);
+
+ useEffect(() => {
+ if (!loaded) {
+ Tracer()
+ .catch(() => console.warn("failed to setup tracer"))
+ .finally(() => setLoaded(true));
+ }
+ }, [loaded]);
+
+ return {
+ loaded,
+ };
+};