---
title: "OP-04: Apply appsettings.json logger overrides"
summary: How per-sink appsettings.json overrides replace standard sink configuration.
status: draft
owner: SAIF Platform Team
last_reviewed: 2026-10-02
library: SAIF.NetCore.Logging
reference_type: package
operation_id: OP-04
trigger: Invoked internally during SaifLog.Configure or SaifWindowsServiceLog.Configure, after the standard sink configs are built.
---

# OP-04: Apply appsettings.json logger overrides

| Field | Value |
| --- | --- |
| Operation ID | OP-04 |
| Trigger | Called internally by `SaifLogBase.ConfigureLoggers` after the standard `ApiLoggerConfig`/Event Log configs are built, for web and Windows Service hosts only. |
| Inputs | `IConfiguration` section `SaifLogging:BaseLevel` (existing `BaseLevel` string), `IConfiguration` section `SaifLogging:Loggers`, the standard sink config set. |
| Outputs | A possibly-updated `BaseLevel` string (only when the `SaifLogging:BaseLevel` key is present), and a sink config set with zero, one, or both standard entries replaced wholesale. |
| Primary components | `SaifLogBase.ApplyLoggingSettingOverrides`, `IConfigurationSectionExtensions.ToLoggerConfig` |
| Entry point | `SaifLogBase.ApplyLoggingSettingOverrides` |

## Steps
1. `SaifLogBase.ApplyLoggingSettingOverrides` gets the `SaifLogging` configuration section and checks two conditions: the `SaifLogging` section itself must exist (`loggingSection.Exists()`), **and** it must contain a `BaseLevel` child section (`loggingSection.SectionExists(StringValues.BaseLevelConfigSection)`). Only when both are true does it assign `BaseLevel = loggingSection.GetSection(StringValues.BaseLevelConfigSection).Value` — this assignment itself (via the inherited `BaseLevel` setter) normalizes the value through `ToLogLevelString()`. When either condition is false, the Environment-API-derived `BaseLevel` value from [OP-01](op-01-configure-web-logger.md)/[OP-02](op-02-configure-windows-service-logger.md) is left untouched. This step does **not** construct or modify `BaseLevelSwitch` — the switch (`BaseLevelSwitch = new LoggingLevelSwitch(BaseLevel.ToLogEventLevel())`) is built later, back in `ConfigureLoggers`, after `ApplyLoggingSettingOverrides` and `InitializeLoggerInfoLookups` have both already run, so it always reflects whatever `BaseLevel` value is current at that point — including this step's override, if one was applied.
2. Separately, check whether the `SaifLogging:Loggers` section exists; if not, skip the remaining steps — the standard sink configs stand unchanged.
3. For each child entry, read `Type` and parse it against the `LoggerType` enum (`ApiLog`, `ErrorLog`, `EventLog`); the match is case-sensitive ([BR-LOG-03](../business-rules.md#br-log-03-logger-type-matching-is-case-sensitive)).
4. An invalid or missing `Type` throws `SaifLogConfigurationException` ([BR-LOG-05](../business-rules.md#br-log-05-invalid-logger-type-throws-configuration-exception)).
5. Read `Level` and parse it via `ToLogLevel()`; a missing value throws `ArgumentException` — there is no default fallback to `Info` ([BR-LOG-04](../business-rules.md#br-log-04-missing-override-level-throws-configuration-error)).
6. Read `LogPattern`; if absent, the resulting config carries a `null` pattern — it is **not** backfilled from the standard config, because the override replaces the whole entry.
7. Replace the matching standard sink config (`ApiLog` or `EventLog`) with the parsed override. An `ErrorLog` entry parses successfully but is never matched against any standard sink and is silently ignored.

## Process flow

```mermaid
flowchart TD
    A[ApplyLoggingSettingOverrides runs] --> B{"SaifLogging section exists AND<br>contains a BaseLevel child?"}
    B -->|No| C["BaseLevel left at its<br>Environment-API-derived value"]
    B -->|Yes| D["BaseLevel = SaifLogging:BaseLevel value<br>(normalized via ToLogLevelString)"]
    C --> E["SaifLogging:Loggers section present?"]
    D --> E
    E -->|No| Z[Standard sink configs unchanged]
    E -->|Yes| F["For each entry: parse Type case-sensitive"]
    F --> G{Type valid?}
    G -->|No| G1[SaifLogConfigurationException]
    G -->|Yes| H[Parse Level]
    H --> I{Level present and valid?}
    I -->|No| I1[ArgumentException - no Info fallback]
    I -->|Yes| J[Read LogPattern - null if absent]
    J --> K{Type matches a standard sink?}
    K -->|ApiLog or EventLog| L[Replace standard config wholesale]
    K -->|ErrorLog| M[Parsed but silently ignored - no matching sink]
```

## Failure and edge-case behavior
- A missing `Level` on any `SaifLogging:Loggers` override entry throws `ArgumentException`, aborting the calling `Configure` call entirely ([BR-LOG-04](../business-rules.md#br-log-04-missing-override-level-throws-configuration-error)).
- A `null` `LogPattern` on an override entry propagates to the Event Log `outputTemplate` or to `LoggingApiSink`'s constructor, which builds its `MessageTemplateTextFormatter` from that pattern before any event is dispatched; whether `MessageTemplateTextFormatter`'s own constructor tolerates a `null` template argument is not confirmed in this library's source.** If that constructor call throws, the sink itself fails construction and never reaches `Send`/`SendException` for that attempt ([BR-LOG-14](../business-rules.md#br-log-14-message-formatter-built-once-at-construction)).
- The `SaifLogging:BaseLevel` override check is independent of `SaifLogging:Loggers` section presence, but it is itself conditional on the `SaifLogging` section existing and containing a `BaseLevel` key; an absent `SaifLogging` section or an absent `BaseLevel` key within it leaves the Environment-API-derived `BaseLevel` value untouched, it does not apply a different fallback of its own.
- The Console logger never calls this operation; `SaifLogging:Loggers` has no effect for console hosts ([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)

> **Note:** Statements marked with `**` could not be confirmed; treat as unknown until verified.

## Sources
- `src/SAIF.NetCore.Logging/SAIF.NetCore.Logging/SAIF.NetCore.Logging/SaifLogBase.cs`
- `src/SAIF.NetCore.Logging/SAIF.NetCore.Logging/SAIF.NetCore.Logging/Extensions/IConfigurationSectionExtensions.cs`
- `src/SAIF.NetCore.Logging/SAIF.NetCore.Logging/SAIF.NetCore.Logging/Extensions/LogLevelExtensions.cs`
