npm package: @pgms/radianz-pino-transport
Description: Pino transport for Radianz — send structured logs directly to the Radianz backend
Author: PGMS
Latest version: 1.0.0

# @pgms/radianz-pino-transport

A [Pino](https://getpino.io) transport for **[Radianz](https://radianz.io)** — developer activity analytics & observability.

Sends structured logs directly to the Radianz backend over HTTP, aligned with the existing [Serilog.Sinks.Radianz](https://github.com/PGMS-Dev/Serilog.Sinks.Radianz) ingestion contract.

## Features

| Feature | Description |
|---|---|
| **Async Batching** | Logs are buffered and sent in configurable batches (default 50 every 5 s) |
| **Retry with Back-off** | Exponential back-off + jitter — survives transient failures |
| **Drop Policy** | Configurable in-memory queue size; oldest events dropped when full |
| **Structured Logging** | Full support for Pino properties, error serialisation & error cause chains |
| **CJS + ESM** | Dual-format build with TypeScript declarations |
| **Node 18+** | Uses `undici` for fast HTTP, zero native dependencies |

## Installation

```bash
npm install @pgms/radianz-pino-transport pino
```

## Quick Start

```ts
import pino from "pino";
import { radianzTransport } from "@pgms/radianz-pino-transport";

const logger = pino(
  { level: "info" },
  radianzTransport({
    endpoint: "https://radianz.mycompany.com/api/v1/logs/batch",
    clientId: process.env.RADIANZ_CLIENT_ID!,
    apiKey: process.env.RADIANZ_API_KEY!,
    serviceName: "my-service",
    serviceVersion: "1.2.3",
    environment: "production",
  }),
);

logger.info({ userId: 42 }, "User signed in");
logger.error({ err: new Error("boom") }, "Something broke");
```

## Configuration

| Option | Type | Default | Description |
|---|---|---|---|
| `endpoint` | `string` | *required* | Radianz ingestion URL (`/api/v1/logs/batch`) |
| `clientId` | `string` | *required* | Client ID for authentication |
| `apiKey` | `string` | *required* | API key for authentication |
| `serviceName` | `string` | — | Application / service name |
| `serviceVersion` | `string` | — | Application version |
| `environment` | `string` | — | Environment (e.g. `prod`, `staging`) |
| `flushIntervalMs` | `number` | `5000` | Max time between HTTP flushes |
| `maxBatchSize` | `number` | `50` | Events per HTTP request |
| `maxQueueSize` | `number` | `10000` | In-memory queue limit (FIFO drop) |
| `timeoutMs` | `number` | `30000` | HTTP request timeout |
| `maxRetries` | `number` | `3` | Retry attempts for transient failures |
| `baseRetryDelayMs` | `number` | `1000` | Initial retry backoff |
| `maxRetryDelayMs` | `number` | `30000` | Maximum retry backoff |
| `headers` | `Record<string, string>` | `{}` | Additional HTTP headers |
| `onError` | `(err: Error) => void` | — | Error callback (apiKey never exposed) |

## Level Mapping

| Pino | Radianz (Serilog) |
|---|---|
| `trace` (10) | `Verbose` |
| `debug` (20) | `Debug` |
| `info` (30) | `Information` |
| `warn` (40) | `Warning` |
| `error` (50) | `Error` |
| `fatal` (60) | `Fatal` |

## Authentication

The transport sends `ClientId` and `ApiKey` as HTTP headers — identical to the Serilog sink. Obtain credentials from the Radianz dashboard.

## Best Practices

- Always set `serviceName`, `serviceVersion` and `environment` for proper grouping.
- Keep `maxBatchSize` between 50–200 and `flushIntervalMs` between 1–10 s.
- Use `onError` to monitor transport failures without crashing your app.
- The transport calls `timer.unref()` so it won't keep your process alive.

## FAQ

**Q: Does it block my application?**
No. Logs are queued in memory and flushed asynchronously.

**Q: What happens if the backend is down?**
Batches are retried with exponential backoff. After `maxRetries` failures the batch is dropped and `onError` is called.

**Q: Is the API key logged anywhere?**
No. The `onError` callback never receives the API key.

## License

MIT