Table of Contents

Log Viewer Page

The Log Viewer is the read-only screen at /preference/log, route name preference-log, that renders the web service'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 Show Log button on the menu bar. It sits in the bar's right-hand group, after the active-page title and its separator and before the logout button, and it carries the article icon with the tooltip View the application log. The button 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, the CSV and CL Controller visibility checkboxes and Show Physics Options, and no log entry; the Page dropdown does not list the route either.

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 and the menu bar's active-page indicator reads Log Viewer.

What It Reads

Two endpoints, both on the project controller, and both served from the same file:

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; 404 with the same message when there is none

Both compose logs/log-{yyyy-MM-dd}.txt beneath the process's current working directory and date the name 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 — see Program and Hosting for those rules and how they differ from the console provider's.

The sink is a daily file logger provider registered at startup against a logs folder under the process's current working directory. It writes one entry per line as [yyyy-MM-dd HH:mm:ss.fff] [Level] Category - message, appends the exception on its own lines when there is one, serialises appends behind a lock and swallows every I/O failure, on the principle that logging must never fault a request. Logging scopes are not rendered into the file. The application writes one Information 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 sink recomposes the file name from the clock on every append, 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.

Only the current day's file is reachable. Previous days' files stay on disk 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.

The same controller also exposes a POST endpoint that appends a caller-supplied line to the same daily file. It writes with its own formatting rather than through the logger provider, and nothing in the shipped SPA calls it.

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, 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 menu bar's active-page indicator 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 always-visible Show Log button in the bar's right-hand group, and the Preference dropdown that carries no log entry.
  • 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/menu.ts — the Show Log label and its tooltip.
  • 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.
  • Environments/ProjectController.cs — the two endpoints the page reads, both resolving today's file under the working directory, plus the append endpoint nothing calls.
  • Common/DailyFileLoggerProvider.cs — the sink: the per-day file, the entry format, the lock, the swallowed I/O failures and the null scope.
  • Program.cs — registers that provider and its level filters, and writes the one startup entry right after the host is built.

See Also

  • Main Panel — the menu bar whose right-hand Show Log button is 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