> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mirador.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Traces

> Understanding distributed traces in the Mirador SDK

## What is a Trace?

A **trace** represents a single logical operation or flow in your application. It's the fundamental unit of data in the Mirador platform. Traces capture:

* The operation name
* Attributes (key-value metadata)
* Tags (categorical labels)
* Events (timestamped milestones)
* Transaction hints (blockchain correlation)

## Creating a Trace

Create a trace using the `client.trace()` method:

```typescript theme={null}
const trace = client.trace({ name: 'UserCheckout' });
```

### Trace Options

**Web SDK:**

| Option            | Type      | Default          | Description                                                                        |
| ----------------- | --------- | ---------------- | ---------------------------------------------------------------------------------- |
| `name`            | `string`  | `undefined`      | Name identifying the trace                                                         |
| `traceId`         | `string`  | `undefined`      | Resume an existing trace by ID                                                     |
| `includeUserMeta` | `boolean` | `true`           | Include browser metadata                                                           |
| `maxRetries`      | `number`  | `2`              | Retry attempts on failure                                                          |
| `retryBackoff`    | `number`  | `500`            | Base backoff delay (ms)                                                            |
| `autoClose`       | `boolean` | `false`          | Automatically close trace on page unload                                           |
| `autoKeepAlive`   | `boolean` | `true` / `false` | Auto-start keep-alive timer (`true` for new traces, `false` when `traceId` is set) |

```typescript theme={null}
const trace = client.trace({
  name: 'CriticalOperation',
  includeUserMeta: true,
  maxRetries: 5
});
```

**Node.js SDK:**

| Option              | Type      | Default          | Description                                                                        |
| ------------------- | --------- | ---------------- | ---------------------------------------------------------------------------------- |
| `name`              | `string`  | `undefined`      | Name identifying the trace                                                         |
| `traceId`           | `string`  | `undefined`      | Resume an existing trace by ID (e.g., from frontend SDK)                           |
| `captureStackTrace` | `boolean` | `true`           | Capture stack trace at trace creation                                              |
| `maxRetries`        | `number`  | `2`              | Retry attempts on failure                                                          |
| `retryBackoff`      | `number`  | `500`            | Base backoff delay (ms)                                                            |
| `autoKeepAlive`     | `boolean` | `true` / `false` | Auto-start keep-alive timer (`true` for new traces, `false` when `traceId` is set) |

```typescript theme={null}
const trace = client.trace({
  name: 'BackendOperation',
  traceId: req.headers['x-mirador-trace-id'], // optional: resume frontend trace
  captureStackTrace: false
});
```

## Trace Lifecycle

<Steps>
  <Step title="Create">
    Call `client.trace()` to create a new trace builder
  </Step>

  <Step title="Build">
    Add attributes, tags, events, and transaction hints using the fluent API
  </Step>

  <Step title="Flush">
    First flush sends `FlushTrace` to the gateway
  </Step>

  <Step title="Update">
    Subsequent flushes send `FlushTrace` with new data
  </Step>
</Steps>

### Keep-Alive & Closing Traces

The SDK sends periodic keep-alive pings to the server to maintain trace liveness. The ping interval is configurable at the client level:

```typescript theme={null}
const client = new Client('your-api-key', {
  keepAliveIntervalMs: 15000  // Ping every 15 seconds (default: 10000)
});
```

#### Smart Defaults

The `autoKeepAlive` option controls whether the keep-alive timer starts automatically when a trace is created:

* **New traces** (`autoKeepAlive` defaults to `true`): the timer starts automatically after the first flush.
* **Resumed traces** (when `traceId` is set, `autoKeepAlive` defaults to `false`): the timer does **not** start automatically.

This prevents zombie keep-alive timers — for example, when a backend resumes a frontend trace just to append a few events and close it, there's no need for the backend to keep pinging.

```typescript theme={null}
// New trace — keep-alive starts automatically
const trace = client.trace({ name: 'UserSession' });

// Resumed trace — no keep-alive by default
const resumed = client.trace({
  traceId: req.headers['x-mirador-trace-id'],
  name: 'BackendHandler'
});

// Override the default: force keep-alive on a resumed trace
const resumedWithKeepalive = client.trace({
  traceId: req.headers['x-mirador-trace-id'],
  name: 'LongRunningBackend',
  autoKeepAlive: true
});
```

#### Manual Control

You can manually start or stop the keep-alive timer at any time using the `startKeepAlive()` and `stopKeepAlive()` methods on the trace. Both methods are idempotent — calling them multiple times is safe.

```typescript theme={null}
const trace = client.trace({ name: 'ManualControl', autoKeepAlive: false });

// Start keep-alive manually when needed
trace.startKeepAlive();

// Stop it when the trace is idle
trace.stopKeepAlive();
```

#### Resilience

The keep-alive timer is resilient to transient network issues:

* An **in-flight guard** prevents overlapping pings if a previous ping hasn't completed yet.
* On failure, the SDK performs a **single retry** before counting the attempt as failed.
* After **3 consecutive failures**, the timer automatically stops to avoid wasting resources.

#### Max Trace Lifetime

You can bound the maximum lifetime of a trace independently of keep-alive using the `maxTraceLifetimeMs` client option. When the lifetime is exceeded, the trace is automatically closed.

```typescript theme={null}
const client = new Client('your-api-key', {
  maxTraceLifetimeMs: 300000  // Auto-close traces after 5 minutes
});
```

<Note>
  `maxTraceLifetimeMs` defaults to `0` (disabled). When set, it applies to all traces created by the client.
</Note>

#### Closing Traces

Always close traces when you're done to clean up resources:

```typescript theme={null}
const trace = client.trace({ name: 'UserSession' });

try {
  // ... add events, attributes, etc ...
  await trace.close('Session ended');
} catch (error) {
  trace.addEvent('error', { message: error.message });
  await trace.close('Session failed');
}
```

Enable auto-close on page unload for browser applications:

```typescript theme={null}
const trace = client.trace({
  name: 'UserSession',
  autoClose: true  // Automatically close on page unload
});
```

## The Builder Pattern

The `Trace` class uses a fluent builder pattern. All methods return `this`, allowing you to chain calls:

```typescript theme={null}
const trace = client.trace({ name: 'SwapExecution' })
  .addAttribute('from', '0xabc...')
  .addAttribute('to', '0xdef...')
  .addTags(['dex', 'swap', 'ethereum'])
  .addEvent('quote_received', { price: '1.5' })
  .addEvent('user_approved');

trace.web3.evm.addTxHint('0x123...', 'ethereum');
trace.web3.safe.addMsgHint('0xabc...', 'ethereum', 'Multisig approval');
```

## Getting the Trace ID

In v2, the trace ID is generated client-side at creation time and is available immediately via `getTraceId()`:

```typescript theme={null}
const trace = client.trace({ name: 'MyTrace' });
const traceId = trace.getTraceId(); // Available immediately
console.log('Trace ID:', traceId);
```

## Client-Side Trace ID Generation

In v2, trace IDs are generated client-side at creation time as W3C Tracing Context compatible 32-character hex strings. The ID is available immediately via `getTraceId()` — no need to wait for the server response.

```typescript theme={null}
const trace = client.trace({ name: 'MyTrace' });
const traceId = trace.getTraceId(); // Available immediately

// Pass to backend before any network call
fetch('/api/action', {
  headers: { 'X-Mirador-Trace-Id': traceId }
});
```

This is especially important for cross-SDK trace sharing — the frontend can generate the ID and pass it to the backend via headers before any network call completes.

## Flushing

Both the Web SDK and Node.js SDK automatically batch and send trace data at the end of the current JavaScript tick (microtask). All flushes send idempotent `FlushTrace` requests.

You can also call `flush()` manually at any time to send pending data immediately:

```typescript theme={null}
const trace = client.trace({ name: 'MyTrace' })
  .addAttribute('key', 'value')
  .addEvent('started');

trace.flush();  // Send immediately instead of waiting for microtask
```

<Note>
  `flush()` is fire-and-forget — it returns `void` and maintains strict ordering of requests internally.
</Note>

## Cross-SDK Trace Sharing

You can share a trace between the frontend (Web SDK) and backend (Node.js SDK) so that both sides contribute data to the same trace. This is useful when a user action in the browser triggers a backend API call that you want to correlate.

### How It Works

1. The **frontend** creates a trace — the ID is available immediately via `getTraceId()`
2. The trace ID is passed to the **backend** (e.g., via an HTTP header) before any network call completes
3. The **backend** resumes the trace by passing `traceId` in `TraceOptions`

### Frontend (Web SDK)

```typescript theme={null}
const trace = client.trace({ name: 'TokenSwap' })
  .addAttribute('wallet', userAddress)
  .addEvent('swap:initiated');

// ID is available immediately — no flush needed first
const traceId = trace.getTraceId();

// Pass trace ID to backend via HTTP header
const response = await fetch('/api/swap', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Mirador-Trace-Id': traceId
  },
  body: JSON.stringify(swapParams)
});
```

### Backend (Node.js SDK)

```typescript theme={null}
import { Client } from '@miradorlabs/nodejs-sdk';

const mirador = new Client(process.env.MIRADOR_API_KEY);

app.post('/api/swap', async (req, res) => {
  // Resume the frontend trace
  const trace = mirador.trace({
    traceId: req.headers['x-mirador-trace-id'],
    name: 'SwapBackend'
  })
    .addAttribute('endpoint', '/api/swap')
    .addEvent('backend:received');

  try {
    const result = await executeSwap(req.body);
    trace.addEvent('backend:completed', { txHash: result.hash });
    await trace.close('success');
    res.json(result);
  } catch (error) {
    trace.addEvent('backend:error', { message: error.message });
    await trace.close('error');
    res.status(500).json({ error: error.message });
  }
});
```

### Backend → Frontend

You can also create a trace on the backend and resume it on the frontend:

```typescript theme={null}
// Backend creates the trace and returns the ID in the response
app.get('/api/session', async (req, res) => {
  const trace = mirador.trace({ name: 'UserSession' })
    .addAttribute('userId', req.user.id)
    .addEvent('session:started');

  // ID is available immediately
  const traceId = trace.getTraceId();

  res.json({ traceId });
});
```

```typescript theme={null}
// Frontend resumes the trace
const { traceId } = await fetch('/api/session').then(r => r.json());

const trace = client.trace({ name: 'UserSession', traceId })
  .addEvent('frontend:loaded');
```

## Best Practices

### Name Traces Descriptively

Use clear, action-oriented names:

```typescript theme={null}
// Good
client.trace({ name: 'UserSignup' })
client.trace({ name: 'TokenSwap' })
client.trace({ name: 'NFTMint' })

// Avoid
client.trace({ name: 'trace1' })
client.trace({ name: 'data' })
```

### One Trace Per Logical Operation

Create a new trace for each distinct user action or flow:

```typescript theme={null}
// User connects wallet
const connectTrace = client.trace({ name: 'WalletConnect' });

// User initiates swap (separate operation)
const swapTrace = client.trace({ name: 'TokenSwap' });
```

### Include Context Early

Add identifying attributes at trace creation:

```typescript theme={null}
const trace = client.trace({ name: 'Transfer' })
  .addAttribute('userId', currentUser.id)
  .addAttribute('sessionId', session.id)
  .addAttribute('walletAddress', wallet.address);
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Events" icon="flag" href="/concepts/events">
    Add timestamped events to traces
  </Card>

  <Card title="Attributes & Tags" icon="tags" href="/concepts/attributes-and-tags">
    Enrich traces with metadata
  </Card>
</CardGroup>
