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

# Better Stack Logger

> Ship structured logs to Better Stack (Logtail) behind the framework's ILogger interface.

`BetterstackLogger` is the [Logger](/components/logger) component's [Better Stack](https://betterstack.com) backend. It wraps the official `@logtail/node` client to ship structured logs to Better Stack's Logs (Logtail) platform, attaching your `data` object to every entry and flattening exception details onto the payload. It implements the framework's `ILogger` interface, so call sites stay provider-agnostic and you can swap the backend without touching the code that emits logs.

## Why Better Stack

* **Hosted log management.** Ship logs to Better Stack for search, dashboards, and alerting without running your own pipeline.
* **Structured payloads.** Pass a `data` object and it's sent alongside the message as structured fields.
* **Exception-aware.** `error()` accepts an `IException` and flattens its name, status, and stack trace onto the payload.
* **Configurable ingest host.** Point at a custom ingesting endpoint or use the Logtail default.
* **Buffered delivery.** The client batches logs and `flush()` drains the buffer before the process exits.
* **Container-managed.** Registered with `@decorator.logger()` and resolved from the container.

## Installation

`BetterstackLogger` ships with `@ooneex/logger` and depends on the Logtail Node client.

```bash theme={null}
bun add @ooneex/logger @logtail/node
```

## Environment variables

| Variable                            | Required | Purpose                                                                     |
| ----------------------------------- | -------- | --------------------------------------------------------------------------- |
| `BETTERSTACK_LOGGER_SOURCE_TOKEN`   | Yes      | Better Stack source token. Missing throws `LoggerException` (`LOG_FAILED`). |
| `BETTERSTACK_LOGGER_INGESTING_HOST` | No       | Custom ingesting endpoint. Omitted uses the Logtail default.                |

```bash theme={null}
BETTERSTACK_LOGGER_SOURCE_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxx
# Optional: point at your source's ingesting host
BETTERSTACK_LOGGER_INGESTING_HOST=https://s1234567.eu-nbg-2.betterstackdata.com
```

The source token is validated when `BetterstackLogger` is constructed, so a missing token fails fast at startup.

## How it works

On construction, the backend creates a single `Logtail` client with your source token (and endpoint, if set). Each level method forwards the message and `data` to the matching Logtail method — `error`, `warn`, `info`, `debug` — while `log` and `success` map to `info` with a `level` field (`LOG` / `SUCCESS`) so the original level is preserved. When an `IException` is passed to `error()`, its name, status, and JSON stack trace are extracted and merged into the payload. Delivery is buffered; call `flush()` to drain it.

| Method                    | Purpose                                                          |
| ------------------------- | ---------------------------------------------------------------- |
| `init()`                  | No-op; returns immediately.                                      |
| `error(message, data?)`   | Ship an error. `message` may be a `string` or `IException`.      |
| `warn(message, data?)`    | Ship a warning.                                                  |
| `info(message, data?)`    | Ship informational output.                                       |
| `debug(message, data?)`   | Ship debug detail.                                               |
| `log(message, data?)`     | Ship a general message (sent as `info` with `level: "LOG"`).     |
| `success(message, data?)` | Ship a success message (sent as `info` with `level: "SUCCESS"`). |
| `flush()`                 | Drain the buffered logs.                                         |

## Usage

```typescript theme={null}
import { container } from "@ooneex/container";
import { BetterstackLogger } from "@ooneex/logger";

const logger = container.get(BetterstackLogger);

logger.info("User signed in", { userId: "123", email: "john@example.com" });
logger.success("Payment captured", { orderId: "ord_456", amount: 4999 });
logger.warn("Rate limit approaching", { remaining: 5 });

// Flush before the process exits
await logger.flush();
```

`error()` accepts a plain message or an `IException` — when given an exception it flattens the name, status, and stack trace onto the payload:

```typescript theme={null}
import { container } from "@ooneex/container";
import { BetterstackLogger } from "@ooneex/logger";
import type { IException } from "@ooneex/exception";

const logger = container.get(BetterstackLogger);

try {
  await processPayment(order);
} catch (error) {
  logger.error(error as IException, { orderId: order.id });
}
```

Inject it into a service to ship logs as part of your domain logic:

```typescript theme={null}
import { inject } from "@ooneex/container";
import { BetterstackLogger } from "@ooneex/logger";

export class CheckoutService {
  constructor(@inject(BetterstackLogger) private readonly logger: BetterstackLogger) {}

  public completeOrder(userId: string, order: Order) {
    this.logger.success("Order completed", { userId, orderId: order.id });
  }
}
```

## Use in the app

In an `@ooneex/app` application, register `BetterstackLogger` by adding it to the `loggers` array of your `App` config. The `loggers` slot of `AppConfigType` is typed as `LoggerClassType[]`, so you pass the class itself — the framework registers it with the container at startup.

```bash theme={null}
bun add @ooneex/app @ooneex/logger @logtail/node
```

```typescript theme={null}
import { App } from "@ooneex/app";
import { BetterstackLogger } from "@ooneex/logger";

const app = new App({
  routing: { prefix: "/api" },
  loggers: [BetterstackLogger],
});

await app.run();
```

`loggers` accepts an array, so you can ship to Better Stack while also printing to the terminal in development:

```typescript theme={null}
import { App } from "@ooneex/app";
import { BetterstackLogger, TerminalLogger } from "@ooneex/logger";

const app = new App({
  routing: { prefix: "/api" },
  loggers: [TerminalLogger, BetterstackLogger],
});
```

Set `BETTERSTACK_LOGGER_SOURCE_TOKEN` (and optionally `BETTERSTACK_LOGGER_INGESTING_HOST`) in your `.env.yml` (or environment), then inject the logger into a service or controller:

```typescript theme={null}
import { inject } from "@ooneex/container";
import { BetterstackLogger } from "@ooneex/logger";

export class CheckoutService {
  constructor(@inject(BetterstackLogger) private readonly logger: BetterstackLogger) {}

  public async completeOrder(userId: string, order: Order) {
    // ... persist the order ...
    this.logger.success("Order completed", { userId, orderId: order.id });
  }
}
```

Because delivery is buffered, flush the client before the process exits — for example from a shutdown hook:

```typescript theme={null}
import { container } from "@ooneex/container";
import { BetterstackLogger } from "@ooneex/logger";

process.on("SIGTERM", async () => {
  await container.get(BetterstackLogger).flush();
  process.exit(0);
});
```

## Exceptions

`BetterstackLogger` throws `LoggerException` on misconfiguration, carrying a machine-readable `key`.

| Key          | When                                                                          |
| ------------ | ----------------------------------------------------------------------------- |
| `LOG_FAILED` | `BetterstackLogger` is constructed without `BETTERSTACK_LOGGER_SOURCE_TOKEN`. |

```typescript theme={null}
import { LoggerException } from "@ooneex/logger";

try {
  const logger = container.get(BetterstackLogger);
} catch (error) {
  if (error instanceof LoggerException && error.key === "LOG_FAILED") {
    console.error("Better Stack is missing its source token", error.data);
  }
  throw error;
}
```

## Best practices

* **Call `flush()` on exit.** Delivery is buffered; drain it before the process terminates so you don't drop the last logs.
* **Pass structured data, not interpolated strings.** Keep the message stable and put variable detail in the `data` object so logs stay searchable.
* **Log exceptions as exceptions.** Pass the `IException` to `error()` so the name, status, and stack trace are captured.
* **Keep the source token in the environment.** Load it from `.env`; never hard-code it.
* **Match the ingest host to your source.** Set `BETTERSTACK_LOGGER_INGESTING_HOST` when your source uses a region-specific endpoint.

For error and exception tracking specifically, see [Better Stack Exceptions](/integrations/betterstack-exception-logger). See the [Logger component](/components/logger) for the provider interface and how logs flow through the framework.
