# 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 consumer supplies its own `TLogItem : AcLogItemClient` (typically marked `[AcBinarySerializable]`, so the generated serializer lives in the consumer 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` |
