format-config-extension¶
Context¶
Stogger hardcodes all timestamps to ISO 8601 with microsecond precision. The existing SimpleFormatSettings dataclass is dead config — unreachable from pyproject.toml or public API, with documented values that were never implemented. The broader config layer uses manual _load_config() parsing without validation. This spec addresses both: timestamp format configuration and a proper attrs-based config layer.
Decisions¶
config-layer-attrs¶
Context¶
StoggerConfig uses a manual __init__ with _load_config() that parses TOML, merges kwargs, and extracts ~20 fields via config.get(key, default). No validation, no type safety. SimpleFormatSettings is a separate dataclass that is never constructed from config — always defaults.
Decision¶
Migrate StoggerConfig to attrs classes with proper converters and validators. FormatConfig is a nested attrs class for [tool.stogger.format], following the existing [tool.stogger.ast] precedent. SimpleFormatSettings is deleted — its living fields migrate to FormatConfig.
Alternatives¶
a. Only new FormatConfig as attrs, rest stays manual — rejected, half-cleaned state.
b. Pydantic instead of attrs — rejected, heavy runtime dependency for a 2-dep library.
c. attrs + cattrs — rejected, cattrs is overkill for simple TOML-to-attrs mapping.
d. Keep dataclasses with __post_init__ validation — rejected, reimplements attrs validators.
Consequences¶
attrs becomes a new runtime dependency. Config loading gains type safety and validation. SimpleFormatSettings dies. One-time migration effort for StoggerConfig (~160 lines).
format-config-fields¶
Context¶
SimpleFormatSettings has 6 fields. Three are never used from config (show_logger_brackets, show_pid, timestamp_format). Three are functional (min_level, show_code_info, pad_event_width). A new timestamp_precision field is needed.
Decision¶
FormatConfig (attrs class) contains:
Field |
Type |
Default |
Origin |
|---|---|---|---|
|
|
|
New |
|
|
|
From SimpleFormatSettings |
|
|
|
From SimpleFormatSettings |
|
|
|
From SimpleFormatSettings |
Dead fields (show_logger_brackets, show_pid, timestamp_format) are not migrated.
Alternatives¶
a. Only timestamp_precision in FormatConfig, keep SimpleFormatSettings — rejected, defeats cleanup purpose.
b. All 6 SimpleFormatSettings fields migrated — rejected, includes dead fields.
c. No FormatConfig, fields live in StoggerConfig flat — rejected, loses sub-section structure.
Consequences¶
ConsoleFileRenderer receives FormatConfig instead of SimpleFormatSettings. Four fields total. Config TOML uses [tool.stogger.format] section.
timestamp-precision-values¶
Context¶
structlog’s TimeStamper accepts fmt="iso" (with µs), arbitrary strftime strings, or fmt=None (unix float). The feature needs four format variants with zero validation on invalid input (GIGO).
Decision¶
Four supported values:
Value |
TimeStamper fmt |
Output example |
Description |
|---|---|---|---|
|
|
|
Full precision with µs |
|
|
|
Seconds only |
|
|
|
Seconds, no Z suffix |
|
|
|
Renderer computes delta from process start |
Default changes from "iso" to "iso_seconds" — breaking change, no deprecation. Invalid values pass through to structlog/strftime (GIGO).
Alternatives¶
a. Free-form strftime with “iso” shortcut — rejected, ugly in TOML, validation complexity. b. Two values only (full/seconds) — rejected, drops iso_no_z and relative. c. Validation with error on invalid value — rejected, user wants GIGO.
Consequences¶
Breaking change: existing log output loses microseconds unless user opts into "iso". Three ISO variants handled by TimeStamper fmt parameter. Relative handled by renderer.
pipeline-approach¶
Context¶
Three separate TimeStamper call sites exist (factory.py:41, core.py:507, core.py:582), all hardcoded to fmt="iso". The renderer’s _format_timestamp only strips Z for "iso_no_z", passes everything else through.
Decision¶
ISO variants (iso, iso_seconds, iso_no_z): TimeStamper receives the appropriate fmt parameter. Renderer wraps in ANSI colors, no further transformation needed.
Relative format: TimeStamper keeps fmt="iso" (unchanged). Renderer computes time.time() - format_config._process_start and formats as +X.XXXs. The FormatConfig stores _process_start: float = time.time() at instantiation.
Alternatives¶
a. TimeStamper always fmt=None (unix float), custom processor formats everything — rejected, reimplements TimeStamper.
b. TimeStamper unchanged, renderer strips µs/Z from ISO strings — rejected, wasteful string manipulation.
c. Custom TimestampProcessor replaces TimeStamper — rejected, maintains own timestamp logic.
Consequences¶
Minimal pipeline change for ISO variants (just fmt parameter). Relative needs renderer change and process-start tracking. No new processors added.
call-site-unification¶
Context¶
Three TimeStamper instantiations with inconsistent parameters: init_logging() uses utc=False, the other two use utc=True. One omits key="timestamp".
Decision¶
Central build_timestamp_processor(config) function creates TimeStamper with the correct fmt from config.format.timestamp_precision. All three call sites use this function. All sites normalized to utc=True.
Alternatives¶
a. Config passed to each site, each creates own TimeStamper — rejected, code duplication. b. Only fix factory.py and init_early_logging, leave init_logging — rejected, leaves inconsistency. c. Keep all three hardcoded, renderer handles formatting — rejected, renderer becomes complex.
Consequences¶
Single source of truth for timestamp configuration. init_logging() gains utc=True (was utc=False). Three code paths reduced to one factory function.
test-strategy¶
Context¶
Zero tests exist for timestamp config flowing from TOML through to renderer. Existing tests cover ConsoleFileRenderer with direct SimpleFormatSettings construction.
Decision¶
TDD approach. Spec-validation tests in tests/impl_spec/test_format_config_extension.py (xfail, garbage-collected after passing). Permanent decision tests in tests/test_core.py and tests/test_config.py. Tests cover:
TOML
[tool.stogger.format]loading → FormatConfig field valuesEach
timestamp_precisionvalue produces correct TimeStamper fmtRenderer output for all four format values
Relative format shows elapsed time
GIGO: invalid value produces output without crash
Default is
"iso_seconds"when no config present
Alternatives¶
a. Tests-after — rejected, no spec validation traceability. b. Only extend existing tests — rejected, no config-flow coverage. c. No automated tests — rejected, config pipeline is critical path.
Consequences¶
Full test coverage for config flow. Bidirectional traceability: ADR references test file paths. Spec-validation tests verify design decisions before implementation.