Table of Contents

Log Viewer Page

The Log Viewer is the read-only screen at /preference/log, route name preference-log, that renders this web service instance's own log file for the current day. It is not a Control-Tree page and takes no ?tree= query: the whole page is one toolbar over one scrolling text area. Despite the route path it is not an entry of the Preference dropdown, and it is not under /util/.

Reaching the Page

The only navigation into the page is the Log Viewer entry of the menu bar's Help dropdown — a support tool rather than a shop-floor one, so it sits beside the other two references the service hosts instead of on the bar. It sits below Documentation, API Reference (Swagger) and a separator, and is the dropdown's last entry unless a host built the SPA with Help items of its own. Unlike the two above it, which open a same-origin reference in a new tab and carry an open-in-new-tab icon, it is a router link to the preference-log route, so the page opens in place, in the same tab, and the entry carries no icon. Its label is the route's own title key — the one the browser tab resolves — rather than a menu string of its own. The entry is rendered unconditionally — no route, project, licence or authentication state hides it — so it is reachable from every page in the shell. The Preference dropdown carries the Language submenu, Show Physics Options and the Optional Nodes & Charts submenu, and no log entry; no page switch names the route either, so none is lit while the page is shown.

The route sits inside the main layout, so the page renders under the same menu bar and footer as every other page, and it is behind the login gate whenever that gate is on — the router's navigation guard admits only the login route itself while the session is unauthenticated. The route carries an i18n title key, so the browser tab reads Log Viewer - HiNC. The menu bar prints no page title on this page or any other; the name on screen is the page's own Log Viewer title row.

What It Reads

Two endpoints, both on the project controller, and both served from the same file — the one this instance is writing today:

Request Answer
GET /api/project/logs { date, content } for today's file; 404 with a message when there is none
GET /api/project/download-log the same file as a text/plain attachment named as the file is, for example log-Development-2026-09-23.txt; 404 with the same message when there is none

Neither composes a path of its own: both ask the file log provider for today's file, so they follow its configured directory and its instance name, and they open it with write sharing while the provider's writer still holds it. The date comes from the server's local clock. Neither supports a range or a tail: the whole file is read into memory and returned in full on every call, so an auto-refresh at a short interval re-transfers the entire log each tick. Any failure inside either handler answers 400 with the exception message.

The two client wrappers part company on that 404. The wrapper over logs normalises it into an empty result rather than an error, which is what gives the page a clean “no log yet” state instead of a red panel; every other non-OK status there is raised as a typed error carrying the HTTP status and the response body. The download wrapper normalises nothing — 404 included, every non-OK status becomes that same typed error, a path the disabled-without-content Download button makes hard to reach. Because the controller declares no anonymous carve-out, both endpoints fall under the authenticated-user fallback policy when the login gate is enabled.

The date badge shows whatever date the last answer reported. On the 404 path that date is not the server's — the client fills it from the browser's own clock in UTC — so with no log file present the badge can read a different day than the file name the server would have used.

Refresh and the Auto Interval

The page loads once on mount. After that every reload is driven by one of three things: the Refresh button, the Retry button inside the error panel, or the auto-refresh timer.

Auto-refresh is off by default. The Auto toggle arms it and the interval select beside it chooses the period from a fixed list of 2 s, 5 s, 10 s and 30 s, defaulting to five seconds; the select is disabled while the toggle is off, but it keeps its value. The period is picked from that dropdown rather than typed, so it takes effect on selection. A change to either control tears the existing timer down, and a fresh one starts only while the toggle is still armed: an interval change therefore takes effect immediately but also restarts the countdown, while disarming the toggle simply stops the timer. Arming the toggle schedules the first automatic load one full interval later; it does not fetch straight away.

Ticks are not coalesced with a request already in flight — each tick starts the same load unconditionally.

Neither the toggle nor the interval is persisted. They are page-local state, held in neither the browser's local storage nor the server-side user preferences, so a browser reload returns both to their defaults.

The Page Is Kept Alive

The shell caches routed pages in a keep-alive keyed on the project epoch. Leaving the Log Viewer therefore deactivates it rather than unmounting it, and the page's teardown hook — the one that stops the timer — does not run. Two consequences follow. An armed auto-refresh keeps polling the log endpoint in the background after navigation away, until the keep-alive is rebuilt by a project change or the browser reloads. And returning to the page does not re-run the initial load: the previous content, its last loaded stamp, the toggle and the chosen interval are all still there, and the view refreshes on the next tick or on Refresh.

Errors and Scroll Position

A failed load fills the page's error state and also pushes one foreground line onto the shell's routine-progress footer, so the failure is visible from any page. The error panel takes precedence over content in the text area, so a failed refresh hides the text that was already loaded — but only until the next attempt starts, because the error text is cleared before that request goes out. The content itself is never discarded: it reappears the moment the attempt begins, and the error panel returns only if that attempt fails as well. With auto-refresh armed against an endpoint that keeps failing, the text and the panel therefore alternate on every tick.

After a successful load the view scrolls to the bottom, but only while it is already stuck there — the scroll handler treats a distance of less than 30 px from the bottom as stuck. Scrolling up therefore freezes the position through an auto-refresh, and scrolling back to the bottom re-arms the follow behaviour.

Level Filtering, and Where the File Comes From

The page applies no filtering of its own. There is no level selector, no category selector, no search box and no date picker: it renders the file it is given, verbatim, in the order it was written. Which entries reach the file at all is decided at write time, by the host's logging filters rather than by anything on this screen: the provider's alias is File, so Logging:File:LogLevel in configuration sets the file's levels apart from the console's Logging:LogLevel, over defaults the host seeds in code — see Program and Hosting for those rules and how they combine.

The sink is DailyFileLoggerProvider, registered at startup. It writes {Directory}/log-{instance}-{yyyy-MM-dd}.txt. The directory is Logging:File:Directory, logs by default, a relative path being taken from the process's current working directory; the instance is the host environment name — Development under both launch profiles — so several instances that share a working directory keep separate files. A second process started under the same environment name and directory cannot take the instance lock file, log-{instance}.lock, and writes log-{instance}-{pid}-{yyyy-MM-dd}.txt instead of appending over the first one's lines; it says so on its first line, and its own endpoints serve that file. A directory that cannot be created or written falls back to logs under the working directory, and the first line this process writes there gives the reason.

Each entry is one line, [yyyy-MM-dd HH:mm:ss.fff] [Level] Category - message, with the exception appended on its own lines when there is one; logging scopes are not rendered into the file. A log call only enqueues the line: one background writer thread owns the open file, so a call on a render or step thread never waits on disk. A Critical entry waits until it is written, being often the last line a failing process gets out. When the queue is full, entries are dropped and the next line that gets in is preceded by a note of how many; a write that fails drops its line rather than faulting the caller. Program.cs flushes the queue before the native core's teardown and shuts the writer down last, after which lines are written synchronously.

The application writes a startup summary line immediately after the host is built, so the file for the day the service starts has content before the first request arrives and a freshly started service shows something rather than an empty state. The writer opens the file named for each entry's date, so a service left running past midnight has no file for the new day until something is logged, and the page shows its empty state until then. It also reopens today's file when that file is deleted under it, rather than writing on into an orphan.

Retention is Logging:File:RetainDays, 30 by default, with 0 keeping every file. At start and at each day change the provider deletes this instance's files older than that, and files of the older single-file naming, log-{yyyy-MM-dd}.txt, once they pass the same age; another instance's files are left to that instance.

Only the current day's file is reachable. Previous days' files stay on disk until retention removes them, and the page offers no way to open one. A file that exists but is empty renders the same empty state as a missing one; only the date badge can separate the two, since the empty file's date comes from the server's answer and the missing file's from the browser's UTC clock.

No endpoint appends to the file; the logger provider is its only writer.

What the File Holds

At the shipped levels the file is a record of what the instance did rather than a trace of every request:

  • Startup — one summary line naming the instance, the build (the HiNc assembly version), the REST API version, the working directory, the admin directory and whether the login gate is on; then the host's own Microsoft.Hosting.Lifetime lines — the listening address, the environment and the content root. The shutdown sequence logs its steps as well.
  • Project operations — one line per completed File-menu operation from the project controller, Project load|reload|new|save|save-as|close: <path> in N ms. Load and reload add the load's error and warning counts, and a load with errors is a Warning: the project is open but not whole.
  • Runs — Run N started: <project>, and when the run ends Run N finished|stopped|ended after S s: <project>, <steps> steps; errors NC / step / shell; warnings NC / step / shell; K repeated message(s) not logged, a Warning when the run raised errors. A run is a pace-player lock that arrives with a new run count; an idle one-shot recompute takes the lock without one and is not a run. ended is the outcome when the player was already finished as the run began, where a finish cannot be told from a stop.
  • Engine messages — every Warning and Error of the four session sinks, under the categories Hi.Engine.Shell, Hi.Engine.Nc, Hi.Engine.Step and Hi.Engine.NcManipulation, as <id> <anchor> <message>, the anchor being Sn n for an NC sentence, S<step> Sn n for a step (S<step> alone when the step has no sentence) and - for a shell message or an NC message with no sentence. One message id is logged at most 20 times per run — a step diagnostic can repeat on every step — and the run-end line counts the rest; messages outside a run are capped the same way per idle period. These lines outlive the rows of the sinks the Session Message Panel reads, which each sink's next clearing discards.
  • Sign-ins — a sign-in, a refused sign-in (a Warning) and a sign-out, each with the caller's address; see Login and Authentication.
  • Refused hosts — a request addressed to a host name the service does not admit is answered 400 by host filtering and leaves one line naming that host, under a Microsoft.AspNetCore.HostFiltering category; see Program and Hosting for the names it admits.
  • Native core — the core's own log lines under Hi.Native, at a level read from their wording: licence failures Critical, a GL, shader or EGL failure Error, Mesa software rasterisation Warning, routine records such as the OpenGL context Information. The library's licence abort line is Critical under Hi.HiNcKits.LocalApp.
  • Crashes — an unhandled exception (Critical) and a faulted task nobody observed (Error), each with its exception.
  • Connections and commands — the per-connection lifecycle of the canvases and the Execution page (canvas initialisation, display engine create and remove, the first rendered frame, the Execution page initialised), the playback transport commands other than start (pause, resume, run to line end, run to next pace, stop and reset; a start shows as the Run N started line instead), and Tool House create, duplicate, rename and delete. A hub disconnect that carries an exception is a Warning.

Per-input, per-frame, visibility, compression, hub-connect and status-broadcast chatter, and the editors' per-field echoes, are logged at Debug and do not reach the file at the shipped levels.

Copy, Download, and What the Page Cannot Do

Copy writes the loaded text to the system clipboard and reports the outcome as a toast, positive or negative. Download fetches the file as a blob from the download endpoint and saves it client-side under the name log-<date>.txt, repeating the date the last answer reported — the page names the blob itself, so the instance name in the endpoint's attachment name does not reach the saved file — and also ends in a toast. Both buttons are disabled while there is nothing loaded.

The page cannot clear, truncate, rotate or delete the log — there is no destructive action on it at all, and no endpoint behind it that would perform one. It cannot reach another day, filter by level or text, or stream: refreshing is polling over the same whole-file request, with no hub connection and no server push. It is a viewer.

Layout

  • Log Viewer Page — a viewport-locked page sized from the layout's measured header and footer, so the window itself never scrolls
    • Top Tool Bar
      • Log Viewer Title
      • Date Badge — outlined; carries the date the last answer reported, and reads today only before any answer has set one. Coloured as the primary accent while content is loaded and grey otherwise
      • Last-Loaded Caption — last loaded: {time}, shown once a load has succeeded. The time is formatted in the application locale rather than the browser's, so it re-renders on a language switch; the hover title carries the same instant as an ISO 8601 UTC string
      • Line Count Caption — shown only while content is loaded, where it reads {count} lines. The plural string's no lines and one line forms are out of reach: a zero count is exactly the state that hides the caption, and every writer ends its entry with a newline. The count is the number of newline-separated segments, so a file ending in a newline counts one more than it shows, and a multi-line exception counts as several
      • Auto Toggle — arms auto-refresh; off by default. Tooltip Re-fetch the log file periodically
      • Interval Select — 2 s / 5 s / 10 s / 30 s, defaulting to 5 s; disabled while Auto is off
      • Refresh Button — reloads at once, and shows a spinner in place of its label while the request is in flight. Tooltip Reload the log file
      • Copy Button — disabled without content. Tooltip Copy log content to clipboard
      • Download Button — disabled without content. Tooltip Download today's log file
    • Separator
    • Log Area — a dark, monospaced, scrolling region filling the rest of the page. Long lines are not wrapped, so it scrolls in both directions. It shows exactly one of four things:
      • Error Panel — an error icon, the failure message, and a Retry Button that repeats the load
      • Empty Panel — a document icon, No log file for today., and the hint Log file will appear once the webservice writes its first entry.
      • Log Content — the file's text, preformatted and read-only, with no folding, no line numbers and no per-line affordance of any kind
      • Loading Spinner — with Loading log... beneath it, shown while a load is in flight and nothing has been loaded yet

Source Code Path

See HiNC App Anatomy for git repository links.

Web Application

HiNC-2025-webservice (Quasar CLI SPA):

  • wwwroot-src/src/pages/LogViewerPage.vue — the whole page: the toolbar, the four states of the log area, the auto-refresh timer and its watcher, the stick-to-bottom scroll rule, and the copy and download handlers.
  • wwwroot-src/src/api/logs.ts — the typed wrappers over the two endpoints, the 404-to-empty normalisation with its browser-clock date fallback, and the error type carrying the HTTP status.
  • wwwroot-src/src/router/routes.ts — the preference/log path, the preference-log route name and the title key the tab and the Help entry resolve.
  • wwwroot-src/src/router/index.ts — the login guard the route passes through, and the retitle hook that names the browser tab.
  • wwwroot-src/src/components/AppMenuBar.vue — the Log Viewer entry below the Help dropdown's separator, followed only by the product module's Help items, and the Preference dropdown that carries no log entry.
  • wwwroot-src/src/product/index.ts — the web service's own HelpMenuItems, null, which leaves the Log Viewer entry last.
  • wwwroot-src/src/layouts/MainLayout.vue — the keep-alive that caches this page rather than unmounting it, and the project epoch that rebuilds the cache.
  • wwwroot-src/src/stores/appState.ts — the message action that forwards a load failure to the footer's foreground channel.
  • wwwroot-src/src/stores/routineProgress.ts — the foreground-message store that failure lands in.
  • wwwroot-src/src/utils/pageFit.ts — the page-height function that locks the page to the measured viewport so only the log area scrolls.
  • wwwroot-src/src/i18n/en/logs.ts — the page's own English strings: the title, the empty and loading states, the plural line count and the tooltips.
  • wwwroot-src/src/i18n/en/common.ts — the shared Auto, Refresh, Copy and Download labels.
  • wwwroot-src/src/i18n/en/routes.ts — the Log Viewer route title, which is also the Help entry's label.
  • Environments/ProjectController.cs — the two endpoints the page reads, both opening today's file through the provider, and the one line each project operation logs.
  • Common/DailyFileLoggerProvider.cs — the sink: the File alias, the per-instance file name, the instance lock and its pid fallback, the directory fallback, the queued writer, the entry format, retention and the null scope.
  • Program.cs — creates that provider from Logging:File:Directory and Logging:File:RetainDays, seeds its default levels ahead of the configured ones, writes the startup summary right after the host is built, hooks unhandled and unobserved exceptions, maps the native core's lines onto Hi.Native levels, and flushes and shuts the writer down around the native teardown.
  • appsettings.json — the shipped Logging:File section: its levels, directory and retention.
  • appsettings.Development.json — the Development overlay's Logging:File:LogLevel keys that keep the development-time browser-refresh middleware out of the file.
  • Execution/ExecutionStatusService.cs — the engine-message lines under the four Hi.Engine.* categories, their per-id cap, and the run-start and run-end lines.
  • Common/AuthController.cs — the sign-in, refused sign-in and sign-out lines.

See Also

  • Main Panel — the menu bar whose Help dropdown holds the only navigation into this page
  • Preference Menu Dropdown — the dropdown this route's path is named after, and which carries no log entry of its own
  • Program and Hosting — the host that registers the file provider, seeds its levels and reads its Logging:File keys