---
title: "OP-02: Configure the Windows Service logger (SaifWindowsServiceLog)"
summary: Bootstrap sequence for the Windows Service SAIF logger.
status: draft
owner: SAIF Platform Team
last_reviewed: 2026-10-02
library: SAIF.NetCore.Logging
reference_type: package
operation_id: OP-02
trigger: Host Windows Service calls SaifWindowsServiceLog.Configure(IEnvironmentClient environmentClient, IHttpJsonClient loggingHttpClient) during service startup.
---

# OP-02: Configure the Windows Service logger (SaifWindowsServiceLog)

| Field | Value |
| --- | --- |
| Operation ID | OP-02 |
| Trigger | Host Windows Service calls `SaifWindowsServiceLog.Configure(IEnvironmentClient, IHttpJsonClient)` during service startup. |
| Inputs | `IEnvironmentClient`, `IHttpJsonClient`. |
| Outputs | A configured Serilog pipeline with an Event Log sink (`manageEventSource: true`), the Logging API sink (error route unset), and a Console sink; `IsConfigured` set to `true`. |
| Primary components | `SaifWindowsServiceLog`, `SaifLogBase`, `ApiLoggerConfig`, `EventLogConfig` |
| Entry point | `SaifWindowsServiceLog.Configure` |

## Steps
1. Call `ConfigureLoggers(environmentClient)`, which resolves the base log threshold using the same application-section-then-`tracingdefaults` fallback as the web logger ([BR-LOG-01](../business-rules.md#br-log-01-application-threshold-overrides-organization-wide-default), [BR-LOG-02](../business-rules.md#br-log-02-numeric-level-strings-bypass-enum-validation)).
2. Parse the threshold; an unparseable or empty value throws `ArgumentException` and aborts configuration ([BR-LOG-08](../business-rules.md#br-log-08-log-entries-drop-until-logger-is-configured)).
3. Still inside `ConfigureLoggers`: build the standard `EventLogConfig` (`Level = LogLevel.Info`) and `ApiLoggerConfig` sink configurations, then apply the `SaifLogging:BaseLevel` and `SaifLogging:Loggers` `appsettings.json` overrides, same as the web logger:
   - 3.1. Check whether the `SaifLogging` section exists **and** contains a `BaseLevel` child; only then replace `BaseLevel` with that configured value (normalized via `ToLogLevelString()`) — an absent section or key leaves the Environment-API-derived `BaseLevel` from steps 1–2 untouched ([BR-LOG-02](../business-rules.md#br-log-02-numeric-level-strings-bypass-enum-validation)).
   - 3.2. Check whether `SaifLogging:Loggers` exists; for each child entry, parse `Type`/`Level`/`LogPattern` and replace the matching standard `ApiLog`/`EventLog` config wholesale, throwing `SaifLogConfigurationException` for an invalid `Type` or `ArgumentException` for a missing `Level` ([BR-LOG-03](../business-rules.md#br-log-03-logger-type-matching-is-case-sensitive), [BR-LOG-04](../business-rules.md#br-log-04-missing-override-level-throws-configuration-error), [BR-LOG-05](../business-rules.md#br-log-05-invalid-logger-type-throws-configuration-exception)).

   > **Origin**: Steps 3.1–3.2 are implemented in [Apply appsettings.json logger overrides](op-04-apply-logger-overrides.md) (`SaifLogBase.ApplyLoggingSettingOverrides`) and executed on behalf of this operation.
4. Back in `Configure`: compute the Event Log source name as `{EnvironmentTrackName.ToUpper().First()}{EnvironmentTrackName.ToUpper().Last()}-{ApplicationName}`.
5. Read `tracingdefaults:loggingapiurl` and `tracingdefaults:logroute` — but **not** `logerrorroute` — and push the base URL into the injected `IHttpJsonClient` ([BR-LOG-09](../business-rules.md#br-log-09-windows-service-logger-never-sets-error-route)).
6. Construct the Serilog logger with the resolved `BaseLevelSwitch`, an Event Log sink built with `manageEventSource: true`, a `LoggingApiSink`, and a Console sink.
7. Set `IsConfigured = true`.

## Process flow

```mermaid
flowchart TD
    A[Configure called] --> B["ConfigureLoggers: resolve base threshold"]
    B --> C{Threshold valid?}
    C -->|No| C1[ArgumentException - Configure aborts]
    C -->|Yes| D["Build standard EventLogConfig + ApiLoggerConfig,<br>apply BaseLevel/Loggers overrides"]
    D --> E["Compute Event Log source name<br>from track name + ApplicationName"]
    E --> F[Read loggingapiurl / logroute only - no logerrorroute]
    F --> G[Push base URL into IHttpJsonClient]
    G --> H["Build Serilog logger: Event Log sink<br>(manageEventSource) + LoggingApiSink + Console sink"]
    H --> I["IsConfigured = true"]
```

## Failure and edge-case behavior
- Same threshold-resolution failure mode as the web logger: an invalid/missing threshold aborts this `Configure` call; `IsConfigured` stays `false` if this is the first attempt, or keeps its prior `true` value if a previously-successful `Configure` is being retried ([BR-LOG-08](../business-rules.md#br-log-08-log-entries-drop-until-logger-is-configured)).
- Because `logerrorroute` is never read, every error entry this logger posts is sent with a `null` error route, regardless of what the Environment API holds for that key ([BR-LOG-09](../business-rules.md#br-log-09-windows-service-logger-never-sets-error-route)).
- An empty `EnvironmentTrackName` from the host's `IEnvironmentClient` can throw when composing the Event Log source, since the code upper-cases and takes the first and last characters of the track name.

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

## Sources
- `src/SAIF.NetCore.Logging/SAIF.NetCore.Logging/SAIF.NetCore.Logging/SaifWindowsServiceLog.cs`
- `src/SAIF.NetCore.Logging/SAIF.NetCore.Logging/SAIF.NetCore.Logging/SaifLogBase.cs`
