Introduced generic `AcLoggerSignalRHub<TLogger, TLogItem>` and `AcSignaRClientLogItemWriter<TLogItem>` for flexible log item wire types (AcBinary/JSON). Updated writer to accept protocol options and enforce feedback-loop prevention. Improved documentation to clarify generic usage, protocol selection, and migration. Added TODOs for protocol-diagnostic logger and log noise reduction. Refactored exception handling and signatures for generics; updated issue/TODO tracking and enhanced code comments. |
||
|---|---|---|
| .. | ||
| README.md | ||
README.md
Remote Log Writers
Client-side log writers sending log data to remote endpoints. Source: Loggers/. Core logging framework: AyCode.Core/AyCode.Core/docs/LOGGING/README.md | Server-side GlobalLogger: AyCode.Core.Server/docs/LOGGING/README.md.
AcBrowserConsoleLogWriter
Blazor browser console writer. Extends AcTextLogWriterBase (text branch, not structured). Uses IJSRuntime to invoke browser console methods:
| LogLevel | JS method |
|---|---|
| Detail – Suggest | console.info |
| Warning | console.warn |
| Error | console.error |
Implements IAcLogWriterClientBase (client-side marker).
AcHttpClientLogItemWriter<TLogItem>
HTTP POST writer. Extends AcLogItemWriterBase<TLogItem> (structured branch). Manages its own HttpClient + HttpClientHandler. Sends log items as JSON:
_httpClient.PostAsJsonAsync(_url, logItem).Forget();
Note: Forget() — fire-and-forget, no await on the HTTP response. HTTP/2 by default.
AcSignaRClientLogItemWriter
SignalR log transport writer, generic in the wire type (2026-07): AcSignaRClientLogItemWriter<TLogItem> : AcLogItemWriterBase<TLogItem> — the app supplies its own TLogItem (e.g. FruitBank's LogItemClient : AcLogItemClient with [AcBinarySerializable], so the generated serializer lives in the app layer and AyCode.Entities stays generator-free). The non-generic AcSignaRClientLogItemWriter shim (= <AcLogItemClient>, JSON) keeps existing consumers working. Sends items to a dedicated logger hub. Manages its own HubConnection:
await _hubConnection.SendAsync("AddLogItem", logItem);
Protocol: default JSON; pass AcBinaryHubProtocolOptions in the ctor to switch the connection to AcBinary. ⚠️ Feedback-loop guard: the protocol's Logger MUST be deaf (NullLogger.Instance) and the writer's builder must never get AddAcDefaults/ConfigureLogging — the protocol's own logs would re-enter this uploader (log → serialize → log → … runaway loop). The server counterpart's TLogItem (see AcLoggerSignalRHub<TLogger, TLogItem>) must match the client's wire type.
Failure reporting: never silent — connect failures, dropped items and recovery are reported once per failure state via Console.Error (a log channel cannot log its own errors through itself); a connected; url: … marker prints once per established connection.
UTC conversion note: SignalR doesn't transmit DateTime.Kind. The writer calls logItem.TimeStampUtc.ToUniversalTime() before sending to ensure the server receives actual UTC.
Connection lifecycle: StartConnection() waits up to 10s for Connected state. StopConnection() stops and disposes the hub.
Remote Logging Flow
Client applications can send log items to a server via SignalR:
Client App Server
────────── ──────
AcSignaRClientLogItemWriter AcLoggerSignalRHub<TLogger>
│ │
│ logItem.ToUniversalTime() │
│ StartConnection() │
├──SendAsync("AddLogItem", item)──► │
│ ├─ logItem.TimeStampUtc = UtcNow
│ ├─ _logger.Write(logItem)
│ │ ├─ Console writer
│ │ ├─ DB writer
│ │ └─ ... (all server writers)
Note: AcLoggerSignalRHub overrides client's TimeStampUtc with DateTime.UtcNow server-side for authoritative timestamps. The hub is a simple Hub (not AcWebSignalRHubBase) — does NOT use tag-based dispatch.
AcLoggerSignalRHub<TLogger, TLogItem> (2026-07): the wire type is generic — SignalR deserializes into TLogItem, so it must equal the client writer's TLogItem (with [AcBinarySerializable(false)] there is no polymorph dispatch). The single-generic AcLoggerSignalRHub<TLogger> shim (= <TLogger, AcLogItem>) remains for JSON clients. The hub also logs connection lifecycle (Logger client connected/disconnected; ConnectionId: …) so a dead upload channel is visible on the server console.
Key Source Files
| Component | Path |
|---|---|
| Browser writer | Loggers/AcBrowserConsoleLogWriter.cs |
| HTTP writer | Loggers/AcHttpClientLogItemWriter.cs |
| SignalR writer | Loggers/AcSignaRClientLogItemWriter.cs |
| Structured writer base | AyCode.Entities/AcLogItemWriterBase.cs |
| DB writer | AyCode.Database/AcDbLogItemWriter.cs |
| Logger hub (server) | AyCode.Services.Server/SignalRs/AcLoggerSignalRHub.cs |