feat: migrate DB error logging onto the generic OCORE MultiLogger SQL sink
Playwright Tests / test (push) Canceled after 0s

Retires Fuchs_intranet.debug_log() and every direct EXECUTE [dbo].[fds__admin_logdebug]
call in favor of a single path: ILogger. A new SQL log sink (OCORE.MultiLogger.SqlLoggerProvider,
gated by Fuchs:Logging:SqlSink:Enabled, default on) mirrors every _logger.LogWarning/-Error/
-Critical call into [dbo].[fds__debuglog] via a new fds__admin_logdebug_ext proc, at Warning+.

- Program.cs registers the sink and RequestLogScopeMiddleware (request id/path/authenticated
  user, pushed after UseAuthentication) so every log line in a request — including ones written
  deep inside OCORE's SQL helpers — is attributed automatically.
- FIS_SQLOptions, IntranetController.Do's catch-all, PdfPreviewAsync, and the banking-upload
  handlers now log once, with an explicit EventId preserving the exact CodeReference text the
  retired debug_log() calls used to write, so existing dashboards/queries over fds__debuglog
  keep working unchanged.
- Fuchs_DataService/FdsMfr.cs's FdsSqlOptions had the identical double-write hazard (it's hosted
  in the same process per ADR 0010) — the original 2026-09-16 log review's mfr__getSchema/
  fds__getUpdateableRequests deadlock rows came from exactly this class. Fixed the same way.
- fds__debuglog gains LogLevel/Machine/Category/Account/AuthUser/IsDebug columns; the legacy
  fds__admin_logdebug proc is left in place unused, not dropped.
- FdsDebug.DebugLog (Fuchs_DataService's other ~9 call sites) deliberately left on its direct
  DB-write path — no ILogger call sits alongside it there, so no duplication risk today.

See ADR 0024 (supersedes 0023) and Docs/Concepts/error-logging-pipeline.md.

New tests: ErrorLoggingTests (FIS_SQLOptions), FdsSqlOptionsTests, RequestLogScopeMiddlewareTests.
694/694 pass (the 4 PdfPipelineTests failures seen under full-suite parallel runs are a
pre-existing, unrelated PdfSharp global-font-resolver test-isolation flake — they pass both
individually and in most full-suite runs).

Bumps the OCORE submodule to 3137b56 (SQL sink hardening: redaction matches short fragments
as whole identifier segments, per-entry-tolerant serialization, lazy dataSerialized, opt-in
SqlLoggerOptions.IncludeCategory, LogEntry.Category/AuthUser capture).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Stefan
2026-09-16 17:20:06 +02:00
co-authored by Claude Sonnet 5
parent a1e6002c3b
commit bcf1f2dbb6
23 changed files with 757 additions and 225 deletions
+1
View File
@@ -353,6 +353,7 @@
<Build Include="dbo\Stored Procedures\fds__admin_updateconflicttables.sql" />
<Build Include="dbo\Stored Procedures\fds__admin_removeconflicttables.sql" />
<Build Include="dbo\Stored Procedures\fds__admin_logdebug.sql" />
<Build Include="dbo\Stored Procedures\fds__admin_logdebug_ext.sql" />
<Build Include="dbo\Stored Procedures\fds__admin_getReportCatalog.sql" />
<Build Include="dbo\Stored Procedures\_BackupAndClearInvoiceFile.sql" />
<Build Include="dbo\User Defined Types\json_data_maxU.sql" />
@@ -0,0 +1,61 @@
-- =============================================
-- Author: Dr. Stefan Ott
-- Create date: 2026-09-16
-- Description: Sink for OCORE.MultiLogger.SqlLoggerProvider (see ADR 0024 in Fuchs/Docs/Decisions).
-- Parameter names/types must match SqlLoggerProvider.BuildParameters exactly — this
-- proc is called generically (CommandType.StoredProcedure), so a mismatched or missing
-- parameter fails every write, not just this one.
-- Writes into the same [dbo].[fds__debuglog] table the legacy, explicit
-- fds__admin_logdebug proc writes to, so both paths are queried identically.
-- =============================================
CREATE PROCEDURE [dbo].[fds__admin_logdebug_ext]
@machine varchar(max) = NULL
,@error varchar(max) = NULL
,@exception varchar(max) = NULL
,@data nvarchar(max) = NULL
,@procedure varchar(max) = NULL
,@category varchar(max) = NULL
,@account varchar(max) = NULL
,@authuser varchar(max) = NULL
,@isdebug bit = 0
,@loglevel varchar(1) = NULL
AS
BEGIN
SET NOCOUNT ON;
-- Every text parameter is declared varchar(max)/nvarchar(max), matching the client's wire size
-- (SqlLoggerProvider's local SQL_VarChar/SQL_NVarChar helpers always send Size=-1) — so nothing
-- can fail at the parameter-assignment boundary. The [dbo].[fds__debuglog] columns that are
-- narrower (Machine/Category/Account/AuthUser, CodeReference) are explicitly LEFT()-truncated
-- below instead, so a value longer than the column never raises a truncation error on INSERT.
--
-- CodeReference: the EventId name a caller gave the log call (@procedure) when there is one —
-- most call sites that matter (SQL errors, PDF-preview failures, the Do() catch-all) set this
-- explicitly to the same string the legacy debug_log() callers used, so existing CodeReference
-- queries/dashboards keep working unchanged. Falls back to the ILogger category, then a
-- constant, for the many ordinary _logger.LogWarning/-Error calls that carry no EventId.
INSERT INTO [dbo].[fds__debuglog]
([CodeReference]
,[ExceptionMessage]
,[StackTrace]
,[Data]
,[timestamp]
,[LogLevel]
,[Machine]
,[Category]
,[Account]
,[AuthUser]
,[IsDebug])
VALUES
(LEFT(COALESCE(NULLIF(@procedure, ''), NULLIF(@category, ''), 'MultiLogger'), 255)
,@error
,@exception
,@data
,GETUTCDATE()
,@loglevel
,LEFT(@machine, 255)
,LEFT(@category, 255)
,NULLIF(LEFT(@account, 100), '')
,NULLIF(LEFT(@authuser, 100), '')
,@isdebug);
END
+11 -2
View File
@@ -4,6 +4,15 @@
[ExceptionMessage] VARCHAR (MAX) NULL,
[StackTrace] VARCHAR (MAX) NULL,
[Data] VARCHAR (MAX) NULL,
[timestamp] DATETIME NULL
[timestamp] DATETIME NULL,
-- Columns below are populated only by rows written through the generic OCORE MultiLogger SQL
-- sink (fds__admin_logdebug_ext, see ADR 0024) — every _logger.LogWarning/-Error/-Critical call
-- at Warning+, attributed automatically. NULL on rows still written by the legacy, explicit
-- fds__admin_logdebug proc call.
[LogLevel] CHAR (1) NULL, -- T/D/I/W/E/C, see OCORE.MultiLogger.SqlLoggerProvider.LogLevelChar
[Machine] VARCHAR (255) NULL, -- Environment.MachineName of the writing process
[Category] VARCHAR (255) NULL, -- ILogger category, e.g. Fuchs.Controllers.IntranetController
[Account] VARCHAR (100) NULL, -- reserved by the shared OCORE proc contract; unused by Fuchs
[AuthUser] VARCHAR (100) NULL, -- fuchs:useraccount_id of the request, when known
[IsDebug] BIT NULL -- true when a debugger was attached to the writing process
);