<!-- Source: https://medius.k4tech.net/library/diagnostics -->
# Diagnostics

_Read-only views of the link_

`logs` and `counters` are lock-free, read-only views on [`Device`](/library/connection.md) and [`AsyncDevice`](/library/features/async.md).

See also: [testing with MockBox](/library/guides/testing.md#testing).

## logs

_Stream of box messages_

```text
fn logs(&self) -> LogStream
```

_No round-trip_

Yields each box [`LOG`](/native/commands/admin.md#log) frame as a [`LogLine`](/library/types/structs.md#log-line).

#### EXAMPLE

```rust
for line in device.logs() {
    println!("[{:?}] {}", line.level, line.text);
}
// the loop ends here when the link drops
```

> **Note**
>
> The box emits [`LOG`](/native/commands/admin.md#log) frames only while the control link is up; with no control PC attached they are dropped, not buffered.

## Reading the stream

_Blocking, polling, and draining_

#### METHODS

| Method | Returns | Blocks | Description |
| --- | --- | --- | --- |
| `recv()` | `Result<LogLine>` | Yes | Waits for the next line. `Err` is [`Error::Disconnected`](/library/types/errors.md) once the link is gone. |
| `try_recv()` | `Option<LogLine>` | No | One queued line, or `None` if nothing is waiting. |
| `recv_timeout(d)` | `Option<LogLine>` | Up to `d` | The next line within the window, or `None` on timeout. |
| `try_iter()` | `impl Iterator` | No | Drains every line queued right now, then stops. |
| `recv_async().await` | `Result<LogLine>` | Awaits | Await the next line (`async` feature), runtime-agnostic. |
| `for line in stream` | `LogLine` | Yes | Blocking `IntoIterator`; yields each line until the link closes. |

`try_iter()` or `try_recv()` for a per-frame loop; `recv()` or the blocking iterator for a dedicated log thread.

#### EXAMPLE

```rust
let stream = device.logs();

// once per frame: drain whatever is queued, never blocking
for line in stream.try_iter() {
    println!("[{:?}] {}", line.level, line.text);
}

// or wait a bounded window for the next line
if let Some(line) = stream.recv_timeout(Duration::from_millis(50)) {
    println!("got {}", line.text);
} else {
    // no log this window, carry on
}
```

> **Warning**
>
> The channel buffers 1024 lines and evicts the oldest when full. A slow poller drops old lines silently, so drain often if you can't miss any.

## counters

_Link statistics_

```text
fn counters(&self) -> CountersSnapshot
```

_No round-trip_

A `Copy` snapshot of four totals that only climb, reset only on process restart: rising `crc_drops` means a flaky cable, `reconnects` counts port reopens after a dropped link. Full field reference on [`CountersSnapshot`](/library/types/structs.md#counters-snapshot).

#### EXAMPLE

```rust
println!("{:?}", device.counters());
```
