---
title: "Sazabi Adapter"
description: "Send wide events to Sazabi over OTLP/HTTP. The log stream's intake URL carries its key, so the only setting is one environment variable."
canonical_url: "https://www.evlog.dev/integrate/adapters/cloud/sazabi"
---
# Sazabi Adapter

> Send wide events to Sazabi over OTLP/HTTP. The log stream's intake URL carries its key, so the only setting is one environment variable.

[Sazabi](https://sazabi.com) is AI-native log search and incident investigation. The Sazabi adapter sends your wide events to a Sazabi log stream over **OTLP/HTTP**, so every field you set with `log.set()` arrives as a searchable attribute.

<prompt :actions="["copy","cursor","claude"]" description="Add the Sazabi drain adapter" icon="i-custom-sazabi">

Add the Sazabi drain adapter to send evlog wide events to Sazabi.

1. Identify which framework I'm using and follow its evlog integration pattern
2. Install evlog if not already installed
3. Import createSazabiDrain from 'evlog/sazabi'
4. Wire createSazabiDrain() into my framework's drain configuration
5. Set SAZABI_INTAKE_URL environment variable in .env
6. Test by triggering a request and checking the log stream in Sazabi

Adapter docs: [https://www.evlog.dev/integrate/adapters/cloud/sazabi](https://www.evlog.dev/integrate/adapters/cloud/sazabi)
Framework setup: [https://www.evlog.dev/integrate/frameworks/overview](https://www.evlog.dev/integrate/frameworks/overview)

</prompt>

## Installation

The Sazabi adapter comes bundled with evlog:

```typescript [src/index.ts]
import { createSazabiDrain } from 'evlog/sazabi'
```

## Quick Start

### 1. Copy your intake URL

1. In Sazabi, open **Log streams** and create or open an **OpenTelemetry** log stream
2. Copy its **Intake URL**. The stream's public key is part of the URL, so there is no separate API key to set

### 2. Set environment variables

```bash [.env]
SAZABI_INTAKE_URL=https://<key>.us-west-2.intake.sazabi.com
```

### 3. Wire the drain to your framework

<framework-tabs>

```typescript [Nuxt]server/plugins/evlog-drain.ts
import { createSazabiDrain } from 'evlog/sazabi'

export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:drain', createSazabiDrain())
})
```

```typescript [Nitro]server/plugins/evlog-drain.ts
import { definePlugin } from 'nitro'
import { createSazabiDrain } from 'evlog/sazabi'

export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:drain', createSazabiDrain())
})
```

```typescript [Next.js]lib/evlog.ts
import { createEvlog } from 'evlog/next'
import { createSazabiDrain } from 'evlog/sazabi'

export const { withEvlog, useLogger, log, createError } = createEvlog({
  service: 'my-app',
  drain: createSazabiDrain(),
})
```

```typescript [SvelteKit]src/hooks.server.ts
import { createEvlogHooks } from 'evlog/sveltekit'
import { createSazabiDrain } from 'evlog/sazabi'

export const { handle, handleError } = createEvlogHooks({ drain: createSazabiDrain() })
```

```typescript [TanStack Start]server/plugins/evlog-drain.ts
import { definePlugin } from 'nitro'
import { createSazabiDrain } from 'evlog/sazabi'

export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:drain', createSazabiDrain())
})
```

```typescript [React Router]app/root.tsx
import { evlog } from 'evlog/react-router'
import { createSazabiDrain } from 'evlog/sazabi'

export const middleware: Route.MiddlewareFunction[] = [
  evlog({ drain: createSazabiDrain() }),
]
```

```typescript [Hono]src/index.ts
import { createSazabiDrain } from 'evlog/sazabi'

app.use(evlog({ drain: createSazabiDrain() }))
```

```typescript [Express]src/index.ts
import { createSazabiDrain } from 'evlog/sazabi'

app.use(evlog({ drain: createSazabiDrain() }))
```

```typescript [Fastify]src/index.ts
import { createSazabiDrain } from 'evlog/sazabi'

await app.register(evlog, { drain: createSazabiDrain() })
```

```typescript [Elysia]src/index.ts
import { createSazabiDrain } from 'evlog/sazabi'

app.use(evlog({ drain: createSazabiDrain() }))
```

```typescript [NestJS]src/app.module.ts
import { createSazabiDrain } from 'evlog/sazabi'

EvlogModule.forRoot({ drain: createSazabiDrain() })
```

```typescript [oRPC]server/orpc.ts
import { withEvlog } from 'evlog/orpc'
import { createSazabiDrain } from 'evlog/sazabi'

const handler = withEvlog(new RPCHandler(router), { drain: createSazabiDrain() })
```

```typescript [Cloudflare Workers]src/worker.ts
import { withEvlog } from 'evlog/workers'
import { createSazabiDrain } from 'evlog/sazabi'

export default withEvlog(handler, { drain: createSazabiDrain() })
```

```typescript [Standalone]
import { createSazabiDrain } from 'evlog/sazabi'

initLogger({ drain: createSazabiDrain() })
```

</framework-tabs>

Trigger a request and the wide event appears in the log stream within a few seconds.

## Configuration

The adapter reads configuration from multiple sources (highest priority first):

1. **Overrides** passed to `createSazabiDrain()`
2. **Runtime config** at `runtimeConfig.evlog.sazabi` or `runtimeConfig.sazabi` (Nuxt/Nitro only)
3. **Environment variables** (`SAZABI_*`)

### Environment Variables

<table>
<thead>
  <tr>
    <th>
      Variable
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        SAZABI_INTAKE_URL
      </code>
    </td>
    
    <td>
      Intake URL of the log stream. evlog posts to <code>
        <intake URL>/v1/logs
      </code>
      
      ; a URL that already ends with <code>
        /v1/logs
      </code>
      
       is used as is
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        SAZABI_SERVICE_NAME
      </code>
    </td>
    
    <td>
      Override <code>
        service.name
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        OTEL_SERVICE_NAME
      </code>
    </td>
    
    <td>
      Fallback for <code>
        service.name
      </code>
      
       (same as the OTLP adapter)
    </td>
  </tr>
</tbody>
</table>

### Runtime Config (Nuxt only)

```typescript [nuxt.config.ts]
export default defineNuxtConfig({
  runtimeConfig: {
    sazabi: {
      // endpoint: '', // Set via SAZABI_INTAKE_URL
    },
  },
})
```

### Override Options

Pass options directly to override any configuration:

<framework-tabs>

```typescript [Nuxt]server/plugins/evlog-drain.ts
import { createSazabiDrain } from 'evlog/sazabi'

export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:drain', createSazabiDrain({
    serviceName: 'checkout-api',
    resourceAttributes: { 'deployment.region': 'us-west-2' },
  }))
})
```

```typescript [Nitro]server/plugins/evlog-drain.ts
import { definePlugin } from 'nitro'
import { createSazabiDrain } from 'evlog/sazabi'

export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:drain', createSazabiDrain({
    serviceName: 'checkout-api',
    resourceAttributes: { 'deployment.region': 'us-west-2' },
  }))
})
```

```typescript [Next.js]lib/evlog.ts
import { createEvlog } from 'evlog/next'
import { createSazabiDrain } from 'evlog/sazabi'

export const { withEvlog, useLogger, log, createError } = createEvlog({
  service: 'my-app',
  drain: createSazabiDrain({
    serviceName: 'checkout-api',
    resourceAttributes: { 'deployment.region': 'us-west-2' },
  }),
})
```

```typescript [SvelteKit]src/hooks.server.ts
import { createEvlogHooks } from 'evlog/sveltekit'
import { createSazabiDrain } from 'evlog/sazabi'

export const { handle, handleError } = createEvlogHooks({
  drain: createSazabiDrain({
    serviceName: 'checkout-api',
    resourceAttributes: { 'deployment.region': 'us-west-2' },
  }),
})
```

```typescript [TanStack Start]server/plugins/evlog-drain.ts
import { definePlugin } from 'nitro'
import { createSazabiDrain } from 'evlog/sazabi'

export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:drain', createSazabiDrain({
    serviceName: 'checkout-api',
    resourceAttributes: { 'deployment.region': 'us-west-2' },
  }))
})
```

```typescript [React Router]app/root.tsx
import { evlog } from 'evlog/react-router'
import { createSazabiDrain } from 'evlog/sazabi'

export const middleware: Route.MiddlewareFunction[] = [
  evlog({
    drain: createSazabiDrain({
      serviceName: 'checkout-api',
      resourceAttributes: { 'deployment.region': 'us-west-2' },
    }),
  }),
]
```

```typescript [Hono]src/index.ts
import { evlog } from 'evlog/hono'
import { createSazabiDrain } from 'evlog/sazabi'

app.use(evlog({
  drain: createSazabiDrain({
    serviceName: 'checkout-api',
    resourceAttributes: { 'deployment.region': 'us-west-2' },
  }),
}))
```

```typescript [Express]src/index.ts
import { evlog } from 'evlog/express'
import { createSazabiDrain } from 'evlog/sazabi'

app.use(evlog({
  drain: createSazabiDrain({
    serviceName: 'checkout-api',
    resourceAttributes: { 'deployment.region': 'us-west-2' },
  }),
}))
```

```typescript [Fastify]src/index.ts
import { evlog } from 'evlog/fastify'
import { createSazabiDrain } from 'evlog/sazabi'

await app.register(evlog, {
  drain: createSazabiDrain({
    serviceName: 'checkout-api',
    resourceAttributes: { 'deployment.region': 'us-west-2' },
  }),
})
```

```typescript [Elysia]src/index.ts
import { evlog } from 'evlog/elysia'
import { createSazabiDrain } from 'evlog/sazabi'

app.use(evlog({
  drain: createSazabiDrain({
    serviceName: 'checkout-api',
    resourceAttributes: { 'deployment.region': 'us-west-2' },
  }),
}))
```

```typescript [NestJS]src/app.module.ts
import { EvlogModule } from 'evlog/nestjs'
import { createSazabiDrain } from 'evlog/sazabi'

EvlogModule.forRoot({
  drain: createSazabiDrain({
    serviceName: 'checkout-api',
    resourceAttributes: { 'deployment.region': 'us-west-2' },
  }),
})
```

```typescript [oRPC]server/orpc.ts
import { withEvlog } from 'evlog/orpc'
import { createSazabiDrain } from 'evlog/sazabi'

const handler = withEvlog(new RPCHandler(router), {
  drain: createSazabiDrain({
    serviceName: 'checkout-api',
    resourceAttributes: { 'deployment.region': 'us-west-2' },
  }),
})
```

```typescript [Cloudflare Workers]src/worker.ts
import { withEvlog } from 'evlog/workers'
import { createSazabiDrain } from 'evlog/sazabi'

export default withEvlog(handler, {
  drain: createSazabiDrain({
    serviceName: 'checkout-api',
    resourceAttributes: { 'deployment.region': 'us-west-2' },
  }),
})
```

```typescript [Standalone]src/index.ts
import { initLogger } from 'evlog'
import { createSazabiDrain } from 'evlog/sazabi'

initLogger({
  drain: createSazabiDrain({
    serviceName: 'checkout-api',
    resourceAttributes: { 'deployment.region': 'us-west-2' },
  }),
})
```

</framework-tabs>

<table>
<thead>
  <tr>
    <th>
      Option
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        endpoint
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        SAZABI_INTAKE_URL
      </code>
    </td>
    
    <td>
      Intake URL of the log stream
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        serviceName
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        event.service
      </code>
    </td>
    
    <td>
      Override <code>
        service.name
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        resourceAttributes
      </code>
    </td>
    
    <td>
      <code>
        object
      </code>
    </td>
    
    <td>
      -
    </td>
    
    <td>
      Additional OTLP resource attributes
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        timeout
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        5000
      </code>
    </td>
    
    <td>
      Request timeout in milliseconds
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        retries
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        2
      </code>
    </td>
    
    <td>
      Retry attempts on transient failures
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        recordShape
      </code>
    </td>
    
    <td>
      <code>
        'compact' | 'json'
      </code>
    </td>
    
    <td>
      <code>
        'compact'
      </code>
    </td>
    
    <td>
      Log record shape: a one-line summary body with dotted attributes, or the serialized event as the body
    </td>
  </tr>
</tbody>
</table>

## How It Works

The adapter is a thin layer over the [OTLP adapter](https://www.evlog.dev/integrate/adapters/hybrid/otlp): it normalizes the intake URL and sends the batch with the same OTLP/JSON encoding.

- The event's `service`, `environment`, `version`, `region` and `commitHash` become resource attributes
- `level` maps to the OTLP severity
- Records use the compact shape: the body is a one-line summary such as `POST /api/checkout (500)`
- Every other field of the wide event becomes a log attribute, with nested fields flattened to dotted keys, so `user.plan` or `cart.total` can be filtered and grouped in Sazabi

## Querying logs in Sazabi

Open the log stream and ask a question in plain language, for example "how many checkout requests failed in the last hour?", or filter on any field the wide event carries.

## Troubleshooting

### Missing endpoint error

```text
[evlog/sazabi] Missing endpoint. Set SAZABI_INTAKE_URL, or pass to createSazabiDrain()
```

Set `SAZABI_INTAKE_URL` in the environment the server runs in, or pass `endpoint` to `createSazabiDrain()`.

### 4xx from the intake

Copy the intake URL again from the log stream: a truncated host or an old stream's URL is rejected.

## Direct API Usage

Send events without the drain, for scripts and jobs:

```typescript [scripts/send.ts]
import { sendBatchToSazabi, sendToSazabi } from 'evlog/sazabi'

await sendToSazabi(event, { endpoint: process.env.SAZABI_INTAKE_URL! })
await sendBatchToSazabi(events, { endpoint: process.env.SAZABI_INTAKE_URL! })
```

## Next Steps

- [OTLP Adapter](https://www.evlog.dev/integrate/adapters/hybrid/otlp) for the encoding this adapter reuses
- [Drain Pipeline](https://www.evlog.dev/extend/drain-pipeline) for batching, retries and buffering
- [Adapters Overview](https://www.evlog.dev/integrate/adapters/overview) for the other destinations

---

- [Sazabi](https://sazabi.com)
- [OTLP Adapter](https://www.evlog.dev/integrate/adapters/hybrid/otlp)


## Sitemap

See the full [sitemap](https://www.evlog.dev/sitemap.md) for all pages.
