76 lines
4.6 KiB
Markdown
76 lines
4.6 KiB
Markdown
# 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:
|
||
|
||
```csharp
|
||
_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`:
|
||
|
||
```csharp
|
||
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` |
|