# OpenTelemetry

import { Tabs, TabItem } from '@astrojs/starlight/components';





LiveStore has built-in support for OpenTelemetry.

## Usage with React

<Tabs>
  <TabItem label="otel.ts">

## `reference/opentelemetry/otel.ts`

```ts filename="reference/opentelemetry/otel.ts"
import { ZoneContextManager } from '@opentelemetry/context-zone'
import { W3CTraceContextPropagator } from '@opentelemetry/core'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { resourceFromAttributes } from '@opentelemetry/resources'
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web'

/**
 * Configure a browser tracer that preserves parent/child spans across async work.
 * Requires a zone.js runtime (e.g. provided by many frameworks) so the ZoneContextManager
 * can keep context during timers, promises, and event callbacks.
 */
export const makeTracer = (serviceName: string) => {
  const url = import.meta.env.VITE_OTEL_EXPORTER_OTLP_ENDPOINT
  const provider = new WebTracerProvider({
    spanProcessors:
      url !== undefined ? [new SimpleSpanProcessor(new OTLPTraceExporter({ url: `${url}/v1/traces` }))] : [],
    resource: resourceFromAttributes({ 'service.name': serviceName }),
  })

  provider.register({
    contextManager: new ZoneContextManager(),
    propagator: new W3CTraceContextPropagator(),
  })

  return provider.getTracer('livestore')
}

export const tracer = makeTracer('my-app')
```

</TabItem>
  <TabItem label="App.tsx">

## `reference/opentelemetry/app.tsx`

```tsx filename="reference/opentelemetry/app.tsx"
import type { FC } from 'react'
import { Suspense, useState } from 'react'
import { unstable_batchedUpdates as batchUpdates } from 'react-dom'

import { makeInMemoryAdapter } from '@livestore/adapter-web'
import { StoreRegistry } from '@livestore/livestore'
import { StoreRegistryProvider, useStore } from '@livestore/react'

import { tracer } from './otel.ts'
import { schema } from './schema.ts'

const adapter = makeInMemoryAdapter()
const suspenseFallback = <div>Loading...</div>

// ---cut---
const useAppStore = () =>
  useStore({
    storeId: 'otel-demo',
    schema,
    adapter,
    batchUpdates,
    otelOptions: { tracer },
  })

export const App: FC = () => {
  const [storeRegistry] = useState(() => new StoreRegistry())
  return (
    <Suspense fallback={suspenseFallback}>
      <StoreRegistryProvider storeRegistry={storeRegistry}>
        <AppContent />
      </StoreRegistryProvider>
    </Suspense>
  )
}

const AppContent: FC = () => {
  const _store = useAppStore()
  // Use the store in your components
  return <div>{/* Your app content */}</div>
}
```

### `reference/framework-integrations/react/schema.ts`

```ts filename="reference/framework-integrations/react/schema.ts"
import { defineMaterializer, Events, makeSchema, Schema, SessionIdSymbol, State } from '@livestore/livestore'

export const tables = {
  todos: State.SQLite.table({
    name: 'todos',
    columns: {
      id: State.SQLite.text({ primaryKey: true }),
      text: State.SQLite.text(),
      completed: State.SQLite.boolean({ default: false }),
      createdAt: State.SQLite.datetime(),
    },
  }),
  uiState: State.SQLite.clientDocument({
    name: 'UiState',
    schema: Schema.Struct({
      newTodoText: Schema.String,
      filter: Schema.Literal('all', 'active', 'completed'),
    }),
    default: { id: SessionIdSymbol, value: { newTodoText: '', filter: 'all' } },
  }),
} as const

export const events = {
  todoCreated: Events.synced({
    name: 'v1.TodoCreated',
    schema: Schema.Struct({ id: Schema.String, text: Schema.String, createdAt: Schema.Date }),
  }),
} as const

const materializers = State.SQLite.materializers(events, {
  [events.todoCreated.name]: defineMaterializer(events.todoCreated, ({ id, text, createdAt }) =>
    tables.todos.insert({ id, text, completed: false, createdAt }),
  ),
})

const state = State.SQLite.makeState({ tables, materializers })

export const schema = makeSchema({ events, state })
```

### `reference/opentelemetry/otel.ts`

```ts filename="reference/opentelemetry/otel.ts"
import { ZoneContextManager } from '@opentelemetry/context-zone'
import { W3CTraceContextPropagator } from '@opentelemetry/core'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { resourceFromAttributes } from '@opentelemetry/resources'
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web'

/**
 * Configure a browser tracer that preserves parent/child spans across async work.
 * Requires a zone.js runtime (e.g. provided by many frameworks) so the ZoneContextManager
 * can keep context during timers, promises, and event callbacks.
 */
export const makeTracer = (serviceName: string) => {
  const url = import.meta.env.VITE_OTEL_EXPORTER_OTLP_ENDPOINT
  const provider = new WebTracerProvider({
    spanProcessors:
      url !== undefined ? [new SimpleSpanProcessor(new OTLPTraceExporter({ url: `${url}/v1/traces` }))] : [],
    resource: resourceFromAttributes({ 'service.name': serviceName }),
  })

  provider.register({
    contextManager: new ZoneContextManager(),
    propagator: new W3CTraceContextPropagator(),
  })

  return provider.getTracer('livestore')
}

export const tracer = makeTracer('my-app')
```

### `reference/opentelemetry/schema.ts`

```ts filename="reference/opentelemetry/schema.ts"
export { schema } from '../framework-integrations/react/schema.ts'
```

</TabItem>
  <TabItem label="livestore.worker.ts">

## `reference/opentelemetry/livestore.worker.ts`

```ts filename="reference/opentelemetry/livestore.worker.ts"
import { makeWorker } from '@livestore/adapter-web/worker'

import { tracer } from './otel.ts'
import { schema } from './schema.ts'

makeWorker({ schema, otelOptions: { tracer } })
```

### `reference/framework-integrations/react/schema.ts`

```ts filename="reference/framework-integrations/react/schema.ts"
import { defineMaterializer, Events, makeSchema, Schema, SessionIdSymbol, State } from '@livestore/livestore'

export const tables = {
  todos: State.SQLite.table({
    name: 'todos',
    columns: {
      id: State.SQLite.text({ primaryKey: true }),
      text: State.SQLite.text(),
      completed: State.SQLite.boolean({ default: false }),
      createdAt: State.SQLite.datetime(),
    },
  }),
  uiState: State.SQLite.clientDocument({
    name: 'UiState',
    schema: Schema.Struct({
      newTodoText: Schema.String,
      filter: Schema.Literal('all', 'active', 'completed'),
    }),
    default: { id: SessionIdSymbol, value: { newTodoText: '', filter: 'all' } },
  }),
} as const

export const events = {
  todoCreated: Events.synced({
    name: 'v1.TodoCreated',
    schema: Schema.Struct({ id: Schema.String, text: Schema.String, createdAt: Schema.Date }),
  }),
} as const

const materializers = State.SQLite.materializers(events, {
  [events.todoCreated.name]: defineMaterializer(events.todoCreated, ({ id, text, createdAt }) =>
    tables.todos.insert({ id, text, completed: false, createdAt }),
  ),
})

const state = State.SQLite.makeState({ tables, materializers })

export const schema = makeSchema({ events, state })
```

### `reference/opentelemetry/otel.ts`

```ts filename="reference/opentelemetry/otel.ts"
import { ZoneContextManager } from '@opentelemetry/context-zone'
import { W3CTraceContextPropagator } from '@opentelemetry/core'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { resourceFromAttributes } from '@opentelemetry/resources'
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web'

/**
 * Configure a browser tracer that preserves parent/child spans across async work.
 * Requires a zone.js runtime (e.g. provided by many frameworks) so the ZoneContextManager
 * can keep context during timers, promises, and event callbacks.
 */
export const makeTracer = (serviceName: string) => {
  const url = import.meta.env.VITE_OTEL_EXPORTER_OTLP_ENDPOINT
  const provider = new WebTracerProvider({
    spanProcessors:
      url !== undefined ? [new SimpleSpanProcessor(new OTLPTraceExporter({ url: `${url}/v1/traces` }))] : [],
    resource: resourceFromAttributes({ 'service.name': serviceName }),
  })

  provider.register({
    contextManager: new ZoneContextManager(),
    propagator: new W3CTraceContextPropagator(),
  })

  return provider.getTracer('livestore')
}

export const tracer = makeTracer('my-app')
```

### `reference/opentelemetry/schema.ts`

```ts filename="reference/opentelemetry/schema.ts"
export { schema } from '../framework-integrations/react/schema.ts'
```

</TabItem>
</Tabs>