mirror of
https://github.com/RedWizardsLab/EchoHub.git
synced 2026-09-06 07:36:01 +02:00
docs: Update documentation for 145 files
Generated by AurionDocs
Job ID: 934f8c39-8082-4942-8d17-72ed8f5f8d50
Source commit: 40aea9a
This commit is contained in:
+23
-43
@@ -7,18 +7,20 @@
|
||||
```mermaid
|
||||
%%{init: {'theme':'base','themeVariables':{'background':'#faf7ef','primaryColor':'#f0e2c2','primaryTextColor':'#1f2840','primaryBorderColor':'#8a7548','secondaryColor':'#d9efec','secondaryBorderColor':'#1d8a80','secondaryTextColor':'#1f2840','tertiaryColor':'#f2ebd8','tertiaryBorderColor':'#8a7548','tertiaryTextColor':'#1f2840','lineColor':'#1d8a80','titleColor':'#1f2840','fontSize':'14px','edgeLabelBackground':'#faf7ef','clusterBkg':'#f2ebd8','clusterBorder':'#8a7548','actorBkg':'#f0e2c2','actorBorder':'#8a7548','actorTextColor':'#1f2840','actorLineColor':'#8a7548','signalColor':'#1d8a80','signalTextColor':'#1f2840','activationBkgColor':'#d9efec','activationBorderColor':'#1d8a80','noteBkgColor':'#f2ebd8','noteBorderColor':'#8a7548','noteTextColor':'#1f2840','labelBoxBkgColor':'#f0e2c2','labelBoxBorderColor':'#8a7548','labelTextColor':'#1f2840','transitionColor':'#1d8a80','transitionLabelColor':'#1f2840','stateLabelColor':'#1f2840','altBackground':'#f2ebd8'}}}%%
|
||||
flowchart TB
|
||||
A[ServerLogsService ReadBacklog]
|
||||
A --> B[Resolve ServerLogsOptions LogDirectory]
|
||||
B --> Dir{Directory exists}
|
||||
Dir -->|Yes| C[Find newest file matching LogFilePattern]
|
||||
Dir -->|No| E[Return empty list]
|
||||
C --> F{Newest file found}
|
||||
F -->|No| E
|
||||
F -->|Yes| G[Open newest file with FileShare ReadWrite Delete then seek to tail when stream longer than TailReadBytes]
|
||||
G --> H[Read to end and split by newline into lines]
|
||||
H --> I[Group lines into LogBacklogEntry with skipLeadingContinuations if seeked and limit from ServerLogsOptions BacklogLines]
|
||||
I --> J[Return list of LogBacklogEntry]
|
||||
A -.-> E
|
||||
ServerLogsService["Start ReadBacklog()"]
|
||||
ServerLogsOptions["Load ServerLogsOptions (LogDirectory, LogFilePattern, BacklogLines)"]
|
||||
LogBacklogEntry["Return IReadOnlyList of LogBacklogEntry (backlog or empty)"]
|
||||
|
||||
ServerLogsService -->|"Resolve full path of LogDirectory"| ServerLogsOptions
|
||||
ServerLogsOptions -->|"If directory does not exist -> return empty list"| LogBacklogEntry
|
||||
ServerLogsOptions -->|"Find newest file matching LogFilePattern (order by LastWriteTimeUtc)"| ServerLogsService
|
||||
ServerLogsService -->|"If no newest file -> return empty list"| LogBacklogEntry
|
||||
ServerLogsService -->|"Open FileStream(newest.FullName, FileMode.Open, FileAccess.Read, FileShare ReadWrite and Delete)"| ServerLogsOptions
|
||||
ServerLogsOptions -->|"Determine if stream.Length > TailReadBytes (seeked)"| ServerLogsService
|
||||
ServerLogsService -->|"If seeked -> stream.Seek(-TailReadBytes, SeekOrigin.End)"| ServerLogsOptions
|
||||
ServerLogsOptions -->|"Read remainder with StreamReader and split into lines"| ServerLogsService
|
||||
ServerLogsService -->|"Call GroupIntoEntries(lines, skipLeadingContinuations: seeked, BacklogLines)"| LogBacklogEntry
|
||||
ServerLogsService -->|"On any exception -> return empty list"| LogBacklogEntry
|
||||
```
|
||||
|
||||
## Contents
|
||||
@@ -37,37 +39,16 @@ public sealed class ServerLogsService
|
||||
```
|
||||
|
||||
|
||||
Provides the logic needed to present a read-only "live logs" room: it exposes the room identity, the role-based gate for who may join, and a best-effort reader that returns the most recent log entries from the active rolling Serilog file. Use this service when you need to show a live, read-only backlog of server log entries (rather than storing log lines as chat messages).
|
||||
Provides utilities for exposing a read-only, live server log room: it knows the room identity and access gate and can read the tail of the current rolling log file into `LogBacklogEntry` items for display. Use `ServerLogsService` when you need to determine whether a channel is the configured logs room, check whether a role may view logs, or retrieve a best-effort backlog snapshot from the most recent log file (rather than relying on persisted messages).
|
||||
|
||||
## Remarks
|
||||
This class centralizes the concerns required for a live log room: determining the configured room name and sender, enforcing the minimum role required to view logs, and extracting a focused backlog from the current log file on disk. It treats the file sink as the single source of truth (Serilog keeps the file open and rolls it), reads only the tail of the newest file up to a bounded byte size, and groups raw lines into logical entries by detecting timestamp-prefixed lines. ReadBacklog is resilient: any I/O or parsing problem yields an empty backlog rather than propagating an error.
|
||||
|
||||
## Example
|
||||
```csharp
|
||||
// 'options' is an existing ServerLogsOptions instance configured for the server.
|
||||
var service = new ServerLogsService(options);
|
||||
|
||||
// Check whether a user role may view/join the live logs room
|
||||
if (service.CanView(userRole))
|
||||
{
|
||||
// Read the most recent backlog entries (best-effort; may be empty on error)
|
||||
var backlog = service.ReadBacklog();
|
||||
foreach (var entry in backlog)
|
||||
{
|
||||
// LogBacklogEntry exposes a timestamp and the concatenated content
|
||||
Console.WriteLine($"{entry.Timestamp:O} {entry.Content}");
|
||||
}
|
||||
}
|
||||
|
||||
// Room identity helpers
|
||||
var isLogs = service.IsLogsChannel("logs");
|
||||
var sender = ServerLogsService.SenderName; // "server"
|
||||
```
|
||||
`ServerLogsService` centralizes the concerns around presenting live server logs without persisting log lines as messages. It uses the configured [`ServerLogsOptions`](../../Config/ServerLogsOptions.cs.md) to decide whether logging is enabled, to match a channel name (`NormalizedRoomName`) in `IsLogsChannel`, and to gate access with `CanView` based on `MinRole`. For backlog retrieval, `ReadBacklog` opens the newest file matching `LogFilePattern` in `LogDirectory` with `FileShare.ReadWrite | FileShare.Delete` (to cooperate with a rolling sink like Serilog), reads up to `TailReadBytes` from the file end, and converts raw lines into `LogBacklogEntry` instances via `GroupIntoEntries`. The `GroupIntoEntries` method is public to allow unit testing of the timestamp-based grouping logic.
|
||||
|
||||
## Notes
|
||||
- ReadBacklog swallows all exceptions and returns an empty list on any I/O problem; callers must tolerate an empty backlog as a sign of transient failure or missing files.
|
||||
- To avoid reading an arbitrarily large file, the reader seeks to the last TailReadBytes bytes; that can start the scan mid-entry, so the grouping logic optionally drops leading continuation lines when the tail was seeked.
|
||||
- The FileStream is opened with FileShare.ReadWrite | FileShare.Delete because the Serilog file sink typically keeps the file open for writing and may roll it; the service reads concurrently without taking exclusive locks.
|
||||
- `IsLogsChannel` calls `Trim()` on the provided `channelName`; passing `null` will throw a `NullReferenceException` — callers should ensure they pass a non-null string or guard accordingly.
|
||||
- `ReadBacklog` is intentionally best-effort: it catches all exceptions and returns an empty list on any I/O or parsing failure. This prevents join failures but can hide filesystem problems; monitor logs or surface errors elsewhere if you need diagnostics.
|
||||
- The grouping logic depends on lines that start with the timestamp format defined by `TimestampFormat`. If your log sink uses a different timestamp template, `GroupIntoEntries` will treat those timestamped lines as continuations and entries will be merged incorrectly.
|
||||
- When the newest file is larger than `TailReadBytes`, `ReadBacklog` seeks into the file and sets `skipLeadingContinuations` so a partial entry at the seek boundary is dropped. This is deliberate to avoid presenting truncated entries but means very long single entries near the file end can be partially excluded.
|
||||
|
||||
---
|
||||
|
||||
@@ -87,13 +68,12 @@ public record LogBacklogEntry(DateTimeOffset Timestamp, string Content)
|
||||
| `Content` | `string` | — |
|
||||
|
||||
|
||||
LogBacklogEntry is a tiny, immutable data container that models a single backlog item read from the server log file. It captures the timestamp of the original log line via Timestamp and the associated log text in Content, which may include the initial line plus any continuation lines (such as exception stack traces) that followed it. Use this type when you need to treat a complete backlog segment as a unit, instead of handling raw lines individually; it’s especially helpful for grouping, displaying, or analyzing backlog entries after parsing.
|
||||
LogBacklogEntry is an immutable value object that captures a backlog entry read from the log file. It consists of a timestamp (`Timestamp`) and the associated content (`Content`), representing the first line of the backlog entry plus any continuation lines (such as exception stack traces) that followed it.
|
||||
|
||||
## Remarks
|
||||
Because LogBacklogEntry is a record, it benefits from value-based equality and concise deconstruction, making it easy to compare backlog entries or to extract the fields in pattern-matching. The Content field holds a multi-line string that includes the initial line and any continuation text that followed it; consumers should be aware that the entry may span multiple lines. This type is commonly produced by the server log reader (e.g., ServerLogsService) when assembling backlog entries from the log file, serving as a stable data carrier between parsing and presentation layers.
|
||||
Because this is a `record`, it provides value-based equality and deconstruction, which simplifies comparing backlog entries and passing them through the processing pipeline without mutation. It acts as a lightweight data carrier that decouples raw log parsing from higher-level log aggregation or display concerns, allowing the server logs service to operate on coherent chunks of log data.
|
||||
|
||||
## Notes
|
||||
- When collecting backlog lines, ensure that each entry groups the initial timestamped line with its subsequent continuation lines exactly once; splitting or merging entries incorrectly can corrupt the log's temporal grouping.
|
||||
|
||||
- The `Content` may be large and contain newline characters representing multi-line stack traces; treat it as an opaque blob when storing or transmitting.
|
||||
|
||||
---
|
||||
@@ -8,12 +8,12 @@ public sealed class ServerLogsSink : ILogEventSink
|
||||
```
|
||||
|
||||
|
||||
ServerLogsSink is a Serilog sink that feeds the live log room by buffering log events in a bounded channel and exposing them to the streaming pipeline without persisting them. It enforces a minimum log level, filters out internal sources to avoid feedback loops, and writes accepted events into a single-reader queue consumed by the live broadcast path. This sink thus serves as a lightweight, non-persistent conduit for real-time visibility of logging activity.
|
||||
ServerLogsSink is a Serilog sink that buffers recent `LogEvent`s into a bounded, single-reader [`Channel<LogEvent>`](../../../EchoHub.Core/Models/Channel.cs.md) and exposes a `Reader` for the live log streaming path. It enforces a minimum level via the `ServerLogsOptions.MinLevel` and filters out internal streaming sources using `ExcludedSourcePrefixes` to prevent a feedback loop where a log would broadcast and re-log itself.
|
||||
|
||||
## Remarks
|
||||
The sink decouples log emission from the live broadcast pathway, providing backpressure via a bounded channel (capacity 512) with drop-oldest semantics to prevent unbounded memory growth. Internal pipeline events are culled by inspecting the SourceContext and excluding known internal prefixes, which prevents the log → broadcast → log feedback loop. By not writing to a database, the component prioritizes timely visibility for operators and clients over long-term auditing.
|
||||
Serving as a bridge between Serilog and the live log room, `ServerLogsSink` deliberately does not write to a database; events are queued for streaming consumption by [`ServerLogsStreamService`](ServerLogsStreamService.cs.md). The channel is sized with a capacity of 512 and uses `BoundedChannelFullMode.DropOldest` with `SingleReader = true`, which preserves the most recent events while avoiding unbounded memory growth. The internal filtering — checking `Constants.SourceContextPropertyName` and skipping any source that starts with entries in `ExcludedSourcePrefixes` — protects against recursive logging from the streaming infrastructure.
|
||||
|
||||
## Notes
|
||||
- When the channel is full, TryWrite may return false and the log event will be dropped, ensuring the application does not stall due to logging backpressure.
|
||||
- The channel is configured with SingleReader = true, so there is a single consumer in the streaming path; additional readers would not receive the full event sequence.
|
||||
- Only events that pass the MinLevel filter and do not originate from excluded internal sources are enqueued for broadcast.
|
||||
- The bound buffer capacity is 512 and uses `BoundedChannelFullMode.DropOldest`; when full, the oldest buffered events are dropped to make room for newer ones.
|
||||
- `Emit` uses `TryWrite` and ignores the return value; under load, logs may be dropped if the consumer lags behind.
|
||||
- Internal sources are excluded by prefix; adding new internal namespaces requires updating `ExcludedSourcePrefixes` to avoid self-logging.
|
||||
|
||||
+54
-24
@@ -8,41 +8,71 @@ public sealed class ServerLogsStreamService : BackgroundService
|
||||
```
|
||||
|
||||
|
||||
Streams queued log events to the live log room as ephemeral SignalR messages, never persisting them to the IRC gateway or a database, and with a guard against introducing new logging from the streaming path itself. It runs as a background service, ensuring the destination room exists before sending each event and recreating it if needed, so live viewers can always join the stream without manual intervention.
|
||||
Description:
|
||||
|
||||
Streams queued log events to the live log room as ephemeral SignalR messages, ensuring the room exists before each publish and recreating it on demand if it was removed. The streaming path is intentionally non-logging to avoid recursive logging and potential message sprawl. Use this service when you want real-time, in-memory broadcasts of server log events to connected clients without persisting those lines to a database.
|
||||
|
||||
## Remarks
|
||||
This symbol acts as a thin, resilient bridge between the server-side log sink and the real-time chat hub. It separates the streaming path from log persistence, enforcing a no-log-from-stream policy to avoid feedback loops where streaming would itself generate more log lines. By lazily resolving the hub context, it avoids tight coupling during service construction and ensures the ChatHub context is available when streaming begins. The class also enforces room existence in a lightweight, interval-bounded way to tolerate transient room removal without blocking the live stream.
|
||||
|
||||
This symbol acts as the dedicated conduit between the server-side log sink and the live chat hub. It coordinates with a channel service to guarantee the existence of the log room (and to recreate it if it disappears), throttling such housekeeping to at most once every 15 seconds to avoid excessive churn. Messages are encrypted before transmission and delivered to the room group via a lazily-resolved `HubContext`, which is intentionally retrieved only after the host has fully configured its DI graph. The static `Format` helper is public for tests, enabling validation of the exact, client-rendered payload without instantiating the streaming pipeline. This separation keeps streaming concerns isolated from the rest of the logging infrastructure and prevents per-event logging from leaking into the stream itself.
|
||||
|
||||
## Notes
|
||||
- The streaming path must never emit logs of its own activity; per-event logging is explicitly suppressed to prevent cascading streams.
|
||||
- TryEnsureRoomAsync re-checks the room at most once per EnsureInterval (15 seconds) to balance responsiveness with avoiding repeated recreation attempts.
|
||||
- Messages are formatted and then encrypted before sending; the client receives an encrypted payload and is responsible for decrypting it, mirroring the design that prioritizes privacy and transport safety. The public Format method is exposed for tests, reflecting a desire to validate formatting behavior in isolation.
|
||||
- If room recreation fails, events are streamed to a group with no members until the next interval, ensuring that the streaming pipeline remains non-blocking and resilient to transient failures.
|
||||
|
||||
- The service reads from `ServerLogsSink.Reader` and, for each event, ensures the destination room exists, formats the log event, encrypts the payload, and sends it to the [`ChatHub`](../../Hubs/ChatHub.cs.md) group corresponding to the room name.
|
||||
- Room creation/verification is throttled by `EnsureInterval` (15 seconds) to avoid excessive calls during high-frequency log bursts; failed attempts are silently retried on the next interval.
|
||||
- The streaming path is guarded to swallow non-cancellation exceptions to prevent re-entrancy into the logging pipeline.
|
||||
- The `Format` method is intentionally public for testability, and truncates messages to `HubConstants.MaxMessageLength` with an ellipsis when necessary.
|
||||
|
||||
## Example
|
||||
- Not included: non-obvious usage from the signature; the behavior is exercised through the background streaming loop and the TryEnsureRoomAsync room-recovery logic. See the source for exact flow and state transitions.
|
||||
|
||||
```csharp
|
||||
// The example demonstrates formatting a log event for client rendering and ensuring the message is wrapped for transport.
|
||||
var logEvent = new LogEvent(/* parameters omitted for brevity */);
|
||||
var payload = ServerLogsStreamService.Format(logEvent);
|
||||
// payload is then embedded in a [`MessageDto`](../../../EchoHub.Core/DTOs/ChatDtos.cs.md), encrypted, and sent to the SignalR hub.
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
- SignalR, BackgroundService, MessageDto, StringBuilder, TimeSpan, DateTimeOffset, Reader, Guid
|
||||
|
||||
## Dependency APIs (verified signatures)
|
||||
|
||||
The REAL, parser-verified API surface of this symbol's collaborators:
|
||||
|
||||
- MessageDto (src/EchoHub.Core/DTOs/ChatDtos.cs)
|
||||
- Reader (src/EchoHub.Server/Services/ServerLogs/ServerLogsSink.cs)
|
||||
- ServerLogsService (src/EchoHub.Server/Services/ServerLogs/ServerLogsService.cs)
|
||||
- SenderName, RoomTopic, TimestampFormat, TailReadBytes
|
||||
- ServerLogsService(ServerLogsOptions options)
|
||||
- Options, IsLogsChannel, CanView(ServerRole)
|
||||
- ReadBacklog(), GroupIntoEntries(`IReadOnlyList<string>`, bool, int)
|
||||
- TryParseTimestamp(string, out DateTimeOffset, out string)
|
||||
- HubContext (src/EchoHub.Server/Services/ServerLogs/ServerLogsStreamService.cs)
|
||||
- HubConstants (src/EchoHub.Core/Constants/HubConstants.cs)
|
||||
- ChatHubPath, DefaultChannel, IrcConnectionIdPrefix, DefaultHistoryCount, MaxMessageLength
|
||||
- MaxImageSizeBytes, MaxAudioFileSizeBytes, MaxFileSizeBytes, MaxAvatarSizeBytes
|
||||
- MaxMessageNewlines, MaxAttachmentsPerMessage, MaxConsecutiveNewlines
|
||||
- record [`MessageDto`](../../../EchoHub.Core/DTOs/ChatDtos.cs.md) (`src/EchoHub.Core/DTOs/ChatDtos.cs`)
|
||||
- property `Reader` (`src/EchoHub.Server/Services/ServerLogs/ServerLogsSink.cs`)
|
||||
- class [`ServerLogsService`](ServerLogsService.cs.md) (`src/EchoHub.Server/Services/ServerLogs/ServerLogsService.cs`)
|
||||
- field `string SenderName`
|
||||
- field `string RoomTopic`
|
||||
- field `string TimestampFormat`
|
||||
- field `int TailReadBytes`
|
||||
- `ServerLogsService(ServerLogsOptions options)`
|
||||
- property `ServerLogsOptions Options`
|
||||
- `bool IsLogsChannel(string channelName)`
|
||||
- `bool CanView(ServerRole role)`
|
||||
- `IReadOnlyList<LogBacklogEntry> ReadBacklog()`
|
||||
- `IReadOnlyList<LogBacklogEntry> GroupIntoEntries(IReadOnlyList<string> lines, bool skipLeadingContinuations, int maxEntries)`
|
||||
- `bool TryParseTimestamp(string line, out DateTimeOffset timestamp, out string rest)`
|
||||
- property `HubContext` (`src/EchoHub.Server/Services/ServerLogs/ServerLogsStreamService.cs`)
|
||||
- class [`HubConstants`](../../../EchoHub.Core/Constants/HubConstants.cs.md) (`src/EchoHub.Core/Constants/HubConstants.cs`)
|
||||
- field `string ChatHubPath`
|
||||
- field `string DefaultChannel`
|
||||
- field `string IrcConnectionIdPrefix`
|
||||
- field `int DefaultHistoryCount`
|
||||
- field `int MaxMessageLength`
|
||||
- field `int MaxImageSizeBytes`
|
||||
- field `int MaxAudioFileSizeBytes`
|
||||
- field `int MaxFileSizeBytes`
|
||||
- field `int MaxAvatarSizeBytes`
|
||||
- field `int MaxMessageNewlines`
|
||||
- field `int MaxAttachmentsPerMessage`
|
||||
- field `int MaxConsecutiveNewlines`
|
||||
- …and 7 more member(s) not shown
|
||||
|
||||
## Symbol To Document
|
||||
- Name: ServerLogsStreamService
|
||||
- Name: `ServerLogsStreamService`
|
||||
- Kind: class
|
||||
- File: src/EchoHub.Server/Services/ServerLogs/ServerLogsStreamService.cs
|
||||
- Language: csharp
|
||||
- ID: fe54ac96-642e-4dbe-af25-3d2559e01299
|
||||
- File: `src/EchoHub.Server/Services/ServerLogs/ServerLogsStreamService.cs`
|
||||
- Language: `csharp`
|
||||
- ID: 24389698-e5ce-4385-b392-f34e08edf31f
|
||||
|
||||
Reference in New Issue
Block a user