Skip to content

OP-01: Configure the web logger (SaifLog)

Field Value
Operation ID OP-01
Trigger Host application calls SaifLog.Configure(IEnvironmentClient, IHttpJsonClient, IHttpContextAccessor) during application startup.
Inputs IEnvironmentClient (Environment API access), IHttpJsonClient (Logging API transport), IHttpContextAccessor (used to build the web-specific exception formatter).
Outputs A configured Serilog pipeline with Event Log and Console sinks plus the Logging API sink; IsConfigured set to true.
Primary components SaifLog, SaifLogBase, ApiLoggerConfig, EventLogConfig
Entry point SaifLog.Configure

Steps

  1. Call ConfigureLoggers(environmentClient), which resolves the base log threshold by checking the application-named Environment API section first, falling back to GetSettingValue("logthreshold", "tracingdefaults") (BR-LOG-01, BR-LOG-02).
  2. Parse the resolved threshold string into a LogEventLevel; an unparseable or empty value throws ArgumentException and aborts configuration (BR-LOG-08).
  3. Still inside ConfigureLoggers: build the standard ApiLoggerConfig and EventLogConfig sink configurations from the Environment API pattern keys and the resolved threshold, then apply the SaifLogging:BaseLevel and SaifLogging:Loggers appsettings.json overrides:
  4. 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).
  5. 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, BR-LOG-04, BR-LOG-05).

Origin: Steps 3.1–3.2 are implemented in Apply appsettings.json logger overrides (SaifLogBase.ApplyLoggingSettingOverrides) and executed on behalf of this operation. 4. Back in Configure: set ExFormatService to a new ExceptionFormatServiceForWeb(environmentClient, httpContext) and call Log.CloseAndFlush(). 5. Read tracingdefaults:loggingapiurl, tracingdefaults:logroute, and tracingdefaults:logerrorroute from the Environment API, and push the base URL/credentials into the injected IHttpJsonClient. 6. Construct the Serilog logger with the resolved BaseLevelSwitch, an Event Log sink at the configured EventLogConfig level, a LoggingApiSink, and a Console sink. 7. Set IsConfigured = true so that subsequent log calls are accepted.

Process flow

flowchart TD
    A[Configure called] --> B["ConfigureLoggers: resolve base threshold"]
    B --> C{Threshold valid?}
    C -->|No| C1["ArgumentException - Configure aborts,<br>IsConfigured stays false if this is the first<br>attempt, or keeps its prior true value<br>if a previously-successful Configure is retried"]
    C -->|Yes| D["Build standard ApiLoggerConfig + EventLogConfig,<br>apply BaseLevel/Loggers overrides"]
    D --> E["Set ExFormatService = ExceptionFormatServiceForWeb"]
    E --> F[Read loggingapiurl / logroute / logerrorroute]
    F --> G[Push base URL into IHttpJsonClient]
    G --> H["Build Serilog logger: Event Log sink +<br>LoggingApiSink + Console sink"]
    H --> I["IsConfigured = true"]

Failure and edge-case behavior

  • A missing or invalid base threshold aborts this Configure call entirely; IsConfigured stays false if this is the first Configure attempt on the instance, or keeps its prior true value if a previously-successful Configure is being retried and this one fails (BR-LOG-08). While IsConfigured is false, every subsequent non-exception log call still returns a generated correlation ID but writes only a Debug-level diagnostic trace instead of reaching ILogger/Serilog; an exception-bearing call returns an ID only once its own pre-ID exception-formatting steps complete without throwing.
  • An invalid SaifLogging:Loggers entry Type throws SaifLogConfigurationException; a missing Level throws ArgumentException — both abort this Configure call the same way.
  • Whether a missing loggingapiurl/logroute/logerrorroute aborts configuration is not confirmed one way or the other in this library's source for every case.** LoggingApiSink's constructor calls _httpJsonClient.SetClientInfo(baseUrl, "SaifLog", Environment.MachineName, applicationId) — only the base URL and client identity, not either route — and separately stores routePart/errorRoutePart for later use as the PostAsync URI argument; this call happens without a surrounding try/catch. Whether the specific IHttpJsonClient implementation in use throws for an empty/invalid baseUrl argument is implementation-dependent and not something this library's own source confirms either way. If SetClientInfo (or the sink constructor itself) does not throw, final endpoint validity for the subsequent PostAsync(routePart, ...) call depends entirely on how the injected IHttpJsonClient resolves a base URL and route together — an empty route does not by itself make a valid base URL resolve to an invalid or empty endpoint. An unsuccessful HTTP result from that POST is reported only to Console/Debug (BR-LOG-15); separately, LoggingApiSink.Emit is async void with no try/catch, so an exception thrown by the POST call itself (as opposed to an unsuccessful result) is not caught or reported anywhere in this library.

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

Sources

  • src/SAIF.NetCore.Logging/SAIF.NetCore.Logging/SAIF.NetCore.Logging/SaifLog.cs
  • src/SAIF.NetCore.Logging/SAIF.NetCore.Logging/SAIF.NetCore.Logging/SaifLogBase.cs
  • src/SAIF.NetCore.Logging/SAIF.NetCore.Logging/SAIF.NetCore.Logging/SaifLoggerConfig.cs