Skip to content

Commit 0c1fa97

Browse files
committed
validate add-runtime-logging-support
1 parent 7f22108 commit 0c1fa97

4 files changed

Lines changed: 138 additions & 0 deletions

File tree

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
## Context
2+
- Hosts call `AddLogging()` with defaults and runtime bootstraps its own `LoggerFactory.Create(builder => builder.AddConsole())`, producing inconsistent providers and no file sink.
3+
- Module providers and runtime loaders often receive `NullLogger`, so module discovery/load errors are invisible once the process exits.
4+
- There is no shared configuration contract for logging, no host/module context enrichment, and no retention policy for operators.
5+
6+
## Goals / Non-Goals
7+
- Goals: unify logging pipeline across hosts/runtime, support configurable console + file sinks with sane defaults, include host/module context in log scopes, and keep retention predictable.
8+
- Non-Goals: remote log shipping/observability (Elastic/App Insights), in-app log viewer UI, or distributed tracing.
9+
10+
## Decisions
11+
- Logging stack: keep `Microsoft.Extensions.Logging` as the abstraction and add Serilog sinks (via `Serilog.Extensions.Logging` + `Serilog.Sinks.File`) for file output—no custom file writers; console uses built-in MEL console.
12+
- Configuration contract: `Logging:Console:{Enabled,Level,Format}` and `Logging:File:{Enabled,Level,Path,RollingInterval,FileSizeLimitMB,RetainedFileCountLimit}` with environment variable overrides supported by default binding.
13+
- Defaults: console enabled at `Information`, file enabled at `Information`, rolling daily or 10 MB size (whichever first), retain last 10 files.
14+
- Paths: default logs folder at `%AppData%/Modulus/Logs/{HostType}/` on Windows and `$HOME/.modulus/logs/{hostType}/` elsewhere; filenames include host + date to avoid clashes across hosts.
15+
- Context enrichment: attach `HostType`, `ModuleId`, and `ModuleVersion` via logging scopes at module entry points; lifecycle log messages use structured properties instead of string concatenation.
16+
- Compatibility: replace ad-hoc `LoggerFactory.Create` usage with the configured factory from DI so modules/host share the same providers and filters.
17+
- Module usage: modules resolve `ILogger<T>`/`ILoggerFactory` from DI (host-owned pipeline) and MUST NOT add providers or change logging configuration; host/runtime owns configuration surface and may ignore/reject module-level reconfiguration attempts.
18+
19+
## Risks / Trade-offs
20+
- Additional Serilog dependencies increase package surface; mitigated by using minimal packages (Serilog.Extensions.Logging + Serilog.Sinks.File).
21+
- File IO and retention can increase disk usage; mitigated by size/time rolling and retention limits with documented defaults.
22+
- Concurrent hosts or multiple instances could contend for the same log file; mitigated by host-specific filenames and optional path override.
23+
24+
## Migration Plan
25+
- Introduce the configuration contract with defaults so existing hosts get console + file logging without code changes beyond wiring the pipeline.
26+
- Update runtime bootstrap to consume the DI-provided `ILoggerFactory` and remove `NullLogger` placeholders.
27+
- Add enrichment helpers/scopes and apply them around module discovery/load/init/shutdown.
28+
29+
## Open Questions
30+
- Do we need JSON vs plaintext for file format? (default to JSON for structure unless operators request plaintext.)
31+
- Should we expose a CLI/management endpoint to download logs, or keep out-of-scope for now?
32+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Change: Runtime logging with console and file sinks
2+
3+
## Why
4+
- Hosts and runtime rely on ad-hoc console logging or `NullLogger`, leaving module discovery/load issues without diagnostics once the process exits.
5+
- There is no file-based runtime log, so production incidents cannot be reconstructed from user machines.
6+
- Logging configuration is not centralized or host-aware, and log entries lack host/module context for troubleshooting.
7+
8+
## What Changes
9+
- Introduce a unified logging configuration (console + file) driven by `appsettings`/environment with defaults for levels, retention, and paths.
10+
- Enable structured file logging with rolling retention under a predictable host-specific logs folder, while keeping console logging configurable (default on for development).
11+
- Propagate a single `ILoggerFactory` into runtime/module contexts and enrich entries with host type and module identity.
12+
- Add lifecycle logging for module discovery/load/init/shutdown and manifest validation so failures surface in both console and file sinks.
13+
14+
## Impact
15+
- Affected specs: logging (new)
16+
- Affected code: Modulus.Core runtime bootstrap (ModulusApplicationFactory, ModuleLoader/Manager/LoadContext), host startup (Avalonia/Blazor builders), host configuration files
17+
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
## ADDED Requirements
2+
3+
### Requirement: Logging configuration
4+
The host SHALL expose a shared logging configuration that controls console and file sinks for runtime and modules.
5+
6+
#### Scenario: Default configuration applied
7+
- **WHEN** the host starts without custom logging settings
8+
- **THEN** console logging is enabled at Information level
9+
- **AND** file logging is enabled at Information level
10+
- **AND** the log directory defaults to the host-specific logs folder.
11+
12+
#### Scenario: Environment overrides configuration
13+
- **WHEN** logging settings (levels, enablement, paths) are provided via appsettings or environment variables
14+
- **THEN** the runtime applies the overridden values without code changes
15+
- **AND** the settings flow to all runtime and module loggers.
16+
17+
### Requirement: Console logging
18+
The system SHALL emit console logs for runtime and module events when console logging is enabled.
19+
20+
#### Scenario: Console output on startup
21+
- **WHEN** the host starts with console logging enabled
22+
- **THEN** startup events (database init, module discovery, module load begin/end) are written to STDOUT with timestamp and level.
23+
24+
#### Scenario: Console logging can be disabled
25+
- **WHEN** console logging is disabled via configuration
26+
- **THEN** runtime and module logs stop writing to the console sink.
27+
28+
### Requirement: File runtime logging
29+
The system SHALL write runtime and module logs to a rolling file sink with retention using Serilog (MEL provider + Serilog file sink), avoiding custom file writers.
30+
31+
#### Scenario: File log created with defaults
32+
- **WHEN** the host starts with file logging enabled
33+
- **THEN** a log file is created under the configured logs directory (default `%AppData%/Modulus/Logs/{Host}/` or `$HOME/.modulus/logs/{host}/`)
34+
- **AND** runtime/module log entries are appended with timestamps and levels.
35+
36+
#### Scenario: Rolling and retention enforced
37+
- **WHEN** a log file exceeds the configured size limit or rolling interval
38+
- **THEN** the file is rolled with an incremented or timestamped suffix
39+
- **AND** only the configured number of retained log files is kept.
40+
41+
#### Scenario: Serilog file sink in use
42+
- **WHEN** file logging is enabled
43+
- **THEN** the logging pipeline uses the Serilog MEL provider with the Serilog file sink
44+
- **AND** no custom file writer is used for runtime or module logging.
45+
46+
### Requirement: Log context enrichment
47+
Runtime log entries SHALL include host and module context.
48+
49+
#### Scenario: Host log context
50+
- **WHEN** the host emits a log entry
51+
- **THEN** the entry includes the host type identifier (e.g., Avalonia, Blazor).
52+
53+
#### Scenario: Module log context
54+
- **WHEN** code inside a loaded module emits a log entry
55+
- **THEN** the entry includes the module id and version when available
56+
- **AND** the host type context remains present.
57+
58+
### Requirement: Runtime lifecycle logging coverage
59+
The runtime SHALL log critical lifecycle events for module discovery, validation, load, initialization, and shutdown.
60+
61+
#### Scenario: Module load success
62+
- **WHEN** a module is loaded successfully
63+
- **THEN** an info-level log is written with module id, version, and dependency count.
64+
65+
#### Scenario: Module load failure
66+
- **WHEN** a module fails to load or initialize
67+
- **THEN** an error-level log is written with module id, path, and exception details
68+
- **AND** the failure is persisted to the file sink.
69+
70+
### Requirement: Module logging usage
71+
Modules SHALL use the host-provided logging pipeline without redefining or redirecting logging configuration.
72+
73+
#### Scenario: Module obtains logger
74+
- **WHEN** a module requests `ILogger<T>` or `ILoggerFactory` from DI
75+
- **THEN** it receives the host-configured logging pipeline (console/file Serilog) with host and module context enrichment.
76+
77+
#### Scenario: Module cannot reconfigure logging
78+
- **WHEN** a module attempts to add/replace logging providers or change logging levels/paths
79+
- **THEN** the attempt is ignored or rejected
80+
- **AND** the host-owned logging configuration remains active for all runtime and module logs.
81+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
## 1. Implementation
2+
- [ ] 1.1 Add shared `Logging` configuration for console/file sinks (enable flags, levels, path, rolling/retention) with appsettings + environment overrides in both hosts.
3+
- [ ] 1.2 Wire Serilog via `Serilog.Extensions.Logging` + `Serilog.Sinks.File` as the file sink (no custom writers) and initialize a single logging pipeline (console + file) from configuration; pass the configured `ILoggerFactory` into Modulus runtime components instead of ad-hoc factories/null loggers.
4+
- [ ] 1.3 Enrich log scopes with host type and module identity and ensure runtime components use these scopes during module discovery, load, initialization, and shutdown.
5+
- [ ] 1.4 Add structured lifecycle logs for manifest validation, dependency graph building, module load/init/unload, and host startup/shutdown with clear levels/event IDs.
6+
- [ ] 1.5 Add validation/smoke coverage that a log file is created, rolling/retention works, console output remains available when enabled, and the Serilog file sink is active (e.g., host-level test or manual check guidance).
7+
- [ ] 1.6 Ensure modules resolve `ILogger<T>`/`ILoggerFactory` from DI (shared host pipeline) and cannot add/replace logging providers or change logging configuration; document or enforce rejection/ignore behavior for module-level reconfiguration attempts.
8+

0 commit comments

Comments
 (0)