Skip to content

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-02 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).
  4. An invalid or missing Type throws SaifLogConfigurationException (BR-LOG-05).
  5. Read Level and parse it via ToLogLevel(); a missing value throws ArgumentException — there is no default fallback to Info (BR-LOG-04).
  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

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).
  • 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).
  • 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).

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