---
title: "OP-03: Configure the console logger (ConsoleLogService)"
summary: Bootstrap sequence for the console SAIF logger.
status: draft
owner: SAIF Platform Team
last_reviewed: 2026-10-02
library: SAIF.NetCore.Logging
reference_type: package
operation_id: OP-03
trigger: ConsoleLogService is constructed (typically via DI resolution of IConsoleLogService).
---

# OP-03: Configure the console logger (ConsoleLogService)

| Field | Value |
| --- | --- |
| Operation ID | OP-03 |
| Trigger | `ConsoleLogService` is constructed; `Configure()` runs inline in the constructor. |
| Inputs | `IConfiguration`, `IEnvironmentClient` (identity only). |
| Outputs | A configured Serilog pipeline (Event Log sink at `LogEventLevel.Fatal`, plus a Console sink) set as the static `Log.Logger`, and up to five separately-configured auxiliary `LogWriter` file sinks reachable via `GetLog`/`WriteToAppLog`. |
| Primary components | `ConsoleLogService`, `LogWriter` |
| Entry point | `ConsoleLogService` constructor → `Configure()` |

## Steps
1. Read `SaifLogging:BaseLevel` from `IConfiguration`; if absent or whitespace, default to `"Info"` ([BR-LOG-12](../business-rules.md#br-log-12-console-logger-threshold-ignores-environment-api)).
2. Parse the resolved level string into the `BaseLevelSwitch`; an invalid value throws `ArgumentException` directly out of the constructor, failing DI resolution of `IConsoleLogService`.
3. Register the five auxiliary file paths (`file0`–`file4`) from the root of `IConfiguration` via `RegisterLogFiles()`; each `LogWriter` is constructed with whatever value is read, or an empty string when the corresponding key is unset (`GetArg`'s own `defString: ""` default) — `RegisterLogFiles` applies no Windows-style default path of its own. The `C:/temp/logs/file{n}.txt` default is applied later, only inside `LogWriter.Configure()`, when `FilePath` is whitespace at the time that method runs ([BR-LOG-18](../business-rules.md#br-log-18-console-file-sinks-default-to-local-temp-path)); each `LogWriter` is a separate channel that lazily builds its own Serilog logger with an independent `LoggingLevelSwitch(LogEventLevel.Verbose)` threshold — it is not part of the main pipeline built in step 5.
4. Build the standard `EventLogConfig` sink configuration (`BuildStandardLoggerConfigs` returns only this one config for the console logger).
5. Call `Log.CloseAndFlush()` then set `Log.Logger` to a newly built Serilog logger with the resolved `BaseLevelSwitch`, an Event Log sink restricted to `LogEventLevel.Fatal` (hardcoded in `CreateLogger()`, not read from configuration), and a Console sink. No Logging API sink is attached, and no Debug sink exists anywhere in this logger.
6. The console logger never reads the Environment API base threshold or the `SaifLogging:Loggers` override array; both are ignored for this host style.

## Process flow

```mermaid
flowchart TD
    A[ConsoleLogService constructed] --> B[Configure called inline]
    B --> C["Read SaifLogging:BaseLevel,<br>default Info if whitespace/missing"]
    C --> D{Level valid?}
    D -->|No| D1["ArgumentException thrown from constructor -<br>DI resolution fails"]
    D -->|Yes| E["RegisterLogFiles: file0..file4,<br>each its own LogWriter with independent Verbose switch"]
    E --> F["Build Serilog Log.Logger: Event Log sink<br>(Fatal, hardcoded) + Console sink"]
```

## Failure and edge-case behavior
- An invalid `SaifLogging:BaseLevel` value throws `ArgumentException` directly out of the constructor, failing DI resolution of `IConsoleLogService`; it is the only *level-parsing* failure mode, but it is not the only way this constructor can throw — `EventLog` construction in `CreateLogger()` can itself fail at runtime (for example, on a non-Windows host or without the necessary OS permissions to create/open the named Event Log source), and `_loggers`/the registered `LogWriter` list is mutable instance state assembled during the same constructor call, so any exception during `RegisterLogFiles()` or `CreateLogger()` likewise aborts construction and DI resolution.
- File sinks are lazy: a configured path is only created on disk via an explicit call to the writer's own public `Configure()` method or on first access of its `Logger` property (not specifically on first write) — a channel that is never explicitly `Configure()`-d and whose `Logger` property is never accessed never creates a file — and each file sink's own `LoggingLevelSwitch` (fixed at `Verbose`) is independent of `BaseLevelSwitch` and of the main Event Log/Console sinks.
- Environment API thresholds and `SaifLogging:Loggers` overrides set by operators have no effect on this logger ([BR-LOG-12](../business-rules.md#br-log-12-console-logger-threshold-ignores-environment-api)).

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)

## Sources
- `src/SAIF.NetCore.Logging/SAIF.NetCore.Logging/SAIF.NetCore.Logging/ConsoleLogService.cs`
- `src/SAIF.NetCore.Logging/SAIF.NetCore.Logging/SAIF.NetCore.Logging/Console/LogWriter.cs`
