# React

> Instrument your React application with OpenTelemetry to send traces, metrics, and logs to Last9

Source: https://last9.io/docs/integrations/frameworks/javascript/react/

Use OpenTelemetry to instrument your React application and send telemetry data to Last9.

## Installation

1. **Install OpenTelemetry packages**

    Install the following packages:

    ```bash
    npm install \
      @opentelemetry/api@1.9.0 \
      @opentelemetry/context-zone@2.1.0 \
      @opentelemetry/exporter-trace-otlp-http@0.205.0 \
      @opentelemetry/instrumentation@0.205.0 \
      @opentelemetry/instrumentation-document-load@0.51.2 \
      @opentelemetry/instrumentation-fetch@0.205.0 \
      @opentelemetry/instrumentation-user-interaction@0.51.2 \
      @opentelemetry/resources@2.1.0 \
      @opentelemetry/sdk-trace-base@2.1.0 \
      @opentelemetry/sdk-trace-web@2.1.0 \
      @opentelemetry/semantic-conventions@1.37.0
    ```

## Setup

### RUM Endpoint URL

The Last9 endpoint URL follows this format:

```
{{ .Traces.WriteExternalURL }}/v1/otlp/organizations/{{ .Org.Slug }}/telemetry/client_monitoring/v1/traces
```

### Authentication

Create a client token for browser applications:

1. Navigate to [Ingestion Tokens](https://app.last9.io/control-plane/ingestion-tokens) in Last9 dashboard
2. Create separate client tokens for development and production environments
   - Development token: restricted to `localhost` origins (e.g., localhost:3000)
   - Production token: restricted to your production domain(s)

### Environment Variables

Set the environment variables:

```bash
REACT_APP_OTEL_SERVICE_NAME=your-app-name
REACT_APP_OTEL_ENDPOINT={{ .Traces.WriteExternalURL }}/v1/otlp/organizations/{{ .Org.Slug }}/telemetry/client_monitoring/v1/traces
REACT_APP_OTEL_API_TOKEN=your-client-token-here
REACT_APP_OTEL_ORIGIN=your-otel-token-origin
REACT_APP_OTEL_ENVIRONMENT=development
```

## Implementation

### Create Telemetry Setup

**TypeScript**

Create a file named `telemetry.ts`:

```typescript
import { WebTracerProvider } from "@opentelemetry/sdk-trace-web";
import { registerInstrumentations } from "@opentelemetry/instrumentation";
import { ZoneContextManager } from "@opentelemetry/context-zone";
import { FetchInstrumentation } from "@opentelemetry/instrumentation-fetch";
import { DocumentLoadInstrumentation } from "@opentelemetry/instrumentation-document-load";
import { UserInteractionInstrumentation } from "@opentelemetry/instrumentation-user-interaction";
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { resourceFromAttributes } from "@opentelemetry/resources";
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";

// Generate or retrieve browser fingerprint (Client-ID)
const getClientId = (): string => {
  const STORAGE_KEY = "last9_client_id";
  let clientId = localStorage.getItem(STORAGE_KEY);

  if (!clientId) {
    // Generate a simple UUID-like identifier
    clientId = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
      const r = (Math.random() * 16) | 0;
      const v = c === "x" ? r : (r & 0x3) | 0x8;
      return v.toString(16);
    });
    localStorage.setItem(STORAGE_KEY, clientId);
  }

  return clientId;
};

// Configuration from environment variables
const OTEL_CONFIG = {
  serviceName: process.env.REACT_APP_OTEL_SERVICE_NAME,
  endpoint: process.env.REACT_APP_OTEL_ENDPOINT,
  apiToken: process.env.REACT_APP_OTEL_API_TOKEN,
  origin: process.env.REACT_APP_OTEL_ORIGIN || window.location.origin,
  environment: process.env.REACT_APP_OTEL_ENVIRONMENT,
};

export const setupTelemetry = () => {
  console.log("[OpenTelemetry] Initializing OpenTelemetry for React App...");

  // Validate required configuration
  if (
    !OTEL_CONFIG.serviceName ||
    !OTEL_CONFIG.endpoint ||
    !OTEL_CONFIG.apiToken
  ) {
    console.error(
      "[OpenTelemetry Error] Missing required OpenTelemetry configuration",
    );
    console.error(
      "   Required: REACT_APP_OTEL_SERVICE_NAME, REACT_APP_OTEL_ENDPOINT, REACT_APP_OTEL_API_TOKEN",
    );
    return;
  }

  // Create resource with service information
  const resource = resourceFromAttributes({
    [ATTR_SERVICE_NAME]: OTEL_CONFIG.serviceName,
    "service.version": "1.0.0", // change this as needed
    "deployment.environment": OTEL_CONFIG.environment,
  });

  // Get or generate Client-ID (browser fingerprint)
  const clientId = getClientId();

  // Configure OTLP exporter with Last9 Client Monitoring headers
  const otlpExporter = new OTLPTraceExporter({
    url: OTEL_CONFIG.endpoint,
    headers: {
      "X-LAST9-API-TOKEN": `Bearer ${OTEL_CONFIG.apiToken}`,
      "Client-ID": clientId,
      "X-LAST9-ORIGIN": OTEL_CONFIG.origin,
    },
  });

  // Add span processor
  const spanProcessor = new BatchSpanProcessor(otlpExporter, {
    maxExportBatchSize: 512,
    exportTimeoutMillis: 30000,
    scheduledDelayMillis: 5000,
  });

  // Create tracer provider with span processor
  const provider = new WebTracerProvider({
    resource,
    spanProcessors: [spanProcessor],
  });

  // Register the provider with Zone context manager for async operations
  provider.register({
    contextManager: new ZoneContextManager(),
  });

  // Register auto-instrumentations
  registerInstrumentations({
    instrumentations: [
      new DocumentLoadInstrumentation({ enabled: true }),
      new FetchInstrumentation({
        enabled: true,
        propagateTraceHeaderCorsUrls: [/.+/g],
        clearTimingResources: true,
        ignoreUrls: [/otlp.*\.last9\.io/],
      }),
      new UserInteractionInstrumentation({
        enabled: true,
        eventNames: ["click", "submit"],
      }),
    ],
  });

  console.log("[OpenTelemetry] OpenTelemetry initialized successfully");
  console.log(`[OpenTelemetry] Service: ${OTEL_CONFIG.serviceName}`);
  console.log(`[OpenTelemetry] Environment: ${OTEL_CONFIG.environment}`);

  return provider;
};

export default setupTelemetry;
```

**JavaScript**

Create a file named `telemetry.js`:

```javascript
const { WebTracerProvider } = require("@opentelemetry/sdk-trace-web");
const { registerInstrumentations } = require("@opentelemetry/instrumentation");
const { ZoneContextManager } = require("@opentelemetry/context-zone");
const {
  FetchInstrumentation,
} = require("@opentelemetry/instrumentation-fetch");
const {
  DocumentLoadInstrumentation,
} = require("@opentelemetry/instrumentation-document-load");
const {
  UserInteractionInstrumentation,
} = require("@opentelemetry/instrumentation-user-interaction");
const { BatchSpanProcessor } = require("@opentelemetry/sdk-trace-base");
const {
  OTLPTraceExporter,
} = require("@opentelemetry/exporter-trace-otlp-http");
const { resourceFromAttributes } = require("@opentelemetry/resources");
const { ATTR_SERVICE_NAME } = require("@opentelemetry/semantic-conventions");

// Generate or retrieve browser fingerprint (Client-ID)
const getClientId = () => {
  const STORAGE_KEY = "last9_client_id";
  let clientId = localStorage.getItem(STORAGE_KEY);

  if (!clientId) {
    // Generate a simple UUID-like identifier
    clientId = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
      const r = (Math.random() * 16) | 0;
      const v = c === "x" ? r : (r & 0x3) | 0x8;
      return v.toString(16);
    });
    localStorage.setItem(STORAGE_KEY, clientId);
  }

  return clientId;
};

// Configuration from environment variables
const OTEL_CONFIG = {
  serviceName: process.env.REACT_APP_OTEL_SERVICE_NAME,
  endpoint: process.env.REACT_APP_OTEL_ENDPOINT,
  apiToken: process.env.REACT_APP_OTEL_API_TOKEN,
  origin: process.env.REACT_APP_OTEL_ORIGIN || window.location.origin,
  environment: process.env.REACT_APP_OTEL_ENVIRONMENT,
};

const setupTelemetry = () => {
  console.log("[OpenTelemetry] Initializing OpenTelemetry for React App...");

  // Validate required configuration
  if (
    !OTEL_CONFIG.serviceName ||
    !OTEL_CONFIG.endpoint ||
    !OTEL_CONFIG.apiToken
  ) {
    console.error(
      "[OpenTelemetry Error] Missing required OpenTelemetry configuration",
    );
    console.error(
      "   Required: REACT_APP_OTEL_SERVICE_NAME, REACT_APP_OTEL_ENDPOINT, REACT_APP_OTEL_API_TOKEN",
    );
    return;
  }

  // Create resource with service information
  const resource = resourceFromAttributes({
    [ATTR_SERVICE_NAME]: OTEL_CONFIG.serviceName,
    "service.version": "1.0.0", // change this as needed
    "deployment.environment": OTEL_CONFIG.environment,
  });

  // Get or generate Client-ID (browser fingerprint)
  const clientId = getClientId();

  // Configure OTLP exporter with Last9 Client Monitoring headers
  const otlpExporter = new OTLPTraceExporter({
    url: OTEL_CONFIG.endpoint,
    headers: {
      "X-LAST9-API-TOKEN": `Bearer ${OTEL_CONFIG.apiToken}`,
      "Client-ID": clientId,
      "X-LAST9-ORIGIN": OTEL_CONFIG.origin,
    },
  });

  // Add span processor
  const spanProcessor = new BatchSpanProcessor(otlpExporter, {
    maxExportBatchSize: 512,
    exportTimeoutMillis: 30000,
    scheduledDelayMillis: 5000,
  });

  // Create tracer provider with span processor
  const provider = new WebTracerProvider({
    resource,
    spanProcessors: [spanProcessor],
  });

  // Register the provider with Zone context manager for async operations
  provider.register({
    contextManager: new ZoneContextManager(),
  });

  // Register auto-instrumentations
  registerInstrumentations({
    instrumentations: [
      new DocumentLoadInstrumentation({ enabled: true }),
      new FetchInstrumentation({
        enabled: true,
        propagateTraceHeaderCorsUrls: [/.+/g],
        clearTimingResources: true,
        ignoreUrls: [/otlp.*\.last9\.io/],
      }),
      new UserInteractionInstrumentation({
        enabled: true,
        eventNames: ["click", "submit"],
      }),
    ],
  });

  console.log("[OpenTelemetry] OpenTelemetry initialized successfully");
  console.log(`[OpenTelemetry] Service: ${OTEL_CONFIG.serviceName}`);
  console.log(`[OpenTelemetry] Environment: ${OTEL_CONFIG.environment}`);

  return provider;
};

module.exports = { setupTelemetry };
```

### Initialize in Your App

Import the telemetry setup at your application's entry point **before importing React**:

**TypeScript**

In your `index.tsx`:

```typescript
// Initialize OpenTelemetry BEFORE importing React
import { setupTelemetry } from './telemetry';
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';

// Setup telemetry
setupTelemetry();

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
```

**JavaScript**

In your `index.js`:

```javascript
// Initialize OpenTelemetry BEFORE importing React
const { setupTelemetry } = require("./telemetry");
const React = require("react");
const ReactDOM = require("react-dom/client");

// Setup telemetry
setupTelemetry();

// Rest of your React app initialization
```

## What Gets Instrumented

The setup automatically captures:

1. **Document Load Performance** - Page load times and resource timings
2. **Fetch Requests** - HTTP requests made by your application
3. **User Interactions** - Click and submit events
4. **Navigation** - Route changes and page transitions

## Features

- **Automatic client ID generation** - Unique browser fingerprint for session tracking
- **Environment-based configuration** - Different tokens for dev/prod environments
- **CORS-enabled tracing** - Propagates trace headers across domains
- **Performance optimized** - Batched exports and configurable timing
- **Error handling** - Graceful degradation if configuration is missing

## Notes

:::caution[Import Order]
Make sure to import the telemetry script **before** any React packages are imported to ensure proper instrumentation.
:::

Once you run the React application, it will start sending telemetry data to Last9.
