# Console

> The live diagnostic feed of the running program: 500 entries in memory, searched and filtered by level, area and component, with a detailed capture and a support package for troubleshooting.

The Console tab of [Events](events) (`/events?view=console`) is the program-wide diagnostic feed:
every subsystem (the app, the embedded OPC UA server, the drivers, Logic, Process, the dashboards,
the agent access and licensing) writes into one stream, and Console shows the most recent 500
entries of it, live. It is the last tab because it is the technical detail read last, after the
typed timelines. Seeing it takes **View events**; exporting rows or creating a support package takes
**Export/import histories**; starting or stopping a detailed capture takes **Change settings**.

## The feed and its buffer {#the-feed-and-its-buffer}

Console is a projection of the process's single diagnostic flow, kept in memory: the newest 500
entries, shared by every surface that shows it (the station's own window, every remote browser,
every tab). Older entries roll off as new ones arrive; nothing on this tab survives a restart. The
same flow is written to a daily rolling file, `logs\ganter-lab-<date>.log` in the station's data
folder (30 days kept, 50 MB per file), which is what a support package collects.

Every entry is sanitized before it reaches Console or the file: passwords, tokens, keys and
credentials inside URLs are replaced by "[REDACTED]", protocol payloads (raw bytes, JSON or XML
documents) by "[PROTOCOL PAYLOAD OMITTED]", a message is cut at 16 KB and an exception at 64 KB
with "…[TRUNCATED]". Only entries of level Information and above enter the feed; verbose detail, and
entries a producer marks as diagnostic detail (raw, repetitive protocol chatter), reach a detailed
capture file only. Communication failures that repeat are coalesced by their producer into a
first-failure line, periodic summaries and a recovery line, so a broken device does not flood the
list.

## An entry {#an-entry}

Each row shows the time of day, a dot for the level (neutral for Info, caution for
Warning, critical for Error), "Area · Component" and the message. The **Copy line** button at the
right edge of a row copies "full timestamp [Severity] Area · Component message" and the action feed
answers "Line copied to clipboard." (or "The clipboard refused the copy." on a browser that
withholds the clipboard). Clicking the row selects it and opens the detail panel.

## Search {#search}

The box "Search messages and all available context" filters as you type, with no delay. A row
matches when the text is contained (case-insensitive) in any of: the message, the area, the
component, the logger, the device, the tag, the operation, the client, the session, the unit, the
procedure, the run id or the detail text (an exception and its stack trace).

## The level chips {#the-level-chips}

Three chips, **Info**, **Warning** and **Error**, each a toggle with a dot in the level's
tone and a live count. A chip that is on shows its level; click it to hide that level. The count is
how many entries the chip would reveal under the current search, area and component: it honours
those three filters and ignores the level toggles themselves, so a hidden level still advertises
what it holds. All three start on.

There is no Debug chip: entries below Information never enter the feed, so a chip for them could
only ever read 0 and hide nothing. Verbose detail is found in a detailed capture file instead.

## The Area and Component lists {#the-area-and-component-lists}

| List | Values | What it does |
| --- | --- | --- |
| Area | **All areas** (default), then one per subsystem: **App**, **Server**, **Connector**, **Logic**, **Process**, **Dashboard**, **Agent**, **Licensing** | Keeps the entries of one subsystem. |
| Component | **All components** (default), then every component present in the buffer for the selected area, sorted alphabetically | Keeps the entries of one concrete producer inside the area (a driver, a worker, the OPC UA SDK). |

| Area | What writes there |
| --- | --- |
| App | Application lifecycle: the desktop host, logging, crash and update handling, the event journal's own failures, and anything that named no other area. |
| Server | The embedded OPC UA server and its SDK. |
| Connector | Device communication: the connector, the drivers, tag reads and writes. |
| Logic | Variables, actions, triggers, alarms and the write funnel. |
| Process | Models, recipes, evaluations, occurrences, the interlock monitor and the run lifecycle. |
| Dashboard | Commands pressed on dashboards, and the embedded web host that serves them. |
| Agent | What an AI agent did over MCP. |
| Licensing | The station's license and the unlicensed-runtime limiter. |

The component you picked stays on the list even after every entry carrying it has rolled out of the
buffer, so a filter set to watch one driver is not silently widened to every component; an empty
list is then the honest answer. Changing the area rebuilds the component list for that area.

## Following the tail {#following-the-tail}

With **Follow tail (auto-scroll)** on (the default), the list sticks to the newest entry as the feed
grows. Scrolling up turns it off, so you can read something while entries keep arriving; scrolling
back to within 24 pixels of the bottom turns it on again. While it is off, a pill "N new entries"
(or "1 new entry") counts the arrivals that match the current filters; click it, or the toolbar
button, to jump back to the tail. Switching to another tab and back keeps the follow state, and
jumps to the tail if it was on.

## The toolbar {#the-toolbar}

| Command | What it does | Greyed when | Not drawn when |
| --- | --- | --- | --- |
| Start capture / Stop capture | Starts a detailed diagnostic capture for up to 15 minutes, or stops the one running (see below). | While the switch is being applied | The role lacks Change settings |
| Open in window | Opens the Events page in a new browser tab or window, on the station's own window as well as on a remote browser, so the feed can be kept on a second monitor. The new window opens on the Console tab, the one the button sits on. A browser that blocks pop-ups opens nothing and the action feed says so: "The browser would not open a second window. Allow pop-ups for this station and try again." | | |
| Word wrap | Wraps long messages onto several lines instead of clipping them. | | |
| Follow tail (auto-scroll) | Turns following on (jumping to the tail and clearing the new-entry count) or off. | | |
| Clear view | Empties the in-memory feed on every surface at once: the station's window, every remote browser and every tab see the same empty list. The action feed answers "Console view cleared for the whole station." The rolling log files, the capture files and the event journal are untouched. | The runtime is stopped: the button is greyed, and its tooltip says the station accepts no change right now | The role lacks Change settings |
| Export | Opens the menu: **Export as CSV…**, **Export as JSON…** and, after a separator, **Create support package…**. | Create support package is greyed while one is being built | The role lacks Export/import histories |

## Detailed capture {#detailed-capture}

**Start capture** raises the whole process to verbose logging for up to fifteen minutes and writes
everything, verbose detail and protocol chatter included, to a capture file of its own:
`logs\diagnostic-captures\ganter-temporary-diagnostic-<stamp>-<id>.log` in the station's data
folder (a session rolls at 25 MB keeping two files; the ten most recent sessions are kept). The
500-entry feed and the normal rolling file stay exactly as they were: nothing extra reaches them.

The button then reads **Stop capture**; its tooltip says "Stop the detailed diagnostic capture
early. Started hh:mm:ss." The capture stops by itself when the countdown reaches zero, when you
press the button, or when the app shuts down. Both ends are announced in the action feed ("Detailed capture started for up to 15
minutes.", "Detailed capture stopped.") and leave Console lines under App · Diagnostic capture.

The capture is a station-wide switch: every circuit sees the same countdown, and it outlives the
window that pressed it. That is why it takes **Change settings**: a role without it is not drawn
the button, and a press that reaches the handler anyway answers "Starting or stopping the detailed
capture needs the Change settings permission." What the capture is doing is under no permission at
all: while it records, a readout beside the button carries a recording dot, "Detailed capture
active" and the time it has left (mm:ss, from 15:00), and every role reads it. When nothing is
recording, that same readout is left to the screen reader, which reads "Detailed capture inactive".

## Create support package {#create-support-package}

The last item of the Export menu builds a zip to hand to support and downloads it through the
browser; the action feed answers "Support package created:" followed by the file name
(`ganter-support-<stamp>-<id>.zip`), or "Support package failed:" followed by the reason. It needs
**Export/import histories** like every export. The
package holds:

| Entry | Content |
| --- | --- |
| `recent-console.json` | The entries of the in-memory feed as structured records (every field of every entry, not the filtered view). |
| `logs/ganter-lab-*.log` | The normal rolling log files. |
| `diagnostic-captures/ganter-temporary-diagnostic-*.log` | The detailed capture files. |
| `manifest.json` | When it was generated, the capture state (active, started, ends, stopped, file name), how many feed entries were included, the limits, the list of included files with their sizes, and every omission with its reason. |

Files are taken newest first within a budget: at most 8 files, at most 24 MB per file and 96 MB in
total; a file larger than its share is included tail-only (its newest bytes) and the manifest says
so. The zip is built in the system's temporary folder and deleted once the download has been
served.

## The detail panel {#the-detail-panel}

Selecting an entry opens the panel with the severity badge, "Area · Component" and a Close cross,
then:

| Section | Field | Content |
| --- | --- | --- |
| Head | Timestamp | Full date and time with milliseconds. |
| Head | Message | The one-line message. |
| CONTEXT | Area | The subsystem. |
| CONTEXT | Component | The concrete producer. |
| CONTEXT | Logger | The logger category behind the entry, shown when known. |
| CONTEXT | Level | Debug, Information, Warning or Error. |
| CONTEXT | Device, Tag, Operation, Client, Session, Unit, Procedure, Run | Each shown only when the entry carries it: the device and tag a connector line refers to, the operation being diagnosed (a read, a write, a connection), the OPC or protocol client and session, and the Process unit, procedure and run id. |
| DETAILS | | The exception text and stack trace, when the entry carried one. |

**Copy entry** at the foot copies the whole panel as text (the header line, the message, one
"Label: value" line per context field, then the detail block) and answers "Entry copied to
clipboard." The selection is dropped if the entry rolls out of the buffer, and cleared by Clear
view.

## Export {#export}

**Export as CSV…** and **Export as JSON…** write the rows currently visible (the filtered view) as
a browser download named `app-logs-<yyyyMMdd-HHmmss>.csv` or `.json`; the action feed answers
"Exported N row(s) to" followed by the file name. Both carry the same columns, in this order:
`Timestamp` (headed
"Timestamp (UTC-03:00)" in the CSV, the time reference declared once), `Severity`, `Area`,
`Component`, `Logger`, `Device`, `Tag`, `Operation`, `Client`, `Session`, `Unit`, `Procedure`,
`RunId`, `Message` and `Exception`. The JSON is an envelope, `{ "TimeZone": "...", "Entries": [
... ] }`. A CSV cell beginning with `=`, `+`, `-` or `@` is prefixed with an apostrophe so a
spreadsheet reads it as text.

## The status line {#the-status-line}

"N shown · M in buffer", then " · filtered" while any search, area, component or level filter is in
force; on the right, "1 selected" while an entry is selected and "Updated hh:mm:ss", the time of the
last entry that arrived. When nothing matches, the list reads "No entries match the current filters.
Adjust the level, area, component or search to see more."

## What Console does not do {#what-console-does-not-do}

- It is not a record: it holds 500 entries in memory, loses them at restart, and Clear view empties
  it for everyone. The durable record is the three timelines and, on disk, the rolling log files.
- It shows nothing below Information, and never the protocol detail; that is what a detailed
  capture is for.
- Clear view deletes no file and no journal row, it is refused while the runtime is stopped, and it
  is drawn only for a role that may change settings, like Export and Start capture beside it.
- The detailed capture cannot be lengthened beyond fifteen minutes; start another one.
- Export writes what is visible, never the files on disk; the support package is how the files
  leave the station.
- An agent with the Logs area open reads the same feed through `logs_read`
  (see [Tools](agent-tools)); it cannot clear it.
