Skip to main content

LogProcessor

Server-side logging system with pluggable adapters.

See also

Logger​

Extends the commons Logger with server-specific adapters and log anonymization.

import { Loader } from "@io-oidis-localhost/Io/Oidis/Localhost/Loader.js";

const logger : Logger = Loader.getInstance().getLogger();

Adapters​

TypeClassPurpose
STDStdAdapterConsole output with color formatting
FILEFileAdapterFile-based log persistence
JSONJsonAdapterStructured JSON log output
SENTRYSentryAdapterError reporting to Sentry (from commons)

Frontend log forwarding​

Browser-side traces do not stay in the browser. The GUI forwards them to the backend through LoggerConnector.ProcessTrace (@Extern, available to every user), which replays the trace through every enabled backend adapter — so a single LogIt.Warning(...) in the browser lands in the server log, and via SentryAdapter in Sentry as well.

Because of that, a backend log line prefixed with > FE[<sessionId>]: did not originate in the server process — it came from a browser session, and <sessionId> identifies that session. The forwarded trace carries entryPoint.from = "[FE forwarding]". CrashAdapter strips the "> FE: " prefix before its duplicate-message check, so a repeated frontend error is reported once instead of per occurrence.

note

When triaging backend logs, check for the FE[...] prefix first. A frontend fault forwarded from a browser and a genuine server-side fault look identical apart from that prefix.

A frequent forwarded trace is the WebService drop report from BaseWebServiceClient.rejectPendingResponses() (commons). When a connection drops with requests still in flight, the client settles them with an error and logs one summary line, classified so that only real faults reach WARNING (and therefore Sentry):

PrefixLevelMeaning
Local shutdownDebugStopCommunication() was called locally; no socket died.
Benign navigationDebugClean close (code 1000/1001 or wasClean).
Suspended pageDebugThe page was hidden or frozen/slept when the close was delivered (detected via visibilityState and a 1s alive-tick gap); browsers deliver such closes on resume, so unload flags never fire for them.
AbnormalWarningA live, visible page lost its socket without a close handshake (e.g. code 1006). This is the signal worth investigating - server keepalive drop, network fault, or event-loop starvation.

Close code 1006 is never sent on the wire (RFC 6455 §7.4.1); it is synthesized locally when a connection dies without a Close frame, so there is no benign variant of it - suppression happens only through the suspended-page detection above.

Log Anonymization​

The Logger automatically anonymizes sensitive paths in log output using getAnonymizerSymbols(). Platform-aware: replaces Windows env vars (%USERNAME%, %TMP%, %USERPROFILE%) and Unix vars ($HOME, $USER, $HOSTNAME) with symbolic placeholders.

The application's own locations are replaced as well: the configured app data path becomes $appData, the runtime binary base $binBase and the project base $projectBase (%BINBASE%, %PROJECTBASE% in Windows notation). A path such as $appData/gate/cameraEvents.jsonl in a log line or a Sentry event is therefore the real app data path, not an unexpanded token in the configuration.

Configuration​

Default adapters: [STD, FILE, SENTRY]. Configurable via project settings.

note

Always use LogIt.Info/Warning/Error/Debug instead of console.*. The LogIt static API delegates to the Logger.

Suppressing Sentry noise (ignore lists)​

By default, both LogIt.Error(...) and LogIt.Warning(...) calls hit Sentry — the SentryAdapter forwards the ERROR and WARNING levels (ERROR as an exception, WARNING via captureMessage at warning severity). INFO, DEBUG and VERBOSE are never sent to Sentry. That's the right behaviour for genuine system failures, but it pollutes Sentry when expected user-facing exceptions (e.g. ErrorLocalizable thrown from an @Extern method on a domain validation failure) flow through WebResponse.OnError, which logs them at ERROR level before sending the localized text back to the client.

Warning is NOT Sentry-safe

A common mistake is to downgrade a noisy LogIt.Error to LogIt.Warning expecting it to leave Sentry — it does not, because the adapter captures WARNING too. To keep an expected/transient event (e.g. a flaky upstream retry, a KB/Jira gateway 5xx that self-heals next sync) in local logs but out of Sentry, lower it to LogIt.Info. Use the ignore lists below only when you must keep WARNING/ERROR severity yet still drop the Sentry event.

An authorization refusal is not an application error​

The most common source of avoidable Sentry noise in a front end is the catch around a connector call:

} catch (ex) {
LogIt.Error("Md2Pdf.GetDocument failed: " + (<Error>ex).message);
this.ShowMessage("Nacteni dokumentu selhalo: " + (<Error>ex).message, ToastType.ERROR);
}

When the back end refuses the call rather than failing it, the framework has already handled the outcome: WebServiceClientFactory navigates to /ServerError/Http/NotAuthorized, whose resolver shows a toast and redirects. The panel's own catch then reports the same refusal a second time — one Sentry issue plus a second stacked toast for an ordinary permission check. The server logs it too, at WARNING, from BasicAuthenticationAPI.

Classify the rejection before logging it. A small project-level helper keeps it to one line per site:

public static IsRefusal($error : any) : boolean {
return StringUtils.Contains(ConnectorErrors.MessageOf($error),
"not authorized", "not publicly available");
}
} catch (ex) {
if (!ConnectorErrors.IsRefusal(ex)) {
LogIt.Error("Failed to load the document.", ex);
this.ShowMessage("...", ToastType.ERROR);
}
this.items = []; // state resets stay OUTSIDE the guard
}

Two things to get right when applying this to an existing codebase:

  • Anything that clears a spinner, a "Loading…" placeholder or a disabled control must stay outside the guard. Suppressing it leaves the panel visibly stuck on a refusal.
  • Keep the two-argument LogIt.Error($message, $exception) form where it is already used. The second argument carries the stack, and SentryAdapter routes a stack-carrying record to captureException (grouped by stack) but a plain string to captureMessage (grouped by text). Collapsing the two-argument form into a single concatenated string loses the stack and makes distinct defects group together — and a dynamic label (an id, a status) in that string creates one Sentry issue per value.

The real fix for the anonymous case is upstream: see Authentication — Tab-Level Access Control, because a panel that is properly gated never issues the refused call in the first place.

Two configuration knobs, both defined in your project's bin/project/configs/commons.jsonp, feed into Sentry's native Sentry.init({ ignoreErrors }):

sentry.ignoreErrors (Sentry-only)​

sentry: {
ignoreErrors: [
"Period contains incomplete haulages", // exact substring of the thrown message
/^Validation failed:/ // or a regex
]
}

A string[] of substrings or regexes. Use this when the suppression is specifically about Sentry — STD / FILE / SystemLog still record the error so the audit trail is intact, only the Sentry event is dropped.

logger.reportIgnoreList (logger-wide)​

logger: {
reportIgnoreList: {
"Io.Oidis.Commons.Exceptions.ExceptionsManager.Exit": true,
"Unable to send data to socket server: connection not open to": true
}
}

A Record<string, boolean> consumed by any adapter that opts in via Logger.getIgnoreList(). Currently used by Hub's CrashAdapter and merged into Sentry's ignoreErrors at SentryAdapter.getHandler (commons defaults already populate this with framework-level noise).

Which one to use​

GoalKnob
Drop a specific user-facing throw from Sentry onlysentry.ignoreErrors
Suppress framework noise everywhere it might be reportedlogger.reportIgnoreList
Filter by regex patternEither accepts regex via /.../ literal in JSONP

Both lists are concatenated and passed verbatim to Sentry's SDK, which matches each entry as substring against event.message and event.exception.values[*].value.

When you add a new ErrorLocalizable​

If you introduce a new user-facing throw in an @Extern method, add the English (first-arg) message to sentry.ignoreErrors in the same change. Otherwise the next time a user trips that validation, Sentry collects it. The English message is what reaches Sentry — WebResponse.OnError logs the raw Error object before localizing the response payload.

// Backend
throw new ErrorLocalizable("Subject must have an email", {
cs: "Subjekt musí mít vyplněný e-mail.",
en: "Subject must have an email."
});
// commons.jsonp
sentry: {
ignoreErrors: [
"Subject must have an email" // matches the English first-arg
]
}
Future framework improvement

The current WebResponse.OnError at io-oidis-localhost/.../ResponseApi/Handlers/WebResponse.ts logs every @Extern throw at ERROR regardless of type. A planned change is to detect ErrorLocalizable and downgrade to WARNING / INFO so this list can shrink to genuine edge cases. Until then, maintain the per-solution ignore list.


See also: Logging (LogIt & SystemLog) | Utilities Overview | Metrics | Hub Logging & Monitoring