Two independent paths: (1) the Serilog pipeline built in ConsoleLogService.CreateLogger() dispatches a standard Debug/Info/Warning/Error/Fatal call to the Console sink (and the Event Log sink when the event is Fatal); (2) the host explicitly calls ConsoleLogService.WriteToAppLog(message, logType) or GetLog(logType).Write* against one of the five registered LogWriter file channels — this is not automatically triggered by the standard log methods.
Inputs
The formatted log event and the BaseLevelSwitch threshold (path 1); the message text and target LogType (path 2).
Outputs
A console line (and, for Fatal events, a Windows Event Log entry); separately, an appended line in the explicitly targeted log file.
Primary components
ConsoleLogService, LogWriter
Entry point
Serilog sink dispatch for the main pipeline (no public method call); ConsoleLogService.WriteToAppLog / GetLog(LogType) for the file channel
For the main pipeline: Serilog evaluates the event against BaseLevelSwitch, resolved solely from SaifLogging:BaseLevel (default Info) — Environment API thresholds and appsettings.jsonLoggers overrides have no effect here (BR-LOG-12).
The Console sink renders and writes every event that passes the threshold to standard output/error; the Event Log sink additionally writes the event only when its level is Fatal (hardcoded in CreateLogger()).
For the file channel (independent of steps 1–2): the host calls WriteToAppLog(message, logType) or obtains the ILogWriter via GetLog(logType) and calls one of its Write/WriteError/WriteDebug/WriteWarning methods.
Each Write* call first calls SetActivityDetails(), tagging Activity.Current with log.type (the LogType) and log.file (the writer's FilePath).
It then writes to the injected ILogger<ISaifLoggable> with the template "[{LogType}] {Message}" — this write goes wherever the host's own ILogger<ISaifLoggable> provider is configured to send it, which is not necessarily this writer's file.
Only then does it access the Logger property to write the Serilog message: this access is what lazily triggers Configure() on first use if the underlying logger has not been built yet, opening the file at the configured (or defaulted) path and applying its own independent LoggingLevelSwitch fixed at LogEventLevel.Verbose — this threshold is unrelated to BaseLevelSwitch. Only this Serilog Logger instance writes to the channel's actual file; the ILogger<ISaifLoggable> write in the previous step is a separate destination.
flowchart TD
A["Standard log call (Debug/Info/Warning/Error/Fatal)"] --> B{Meets BaseLevelSwitch threshold?}
B -->|No| Z[Event suppressed]
B -->|Yes| C[Console sink writes to stdout/stderr]
C --> D{Event level is Fatal?}
D -->|Yes| E[Event Log sink also writes the entry]
D -->|No| F[Event Log sink skips it]
G["WriteToAppLog / GetLog(LogType).Write* called explicitly"] --> H["SetActivityDetails: tag Activity.Current<br>with log.type and log.file"]
H --> I["Write to injected ILogger (separate destination,<br>not necessarily this writer's file)"]
I --> J["Access Logger property: lazily runs<br>LogWriter.Configure on first use (Verbose switch)"]
J --> K[Write message to this Serilog Logger - appends to the file]
The file channel is only reached through an explicit WriteToAppLog or GetLog(logType).Write* call; it is not a destination that standard Debug/Info/Warning/Error/Fatal calls route through automatically, and it is not an automatic sink fan-out from the main Console/Event Log pipeline.
Only the channel's own Serilog Logger instance writes to that channel's file; the injected ILogger<ISaifLoggable> write in each Write* call reaches whatever destination the host's own ILogger provider is configured for, which is not guaranteed to be this file.
Because file sinks are lazily configured on first access of the Logger property (which, within a Write* call, happens after the ILogger<ISaifLoggable> write) or via an explicit call to the writer's own public Configure() method, a channel that is never explicitly Configure()-d and whose Logger property is never accessed never creates a file on disk — access, not a write specifically, is what triggers creation.
The default Windows-style path (C:/temp/logs/file{n}.txt) is used whenever the corresponding file{n} key is unset, which can be inappropriate on non-Windows hosts (BR-LOG-18).