feat: migrate DB error logging onto the generic OCORE MultiLogger SQL sink
Playwright Tests / test (push) Canceled after 0s
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:
co-authored by
Claude Sonnet 5
parent
a1e6002c3b
commit
bcf1f2dbb6
@@ -0,0 +1,41 @@
|
||||
using Fuchs.intranet;
|
||||
|
||||
namespace Fuchs.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// Pushes a per-request logging scope (request id, path, and — once cookie auth has populated
|
||||
/// <see cref="HttpContext.User"/> — the authenticated user id) so every log line written while
|
||||
/// handling this request, including ones written deep inside OCORE (SQL errors, PDF render
|
||||
/// failures, ...), carries that attribution automatically. See ADR 0024: the scope flows into
|
||||
/// every sink's serialized context (<c>LogEntry.dataSerialized</c>, via
|
||||
/// <see cref="OCORE.MultiLogger.MultiLogger_base"/>'s context merging) and — for "authuser"
|
||||
/// specifically — into the dedicated <see cref="OCORE.MultiLogger.LogEntry.AuthUser"/> column the
|
||||
/// SQL sink writes to <c>fds__debuglog.AuthUser</c>.
|
||||
/// Registered after <c>UseAuthentication()</c> so the user claim is already populated.
|
||||
/// </summary>
|
||||
public sealed class RequestLogScopeMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
public RequestLogScopeMiddleware(RequestDelegate next) => _next = next;
|
||||
|
||||
public async Task InvokeAsync(HttpContext context, ILogger<RequestLogScopeMiddleware> logger)
|
||||
{
|
||||
using (logger.BeginScope(BuildScope(context)))
|
||||
await _next(context);
|
||||
}
|
||||
|
||||
/// <summary>Pure so the scope shape is unit-testable without a live request pipeline.</summary>
|
||||
internal static Dictionary<string, object> BuildScope(HttpContext context)
|
||||
{
|
||||
var scope = new Dictionary<string, object>
|
||||
{
|
||||
["requestId"] = context.TraceIdentifier,
|
||||
["path"] = context.Request.Path.Value ?? "",
|
||||
};
|
||||
string? authUser = context.User?.FindFirst(FuchsUserIdentity.ClaimUserAccountId)?.Value;
|
||||
if (!string.IsNullOrEmpty(authUser))
|
||||
scope["authuser"] = authUser;
|
||||
return scope;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user