@targetprocess/react-tracing
v1.4.0
Published
React utilities for OpenTelemetry tracing
Maintainers
Readme
@targetprocess/react-tracing
React utilities for OpenTelemetry tracing.
Table of Contents
Installation
npm install @targetprocess/react-tracing @opentelemetry/apiOverview
This library provides React components, hooks, and utilities to easily add OpenTelemetry tracing to your React applications. It helps you monitor and analyze the performance of your React components and track user interactions.
Features
- 🪝 React Hooks: Use the
useSpanhook to create spans in functional components - 🧩 HOC Components: Wrap your components with
withSpanto automatically trace rendering - 🌳 Context Providers: Configure tracing settings and propagate spans through your component tree
Quick Start
- Run OTEL exporter like Jaeger
podman run -d --name jaeger -p 16686:16686 -p 4317:4317 -p 4318:4318 -p 14250:14250 -p 14268:14268 -e COLLECTOR_OTLP_ENABLED=true -e COLLECTOR_OTLP_HTTP_HOST=0.0.0.0 -e COLLECTOR_OTLP_GRPC_HOST=0.0.0.0 -e COLLECTOR_OTLP_HTTP_CORS_ALLOWED_ORIGINS=* -e COLLECTOR_OTLP_GRPC_TLS_ENABLED=false jaegertracing/all-in-one:latest - Setup
@opentelemetry
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 {Resource, detectResourcesSync} 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';
export const tracerName = 'my-app';
export const setup = () => {
let resource = new Resource({
[ATTR_SERVICE_NAME]: tracerName,
});
const detectedResources = detectResourcesSync({
detectors: [browserDetector as any],
});
resource = resource.merge(detectedResources);
const OTLPProxyUrl = 'http://localhost:4318/v1/traces';
const provider = new WebTracerProvider({
resource,
spanProcessors: [
new BatchSpanProcessor(
new OTLPTraceExporter({
url: OTLPProxyUrl,
}) as any,
{
scheduledDelayMillis: 500,
},
),
],
});
provider.register({
propagator: new CompositePropagator({
propagators: [
new W3CBaggagePropagator(),
new W3CTraceContextPropagator(),
],
}),
});
registerInstrumentations({
tracerProvider: provider,
});
};- Wrap app with tracer
import { TelemetrySettingsProvider, withSpan, useSpan, useSpanFactory } from '@targetprocess/react-tracing';
import { setup, tracerName } from './tracer';
// Init @opentelemetry tracer
setup()
// Use the withSpan HOC to automatically trace component rendering
const TracedComponent = withSpan()(MyComponent);
// Configure telemetry settings at the app root
function App() {
return (
<TelemetrySettingsProvider settings={{
tracerName,
sharedAttributes: {
environment: process.env.NODE_ENV,
version: '1.0.0'
}
}}>
<MyComponent />
</TelemetrySettingsProvider>
);
}
// Or use the useSpan hook for more control
function MyComponent() {
const { span } = useSpan('MyComponent');
const { startSpan } = useSpanFactory();
useEffect(() => {
// Create a child span for a specific operation
const childSpan = startSpan('DataFetching');
fetchData().then(() => {
childSpan?.end();
});
}, []);
return <div>My Component</div>;
}API Reference
Hooks
useSpan
A React hook that creates an OpenTelemetry span for a component or custom logic. The span is created when the component mounts and can be used throughout the component's lifecycle.
import { useSpan } from '@targetprocess/react-tracing';
function MyComponent() {
const { span } = useSpan('MyComponent');
// Use the span
useEffect(() => {
// Add attributes to the span
if (span) {
span.setAttribute('component.type', 'functional');
}
return () => {
// End the span when component unmounts
if (span) span.end();
};
}, []);
return <div>My Component</div>;
}Parameters:
name: string- The name of the spanoptions?: ReactSpanOptions- Optional OpenTelemetry span options with additional React-specific optionsskipDefaultAttributes?: boolean- When true, shared attributes from TelemetrySettingsProvider will not be applied to this span
Returns:
span: Span | null- The created OpenTelemetry span, or null if telemetry is disabled
useSpanFactory
A React hook that provides a factory function for creating multiple spans on demand. This is useful when you need to create spans for specific operations within a component.
import { useSpanFactory } from '@targetprocess/react-tracing';
function MyComponent() {
const { startSpan } = useSpanFactory();
const handleClick = async () => {
// Create a span for this specific operation
const span = startSpan('button.click');
try {
// Add attributes to the span
span?.setAttribute('button.id', 'submit-button');
await performOperation();
// End the span when the operation is complete
span?.end();
} catch (error) {
// Record the error on the span
span?.recordException(error);
span?.end();
throw error;
}
};
const handleCustomClick = async () => {
// Create a span without shared attributes
const span = startSpan('custom.operation', {
skipDefaultAttributes: true,
attributes: {
custom: 'value',
operation: 'special'
}
});
try {
await performCustomOperation();
span?.end();
} catch (error) {
span?.recordException(error);
span?.end();
throw error;
}
};
return (
<div>
<button onClick={handleClick}>Click Me</button>
<button onClick={handleCustomClick}>Custom Operation</button>
</div>
);
}Parameters:
parentSpan?: Span- Optional parent span to use instead of the span from context
Returns:
startSpan: (name: string, options?: ReactSpanOptions) => Span | null- Function to create a new spanoptions.skipDefaultAttributes?: boolean- When true, shared attributes from TelemetrySettingsProvider will not be applied to this span
Components
withSpan
A higher-order component (HOC) that wraps a component with OpenTelemetry tracing. It automatically creates a span when the component mounts and ends it when the component unmounts.
import { withSpan } from '@targetprocess/react-tracing';
function MyComponent(props) {
return <div>My Component</div>;
}
// Basic usage with default options
export default withSpan()(MyComponent);
// With custom span name and options
export default withSpan({
name: 'CustomSpanName',
options: {
attributes: {
'component.important': true,
},
},
})(MyComponent);Parameters:
spanOptions?: WithSpanOptions- Optional configurationname?: string- Custom span name (defaults to component name)options?: ReactSpanOptions- OpenTelemetry span options with additional React-specific optionsskipDefaultAttributes?: boolean- When true, shared attributes from TelemetrySettingsProvider will not be applied to this span
ErrorBoundary
A React component that catches errors in its child components and records them as OpenTelemetry spans. This helps track and monitor errors in your React application.
import { ErrorBoundary } from '@targetprocess/react-tracing';
function App() {
return (
<ErrorBoundary>
<YourComponent />
</ErrorBoundary>
);
}
// With rethrow option
function AppWithRethrow() {
return (
<ErrorBoundary rethrow={true}>
<YourComponent />
</ErrorBoundary>
);
}Props:
children: ReactNode- Child components to renderrethrow?: boolean- When true, the error will be re-thrown after recording it in the span. This allows parent error boundaries to catch the error. Defaults to false.
Context Providers
TelemetrySettingsProvider
A context provider for configuring telemetry settings across your application.
import { TelemetrySettingsProvider } from '@targetprocess/react-tracing';
function App() {
const settings = {
tracerName: 'my-app-tracer',
disabled: process.env.NODE_ENV === 'development',
sharedAttributes: {
environment: process.env.NODE_ENV,
version: '1.0.0',
region: 'us-east-1'
}
};
return (
<TelemetrySettingsProvider settings={settings}>
<AppContent />
</TelemetrySettingsProvider>
);
}Props:
settings: ContextInitialState- Configuration options for the telemetry systemtracerName: string- Name of the OpenTelemetry tracer to usedisabled?: boolean- Whether telemetry is disabledsharedAttributes?: Attributes- Attributes that will be added to every span in the React tree
ParentSpanContextProvider
A context provider that makes a span available to child components. This is used internally by the withSpan HOC but can also be used directly when needed.
import { ParentSpanContextProvider } from '@targetprocess/react-tracing';
import { trace } from '@opentelemetry/api';
function MyComponent() {
// Create a span
const span = trace.getTracer('my-tracer').startSpan('my-operation');
return (
<ParentSpanContextProvider span={span}>
{/* Child components can access this span via useSpanContext */}
<ChildComponent />
</ParentSpanContextProvider>
);
}Props:
span: Span | null- The span to make available to child componentschildren: ReactNode- Child components
Context Hooks
useSpanContext
A hook that provides access to the parent span from the nearest ParentSpanContextProvider.
import { useSpanContext } from '@targetprocess/react-tracing';
import { trace } from '@opentelemetry/api';
function ChildComponent() {
const { parentSpan, getParentContext } = useSpanContext();
// Use the parent span
useEffect(() => {
if (parentSpan) {
// Access the parent span directly
parentSpan.setAttribute('child.mounted', true);
// Or use the parent context to create a child span
const ctx = getParentContext();
const tracer = trace.getTracer('my-tracer');
const childSpan = tracer.startSpan('child-operation', {}, ctx);
// Do something with the child span
childSpan.end();
}
}, []);
return <div>Child Component</div>;
}Returns:
parentSpan: Span | null- The parent span from context, or null if not availablegetParentContext: () => Context- Function that returns the active context with the parent span set
Redux Tools
Trace Middleware
The trace middleware connects your Redux store to the span tracing system, automatically forwarding actions to the SpanFlowControl.
import { createTraceMiddleware } from '@targetprocess/react-tracing';
import { configureStore } from '@reduxjs/toolkit';
import { trace } from '@opentelemetry/api';
// Define your flows
const flows = new Map();
flows.set('userAuthentication', {
startEvents: new Set(['USER_LOGIN_REQUEST']),
endEvents: new Set(['USER_LOGIN_SUCCESS']),
errorEvents: new Set(['USER_LOGIN_FAILURE']),
// SequenceTelemetryOptions for customizing span creation
getParentSpan: () => parentSpan, // Optional function to provide a parent span
spanOptions: { // Optional OpenTelemetry SpanOptions
attributes: {
'flow.type': 'authentication',
'user.role': 'customer'
}
}
});
// Create the middleware
const tracer = trace.getTracer('my-app');
const traceMiddleware = createTraceMiddleware(flows, tracer);
// Add to your Redux store
const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(traceMiddleware),
});SequenceTelemetryOptions
When defining flows for SpanFlowControl, you can include additional telemetry options to customize how spans are created:
- getParentSpan: () => Span | undefined A function that returns a parent span to establish parent-child relationships between spans. This allows you to create hierarchical traces.
- spanOptions: SpanOptions Standard OpenTelemetry SpanOptions that will be applied to the created span. This can include attributes, links, and other span configuration. Example:
flows.set('checkout', {
startEvents: new Set(['CHECKOUT_START']),
endEvents: new Set(['CHECKOUT_COMPLETE']),
subEvents: new Set(['PAYMENT_PROCESSING', 'INVENTORY_CHECK']),
errorEvents: new Set(['CHECKOUT_ERROR']),
// Telemetry options
getParentSpan: () => currentTransactionSpan,
spanOptions: {
attributes: {
'transaction.type': 'purchase',
'transaction.amount': '125.50',
'customer.type': 'returning'
}
}
});SpanFlowControl
The SpanFlowControl is the core engine behind the trace middleware. It manages event sequences and creates spans based on defined flows. You can use it directly for more advanced use cases:
import { SpanFlowControl } from '@targetprocess/react-tracing';
// Define your flows
const flows = new Map();
flows.set('userSession', {
startEvents: new Set(['USER_LOGIN']),
endEvents: new Set(['USER_LOGOUT']),
subEvents: new Set(['USER_UPDATE_PROFILE', 'USER_CHANGE_PASSWORD']),
errorEvents: new Set(['USER_SESSION_ERROR']),
});
// Create the flow control
const flowControl = new SpanFlowControl(flows, 'my-app-tracer');
// Handle events manually
flowControl.handleEvent('USER_LOGIN');
flowControl.handleEvent('USER_UPDATE_PROFILE');
flowControl.handleEvent('USER_LOGOUT');The SpanFlowControl uses a state machine internally to track event sequences and automatically creates spans with appropriate timing information. When a flow completes (or encounters an error), it creates a span that includes:
- All events in the sequence as span events
- Error events recorded as exceptions
- Accurate timing information for the entire flow
- Custom attributes from spanOptions
This is particularly useful for tracking user journeys, multi-step processes, or any sequence of events that should be traced as a single logical operation.
The SpanFlowControl can handle multiple flows simultaneously, making it ideal for complex applications with overlapping user interactions or system processes.
