# Logs Component

The `Logs` component is a full-screen, real-time log **viewer** for the terminal. It buffers
structured `Record`s, applies live filters (severity, channel, text), and renders a status bar,
a tailing log pane and a keybindings footer — the dashboard you see when an `HTTP_Server_CLI`
runs in **Monitor** mode.

It is not a logger: it never produces records, it consumes them. Records arrive as
newline-delimited JSON (one per line) — typically drained from the IPC pipe that the master and
its workers write to via `ACI\Logs\Handlers\Pipe`.

## Watch logs from a running server

The component is driven by `TCP_Server_CLI::monitoring()` (inherited by `HTTP_Server_CLI`), so the
common path is simply starting a server in Monitor mode — no manual wiring:

```bash :toolbar="true";
bootgly project Demo/HTTP_Server_CLI start -m
```

The server enters the alternate screen, points every `Logger` at the Monitor pipe, and feeds the
viewer in a non-blocking loop. You get a live, filterable dashboard until you press `q`/`Esc`.

The same screen is also reachable **from any other terminal, against any server mode** — the
[`bootgly logs -f`](/guide/logs/overview/) command attaches to a running instance's live tap and
drives this exact component through `CLI\UX\Components\Tail` (same filters, same keys). The detail
view additionally shows the record's **provenance** and **instance** (its `project` and `instance`
fields).

## Drive it yourself

If you build your own monitoring loop, the cycle is: feed bytes, read a key, render a frame.

```php
use const Bootgly\CLI;
use Bootgly\CLI\UI\Components\Logs;

$Input = CLI->Terminal->Input;
$Output = CLI->Terminal->Output;

$Viewer = new Logs($Input, $Output, max: 5000);

// In a non-blocking loop:
$Viewer->feed($chunk);            // newline-delimited JSON drained from the log pipe
$running = $Viewer->control($key); // a raw keystroke (returns false on quit)
$Viewer->render();                 // draw one full frame
```

`feed()` carries over an incomplete trailing line between calls, so partial pipe reads are safe.
`control()` returns `false` only when the user quits (`q`, or `Esc` while live) — your loop owns
the alternate-screen enter/leave and terminal restore.

## Filter and navigate

`control()` maps the keys below. The same keys are surfaced in the footer the component renders:

| Key | Action |
|---|---|
| `l` | cycle the **severity threshold** (Debug → … → Emergency) |
| `1`–`9` | toggle a **channel** on/off (numbered in the status bar) |
| `/` | **search** — type to filter messages, `Enter`/`Esc` to keep it |
| `space` | **pause** — freezes a snapshot; new logs keep buffering, the view stays put |
| `↑`/`↓`, `PgUp`/`PgDn` | **select** a record (pauses to navigate the frozen snapshot) |
| `Enter` | **expand** the selected record — full detail (every line, `context`, `extra`), folded to the terminal width |
| `Home`/`End` | jump to the oldest / back to the live tail (in the detail view: top / bottom of the record) |
| `q` / `Esc` | leave the viewer |

Multiline messages — exceptions, stack traces — are **collapsed to a single line** with a `⏎N`
marker so a trace never floods the pane. Select the record and press `Enter` to read the whole
thing in a scrollable detail view.

The detail view never cuts a row: long message lines fold at the terminal width (re-folded on
every render — the Monitor loop and `logs -f` refresh the width on resize), `context` and `extra`
print one key per line hanging under their label, and the bar counts the folded rows
(`a–b of N lines` while there is more to scroll). `↑`/`↓` and `PgUp`/`PgDn` scroll it,
`Home`/`End` jump to the top/bottom, `Esc`/`Enter`/`q` go back. Only the list truncates (one row
per record).

While **paused**, rendering uses a frozen snapshot of the buffer (`$paused` true), so the screen
does not move as new records stream in. `End` (or `space`) resumes the live tail.

## See it live

The official Logs demo runs in the [live showcase](/manual/CLI/UI/Components/Logs/showcase) — real framework code on PHP 8.4 WebAssembly, in your browser, straight from this page.

## Reference

```php
public function __construct (Input $Input, Output $Output, int $max = 5000)
```

Build a viewer bound to a terminal `Input`/`Output`. `$max` is the ring-buffer capacity in
records (oldest dropped past the cap). The loop is expected to configure `Input` for non-blocking
raw reads.

```php
public function feed (string $chunk): void
```

Append raw pipe bytes (newline-delimited JSON records). Complete lines are decoded via
`Record::import()` and buffered; an incomplete trailing fragment is carried to the next call.

```php
public function control (string $key): bool
```

Handle one keystroke — mutates filter, selection, search and detail state. Returns `false` to
quit the viewer, `true` to keep running.

```php
public function render (): void
```

Draw one full frame: the expanded detail view (rows folded to the terminal width) when a record is
selected, otherwise the status bar + windowed log pane + footer. Uses cursor-home + per-line clear-to-EOL to avoid flicker.

### State (public)

- `$Records` — the live ring buffer (`array<int,Record>`).
- `$level` — current severity threshold (`Levels`, default `Levels::Debug` = show all).
- `$channels` — `channel => bool` visibility map (absent channel = visible).
- `$search` — active message substring (case-insensitive).
- `$paused`, `$searching` — current sub-mode flags.
- `$cursor` — selected record index in the visible list while paused.
- `$Detail` — the expanded `Record` (detail view), or `null` in list mode (read-only).

### Layering

`Logs` lives in `CLI/UI/Components` and consumes `ACI\Logs\{Record, Data\Levels}` plus
`CLI\Terminal` for sizing/keys — it depends downward only. The transport (`ACI\Logs\Handlers\Pipe`)
and the monitoring loop (`WPI`) sit on opposite sides of it and never reach back in.
