diff --git a/.changeset/salty-streets-ring.md b/.changeset/salty-streets-ring.md new file mode 100644 index 0000000..ba1875d --- /dev/null +++ b/.changeset/salty-streets-ring.md @@ -0,0 +1,5 @@ +--- +"react-native-open-telemetry": minor +--- + +sessions: trace correlation based on the session.id attribute diff --git a/example/src/App.tsx b/example/src/App.tsx index affcdcd..21926f5 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -8,6 +8,11 @@ const sdk = openTelemetrySDK({ name: "MyExampleApp", version: "1.0.0-alpha", environment: "development", + features: { + session: { + getSessionId: () => "custom-session-id", + }, + }, }); const tracer = sdk.trace.getTracer("my-js-tracer"); @@ -78,8 +83,8 @@ export default function App() { onPress={() => { fetch("https://api.weatherstack.com/current?query=Portland") .then((response) => response.json()) - .then(data => console.log(data)) - .catch(error => console.error(error)) + .then((data) => console.log(data)) + .catch((error) => console.error(error)); }} > Fetch weather diff --git a/src/SessionIdProcessor.ts b/src/SessionIdProcessor.ts new file mode 100644 index 0000000..29b6067 --- /dev/null +++ b/src/SessionIdProcessor.ts @@ -0,0 +1,32 @@ +import type { Context } from "@opentelemetry/api"; +import type { + Span, + SpanProcessor, + ReadableSpan, +} from "@opentelemetry/sdk-trace-base"; + +type GetSessionId = () => string | null; + +export class SessionIdProcessor implements SpanProcessor { + getSessionId: GetSessionId; + + constructor(getSessionId: GetSessionId) { + this.getSessionId = getSessionId; + } + + onStart(span: Span, _parentContext: Context): void { + const sessionId = this.getSessionId(); + if (!sessionId) return; + span.setAttribute("session.id", sessionId); + } + + onEnd(_span: ReadableSpan): void {} + + forceFlush(): Promise { + return Promise.resolve(); + } + + shutdown(): Promise { + return Promise.resolve(); + } +} diff --git a/src/index.tsx b/src/index.tsx index 55427ae..c27040c 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -27,6 +27,7 @@ import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION, } from "@opentelemetry/semantic-conventions"; +import { SessionIdProcessor } from "./SessionIdProcessor"; import type { Options } from "./types"; export function openTelemetrySDK(options: Options = {}) { @@ -60,12 +61,16 @@ export function openTelemetrySDK(options: Options = {}) { }) ) : null; + const sessionSpanProcessor = options.features?.session + ? new SessionIdProcessor(options.features.session.getSessionId) + : null; const tracerProvider = new WebTracerProvider({ resource, spanProcessors: [ logSpanProcessor, otlpSpanProcessor, + sessionSpanProcessor, ].filter((processor) => processor !== null), }); @@ -84,8 +89,8 @@ export function openTelemetrySDK(options: Options = {}) { propagateTraceHeaderCorsUrls: /.*/, clearTimingResources: false, }), - ] - }) + ], + }); // Metrics diff --git a/src/types.ts b/src/types.ts index 13d2f23..18e87a3 100644 --- a/src/types.ts +++ b/src/types.ts @@ -5,4 +5,9 @@ export interface Options { url?: string; debug?: boolean; native?: boolean; + features?: { + session?: { + getSessionId: () => string | null; + }; + }; }