diff options
Diffstat (limited to 'src/react-native-app/hooks')
| -rw-r--r-- | src/react-native-app/hooks/useColorScheme.ts | 3 | ||||
| -rw-r--r-- | src/react-native-app/hooks/useColorScheme.web.ts | 10 | ||||
| -rw-r--r-- | src/react-native-app/hooks/useThemeColor.ts | 24 | ||||
| -rw-r--r-- | src/react-native-app/hooks/useTracer.ts | 125 |
4 files changed, 162 insertions, 0 deletions
diff --git a/src/react-native-app/hooks/useColorScheme.ts b/src/react-native-app/hooks/useColorScheme.ts new file mode 100644 index 0000000..2a0bb61 --- /dev/null +++ b/src/react-native-app/hooks/useColorScheme.ts @@ -0,0 +1,3 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 +export { useColorScheme } from "react-native"; diff --git a/src/react-native-app/hooks/useColorScheme.web.ts b/src/react-native-app/hooks/useColorScheme.web.ts new file mode 100644 index 0000000..fe92ccd --- /dev/null +++ b/src/react-native-app/hooks/useColorScheme.web.ts @@ -0,0 +1,10 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 +// NOTE: The default React Native styling doesn't support server rendering. +// Server rendered styles should not change between the first render of the HTML +// and the first render on the client. Typically, web developers will use CSS media queries +// to render different styles on the client and server, these aren't directly supported in React Native +// but can be achieved using a styling library like Nativewind. +export function useColorScheme() { + return "light"; +} diff --git a/src/react-native-app/hooks/useThemeColor.ts b/src/react-native-app/hooks/useThemeColor.ts new file mode 100644 index 0000000..c7b9684 --- /dev/null +++ b/src/react-native-app/hooks/useThemeColor.ts @@ -0,0 +1,24 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 +/** + * Learn more about light and dark modes: + * https://docs.expo.dev/guides/color-schemes/ + */ + +import { useColorScheme } from "react-native"; + +import { Colors } from "@/constants/Colors"; + +export function useThemeColor( + props: { light?: string; dark?: string }, + colorName: keyof typeof Colors.light & keyof typeof Colors.dark, +) { + const theme = useColorScheme() ?? "light"; + const colorFromProps = props[theme]; + + if (colorFromProps) { + return colorFromProps; + } else { + return Colors[theme][colorName]; + } +} 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, + }; +}; |
