> ## 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.

# Terminal Logger

> Print colorized, structured logs to the console behind the framework's ILogger interface.

`TerminalLogger` is the [Logger](/components/logger) component's console backend. It writes colorized, level-aware lines to `stdout`/`stderr` — with optional timestamps, symbols, structured data, and formatted stack traces — making it the natural choice for local development and CLI output. It implements the framework's `ILogger` interface, so call sites stay provider-agnostic and you can swap to a remote or database backend without touching the code that emits logs.

## Why Terminal Logger

* **Zero configuration.** No tokens, hosts, or connection strings — it works the moment you resolve it.
* **Colorized levels.** Each level (`ERROR`, `WARN`, `INFO`, `DEBUG`, `LOG`, `SUCCESS`) has its own color and symbol.
* **Structured data rendering.** Pass a `data` object and scalar fields are printed as colorized `key: value` pairs.
* **Exception-aware.** `error()` accepts an `IException` and renders its name, status, and a formatted stack trace.
* **Per-call display options.** Toggle `showArrow`, `showTimestamp`, `showLevel`, and `useSymbol` on any call.
* **Container-managed.** Registered with `@decorator.logger()` and resolved from the container.

## Installation

`TerminalLogger` ships with `@ooneex/logger`.

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

It uses `Bun.color` for ANSI colorization, so it runs under the Bun runtime with no extra dependencies.

## Environment variables

`TerminalLogger` needs no configuration — there are no environment variables to set.

## How it works

Each level method formats a single line and writes it to the console. The line is assembled from an arrow, a timestamp, the level (as `[LEVEL]` or a symbol), and the colorized message. Any scalar fields in `data` are appended as colorized `key: value` pairs, and when an `IException` is passed to `error()`, its structured stack trace is rendered frame by frame. `ERROR`-level lines (and `FATAL`) are written to `stderr`; everything else goes to `stdout`.

| Method                              | Purpose                                                    |
| ----------------------------------- | ---------------------------------------------------------- |
| `init()`                            | No-op; returns immediately.                                |
| `error(message, data?, options?)`   | Log an error. `message` may be a `string` or `IException`. |
| `warn(message, data?, options?)`    | Log a warning.                                             |
| `info(message, data?, options?)`    | Log informational output.                                  |
| `debug(message, data?, options?)`   | Log debug detail.                                          |
| `log(message, data?, options?)`     | Log a general message.                                     |
| `success(message, data?, options?)` | Log a success message.                                     |

The per-call display options (`LoggerOptionsType`):

| Option          | Type      | Purpose                                                                 |
| --------------- | --------- | ----------------------------------------------------------------------- |
| `showArrow`     | `boolean` | Show the leading `->` arrow. Defaults to `true`.                        |
| `showTimestamp` | `boolean` | Show the `YYYY-MM-DD HH:mm:ss` timestamp. Defaults to `true`.           |
| `showLevel`     | `boolean` | Show the level label. Defaults to `true`.                               |
| `useSymbol`     | `boolean` | Render the level as a symbol instead of `[LEVEL]`. Defaults to `false`. |

## Usage

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

const logger = container.get(TerminalLogger);

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 });

// Control terminal rendering per call
logger.debug("Cache miss", { key: "user:123" }, { useSymbol: true, showTimestamp: false });
```

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

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

const logger = container.get(TerminalLogger);

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

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

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

export class PaymentService {
  constructor(@inject(TerminalLogger) private readonly logger: TerminalLogger) {}

  public capture(order: Order) {
    this.logger.success("Payment captured", { orderId: order.id, amount: order.total });
  }
}
```

## Use in the app

In an `@ooneex/app` application, register `TerminalLogger` 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
```

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

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

await app.run();
```

`loggers` accepts an array, so you can register more than one backend at once — for example terminal output alongside a persistent backend:

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

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

Once registered, inject it into a service or controller and log as part of your domain logic:

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

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

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

Because `TerminalLogger` needs no configuration, it's ideal as the development default — swap to a remote or persistent backend (see [Better Stack Logger](/integrations/betterstack-logger), [Database Logger](/integrations/database-logger)) in production by changing which class you register.

## Best practices

* **Use it for local and CLI output.** It's the zero-config development default; ship to a remote or database backend in production.
* **Pass structured data, not interpolated strings.** Keep the message stable and put variable detail in the `data` object.
* **Log exceptions as exceptions.** Pass the `IException` to `error()` so the name, status, and stack trace are rendered.
* **Tune the display per call.** Use `options` to drop the timestamp or switch to symbols for compact CLI output.
* **Keep `data` scalar.** Only scalar fields (string, number, boolean, bigint) are rendered; nested objects are skipped.

See the [Logger component](/components/logger) for the provider interface and how logs flow through the framework.
