cloud-dog-logging 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cloud_dog_logging-0.4.0/.docs-manifest.yml +6 -0
- cloud_dog_logging-0.4.0/.gitignore +16 -0
- cloud_dog_logging-0.4.0/AGENT-INSTRUCTION-FIX-LOGGING.md +133 -0
- cloud_dog_logging-0.4.0/ARCHITECTURE.md +475 -0
- cloud_dog_logging-0.4.0/BUILD.md +73 -0
- cloud_dog_logging-0.4.0/CHANGELOG.md +49 -0
- cloud_dog_logging-0.4.0/DATA-MODEL.md +33 -0
- cloud_dog_logging-0.4.0/LICENCE +190 -0
- cloud_dog_logging-0.4.0/LICENSE +176 -0
- cloud_dog_logging-0.4.0/NOTICE +7 -0
- cloud_dog_logging-0.4.0/PKG-INFO +23 -0
- cloud_dog_logging-0.4.0/README.md +92 -0
- cloud_dog_logging-0.4.0/REQUIREMENTS.md +282 -0
- cloud_dog_logging-0.4.0/TESTS.md +404 -0
- cloud_dog_logging-0.4.0/adoption_test.py +95 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/GAPS.md +38 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/__init__.py +406 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/app_logger.py +143 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/audit_logger.py +333 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/audit_schema.py +237 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/batching.py +86 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/compat.py +94 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/config.py +248 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/correlation.py +100 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/errors.py +35 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/event_catalogue.py +76 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/exceptions.py +43 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/field_providers.py +227 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/formatters/__init__.py +64 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/formatters/json_formatter.py +326 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/formatters/text_formatter.py +88 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/handler_types.py +103 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/handlers/__init__.py +28 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/handlers/dual_handler.py +82 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/handlers/rotating_file.py +210 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/handlers/stdout_handler.py +49 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/health/__init__.py +26 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/health/reporter.py +121 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/integrity.py +223 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/middleware/__init__.py +27 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/middleware/audit.py +261 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/middleware/fastapi.py +163 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/presets.py +80 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/redaction.py +207 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/sampling.py +82 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/signing.py +78 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/sinks/__init__.py +41 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/sinks/base.py +52 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/sinks/db_sink.py +67 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/sinks/fan_out.py +65 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/sinks/file_sink.py +94 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/sinks/stdout_sink.py +56 -0
- cloud_dog_logging-0.4.0/cloud_dog_logging/tool_events.py +67 -0
- cloud_dog_logging-0.4.0/docs/ARCHITECTURE.md +16 -0
- cloud_dog_logging-0.4.0/docs/CONFIGURATION.md +16 -0
- cloud_dog_logging-0.4.0/docs/EXAMPLES.md +16 -0
- cloud_dog_logging-0.4.0/pyproject.toml +30 -0
- cloud_dog_logging-0.4.0/tests/__init__.py +2 -0
- cloud_dog_logging-0.4.0/tests/application/AT1.1_ServiceStartupPattern/test_service_startup.py +114 -0
- cloud_dog_logging-0.4.0/tests/application/AT1.2_AuditEventCoverage/test_audit_coverage.py +225 -0
- cloud_dog_logging-0.4.0/tests/application/AT1.3_HighVolumeLogging/test_high_volume.py +141 -0
- cloud_dog_logging-0.4.0/tests/application/AT1.4_DatabaseSinkPattern/test_db_sink_pattern.py +87 -0
- cloud_dog_logging-0.4.0/tests/application/AT1.5_SignedAuditChain/test_signed_audit.py +87 -0
- cloud_dog_logging-0.4.0/tests/conftest.py +153 -0
- cloud_dog_logging-0.4.0/tests/env-AT +3 -0
- cloud_dog_logging-0.4.0/tests/env-IT +3 -0
- cloud_dog_logging-0.4.0/tests/env-ST +3 -0
- cloud_dog_logging-0.4.0/tests/env-UT +3 -0
- cloud_dog_logging-0.4.0/tests/integration/IT1.1_FastAPIMiddleware/test_fastapi_middleware.py +126 -0
- cloud_dog_logging-0.4.0/tests/integration/IT1.2_CorrelationPropagation/test_correlation_propagation.py +106 -0
- cloud_dog_logging-0.4.0/tests/integration/IT1.3_RequestResponseLogging/test_request_logging.py +129 -0
- cloud_dog_logging-0.4.0/tests/quality/QT_PUBLISH_COMPLIANCE/__init__.py +2 -0
- cloud_dog_logging-0.4.0/tests/quality/QT_PUBLISH_COMPLIANCE/test_publish_compliance.py +58 -0
- cloud_dog_logging-0.4.0/tests/quality/__init__.py +2 -0
- cloud_dog_logging-0.4.0/tests/system/ST1.1_TwoStreamOutput/test_two_streams.py +137 -0
- cloud_dog_logging-0.4.0/tests/system/ST1.2_FileRotation/test_rotation.py +90 -0
- cloud_dog_logging-0.4.0/tests/system/ST1.3_AppendOnlyAudit/test_append_only.py +101 -0
- cloud_dog_logging-0.4.0/tests/system/ST1.4_LogLevelConfig/test_log_levels.py +69 -0
- cloud_dog_logging-0.4.0/tests/system/ST1.5_DualDestination/test_file_plus_stdout.py +66 -0
- cloud_dog_logging-0.4.0/tests/system/ST1.6_RotationNoLoss/test_rotation_no_loss.py +84 -0
- cloud_dog_logging-0.4.0/tests/system/ST_IntegrityVerifier/test_integrity_periodic.py +169 -0
- cloud_dog_logging-0.4.0/tests/system/ST_RotationEnforcement/test_rotation_enforcement.py +112 -0
- cloud_dog_logging-0.4.0/tests/unit/UT1.10_BackwardCompat/test_setup_logger.py +85 -0
- cloud_dog_logging-0.4.0/tests/unit/UT1.11_SecretScanGuard/test_secret_scan.py +127 -0
- cloud_dog_logging-0.4.0/tests/unit/UT1.12_AuditSinkInterface/test_audit_sink.py +61 -0
- cloud_dog_logging-0.4.0/tests/unit/UT1.13_FileSink/test_file_sink.py +65 -0
- cloud_dog_logging-0.4.0/tests/unit/UT1.14_StdoutSink/test_stdout_sink.py +50 -0
- cloud_dog_logging-0.4.0/tests/unit/UT1.15_FanOutSink/test_fan_out_sink.py +73 -0
- cloud_dog_logging-0.4.0/tests/unit/UT1.16_AuditSigning/test_signing.py +91 -0
- cloud_dog_logging-0.4.0/tests/unit/UT1.17_ToolEventHelper/test_tool_events.py +67 -0
- cloud_dog_logging-0.4.0/tests/unit/UT1.18_RedactionPresets/test_presets.py +48 -0
- cloud_dog_logging-0.4.0/tests/unit/UT1.19_LogSampling/test_sampling.py +46 -0
- cloud_dog_logging-0.4.0/tests/unit/UT1.1_JSONFormatter/test_json_formatter.py +144 -0
- cloud_dog_logging-0.4.0/tests/unit/UT1.20_AuditBatching/test_batching.py +87 -0
- cloud_dog_logging-0.4.0/tests/unit/UT1.21_ExceptionSerialisation/test_exception_format.py +81 -0
- cloud_dog_logging-0.4.0/tests/unit/UT1.2_TextFormatter/test_text_formatter.py +80 -0
- cloud_dog_logging-0.4.0/tests/unit/UT1.3_RedactionEngine/test_redaction.py +117 -0
- cloud_dog_logging-0.4.0/tests/unit/UT1.4_CorrelationID/test_correlation.py +113 -0
- cloud_dog_logging-0.4.0/tests/unit/UT1.5_AuditSchema/test_audit_schema.py +227 -0
- cloud_dog_logging-0.4.0/tests/unit/UT1.6_AuditLogger/test_audit_logger.py +169 -0
- cloud_dog_logging-0.4.0/tests/unit/UT1.7_AppLogger/test_app_logger.py +85 -0
- cloud_dog_logging-0.4.0/tests/unit/UT1.8_LoggerFactory/test_logger_factory.py +57 -0
- cloud_dog_logging-0.4.0/tests/unit/UT1.9_HealthReporter/test_health_reporter.py +89 -0
- cloud_dog_logging-0.4.0/tests/unit/UT_HandlerEnum/test_handler_enum.py +223 -0
- cloud_dog_logging-0.4.0/tests/unit/UT_IntegrityVerifier/test_integrity_verifier.py +158 -0
- cloud_dog_logging-0.4.0/tests/unit/UT_JSONFieldRegistry/test_json_field_registry.py +208 -0
- cloud_dog_logging-0.4.0/tests/unit/UT_LogRecordFactory/test_log_record_factory.py +204 -0
- cloud_dog_logging-0.4.0/tests/unit/UT_NIST_AU3/test_nist_au3_fields.py +136 -0
- cloud_dog_logging-0.4.0/working/W28A-316-INTEGRITY-FIX-REPORT.md +107 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Agent Instruction — Fix cloud_dog_logging (v0.2.0)
|
|
2
|
+
|
|
3
|
+
**Package:** `cloud_dog_logging`
|
|
4
|
+
**Target version:** 0.2.0
|
|
5
|
+
**Date:** 2026-02-18 (updated with full gap analysis)
|
|
6
|
+
**Scope:** 7 new features (FR1.18–FR1.24) — **ALL DELIVERED AND VERIFIED**
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Status: ✅ COMPLETE
|
|
11
|
+
|
|
12
|
+
All 7 issues from cross-project impact assessment have been implemented, tested, and verified. This document is retained for reference and future maintenance.
|
|
13
|
+
|
|
14
|
+
**Verified on 2026-02-18:**
|
|
15
|
+
- 201 tests passed (IT env), 0 failed, 0 skipped
|
|
16
|
+
- Uplift scope: 33 passed (targeted v0.2.0 tests)
|
|
17
|
+
- Lint and format clean (`ruff check` + `ruff format --check`)
|
|
18
|
+
- Build produces `cloud_dog_logging-0.2.0.tar.gz` + `cloud_dog_logging-0.2.0-py3-none-any.whl`
|
|
19
|
+
- All 32 SA1 modules present
|
|
20
|
+
- All 35 test directories present and matching TESTS.md (21 UT + 6 ST + 3 IT + 5 AT)
|
|
21
|
+
- Zero config-delegation violations (no `os.environ`/`hvac`/Vault reads)
|
|
22
|
+
|
|
23
|
+
**Governing documents:**
|
|
24
|
+
1. `platform-logging/REQUIREMENTS.md` (v0.2.0) — FR1.18–FR1.24
|
|
25
|
+
2. `platform-logging/ARCHITECTURE.md` (v0.2.0) — CC1.11–CC1.17
|
|
26
|
+
3. `platform-logging/TESTS.md` (v0.2.0) — UT1.12–UT1.21, AT1.4–AT1.5
|
|
27
|
+
4. `packages/backend/AGENT-INSTRUCTION.md` — Integrity Warranty and Config Delegation — ZERO TOLERANCE (MANDATORY)
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Delivery Summary
|
|
32
|
+
|
|
33
|
+
### Issue 1 — Pluggable Audit Sink ✅ DELIVERED
|
|
34
|
+
|
|
35
|
+
**FR:** FR1.18 | **Architecture:** CC1.11 | **Tests:** UT1.12–UT1.15, AT1.4
|
|
36
|
+
|
|
37
|
+
- `cloud_dog_logging/sinks/base.py` — `AuditSink` protocol (`emit`, `flush`, `close`) + `AuditRepository` protocol for DB sinks
|
|
38
|
+
- `cloud_dog_logging/sinks/file_sink.py` — `FileSink` JSONL file output
|
|
39
|
+
- `cloud_dog_logging/sinks/stdout_sink.py` — `StdoutSink` stdout output
|
|
40
|
+
- `cloud_dog_logging/sinks/db_sink.py` — `DatabaseSink` via `AuditRepository` protocol
|
|
41
|
+
- `cloud_dog_logging/sinks/fan_out.py` — `FanOutSink` multi-sink dispatch (one failure doesn't block others)
|
|
42
|
+
- `audit_logger.py` refactored to accept `sink` and `signer` parameters
|
|
43
|
+
- `__init__.py` → `_build_audit_sink()` wires sinks from `LogConfig`
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
### Issue 2 — Audit Signing Hooks ✅ DELIVERED
|
|
48
|
+
|
|
49
|
+
**FR:** FR1.19 | **Architecture:** CC1.12 | **Tests:** UT1.16, AT1.5
|
|
50
|
+
|
|
51
|
+
- `cloud_dog_logging/signing.py` — `AuditSigner` protocol + `HMACSigner` (HMAC-SHA256 with hash chaining)
|
|
52
|
+
- `pre_persist`: adds `_signature` and `_prev_signature` to event details
|
|
53
|
+
- `post_persist`: updates chain with latest signature
|
|
54
|
+
- Integrated into `AuditLogger` via `signer` parameter; `_build_signer()` in `__init__.py`
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
### Issue 3 — Tool Event Helper ✅ DELIVERED
|
|
59
|
+
|
|
60
|
+
**FR:** FR1.20 | **Architecture:** CC1.13 | **Tests:** UT1.17
|
|
61
|
+
|
|
62
|
+
- `cloud_dog_logging/tool_events.py` — `log_tool_event(tool, profile, duration_ms, paths, outcome, **details)`
|
|
63
|
+
- Generates `AuditEvent` with `event_type="tool_call"`, auto-attaches correlation ID and service name
|
|
64
|
+
- Exported from `__init__.py`
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
### Issue 4 — Redaction Presets ✅ DELIVERED
|
|
69
|
+
|
|
70
|
+
**FR:** FR1.21 | **Architecture:** CC1.14 | **Tests:** UT1.18
|
|
71
|
+
|
|
72
|
+
- `cloud_dog_logging/presets.py` — `RedactionPreset` frozen dataclass, `BUILTIN_PRESETS` dict (`default` + `file_tools`), `load_presets(config)` with config-driven composition
|
|
73
|
+
- `RedactionEngine` accepts `presets` parameter
|
|
74
|
+
- `setup_logging()` resolves presets from config via `_resolve_redaction_presets()`
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
### Issue 5 — Log Sampling ✅ DELIVERED
|
|
79
|
+
|
|
80
|
+
**FR:** FR1.22 | **Architecture:** CC1.15 | **Tests:** UT1.19
|
|
81
|
+
|
|
82
|
+
- `cloud_dog_logging/sampling.py` — `SamplingFilter(logging.Filter)` with per-logger rates, hierarchical name lookup, `sampled_out_count` metric
|
|
83
|
+
- WARNING+ always passes; audit events unaffected
|
|
84
|
+
- Wired into `setup_logging()` via `LogConfig.sampling_rates`
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
### Issue 6 — Audit Event Batching ✅ DELIVERED
|
|
89
|
+
|
|
90
|
+
**FR:** FR1.23 | **Architecture:** CC1.16 | **Tests:** UT1.20
|
|
91
|
+
|
|
92
|
+
- `cloud_dog_logging/batching.py` — `BatchingSink` wrapping any `AuditSink` with configurable `batch_size` (default 100) and `flush_interval_s` (default 5.0)
|
|
93
|
+
- Thread-safe with `threading.Lock`; ordering preserved; flush on batch full/interval/close
|
|
94
|
+
- Supports `emit_batch()` on underlying sink if available
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
### Issue 7 — Structured Exception Logging ✅ DELIVERED
|
|
99
|
+
|
|
100
|
+
**FR:** FR1.24 | **Architecture:** CC1.17 | **Tests:** UT1.21
|
|
101
|
+
|
|
102
|
+
- `cloud_dog_logging/exceptions.py` — `format_exception(exc)` returning `type`, `message`, `stack_hash` (SHA-256), `traceback` (list of frame strings)
|
|
103
|
+
- Stable `stack_hash` enables deduplication across identical exceptions
|
|
104
|
+
- Exported from `__init__.py`
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Public API Exports
|
|
109
|
+
|
|
110
|
+
All new APIs exported from `cloud_dog_logging/__init__.py`:
|
|
111
|
+
- `log_tool_event`, `format_exception`
|
|
112
|
+
- `AuditSink`, `FileSink`, `StdoutSink`, `DatabaseSink`, `FanOutSink`
|
|
113
|
+
- `BatchingSink`, `HMACSigner`
|
|
114
|
+
- `RedactionPreset`, `BUILTIN_PRESETS`, `load_presets`
|
|
115
|
+
- `SamplingFilter`
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Verification — Full Suite
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
pytest tests --env tests/env-IT -q
|
|
123
|
+
ruff check cloud_dog_logging tests
|
|
124
|
+
ruff format --check cloud_dog_logging tests
|
|
125
|
+
python -m build --no-isolation
|
|
126
|
+
find cloud_dog_logging -name '*.py' -not -path '*__pycache__*' | sort
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## pyproject.toml version
|
|
130
|
+
|
|
131
|
+
```toml
|
|
132
|
+
version = "0.2.0"
|
|
133
|
+
```
|
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
# platform-logging — Architecture
|
|
2
|
+
|
|
3
|
+
**Package:** `cloud_dog_logging`
|
|
4
|
+
**Version:** 0.3.0
|
|
5
|
+
**Standard:** PS-40 (Logging & Observability)
|
|
6
|
+
**Status:** Implemented
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## OV1 — Overview
|
|
11
|
+
|
|
12
|
+
`cloud_dog_logging` is a drop-in Python library that implements the PS-40 logging and observability standard. It provides two mandatory log streams (audit + application), structured JSON output, correlation ID propagation, secret redaction, and configurable rotation — all behind a simple factory interface.
|
|
13
|
+
|
|
14
|
+
### Design Goals
|
|
15
|
+
|
|
16
|
+
- **Single implementation** of the two-stream logging pattern — no per-project reimplementation.
|
|
17
|
+
- **Zero external dependencies** for core functionality (stdlib `logging` only).
|
|
18
|
+
- **Backward compatible** with existing `setup_logger()` patterns in notification-agent / expert-agent.
|
|
19
|
+
- **Framework-optional**: core library has no web framework dependency; FastAPI middleware is optional.
|
|
20
|
+
- **Async-safe**: compatible with asyncio event loops.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## SA1 — Module Layout
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
cloud_dog_logging/
|
|
28
|
+
__init__.py # Public API: get_logger, get_audit_logger, setup_logger
|
|
29
|
+
config.py # Log configuration from platform config (PS-80)
|
|
30
|
+
app_logger.py # Application logger (structured JSON, levels)
|
|
31
|
+
audit_logger.py # Audit logger (append-only, typed events)
|
|
32
|
+
audit_schema.py # Audit event schema / models
|
|
33
|
+
correlation.py # Correlation ID context (contextvars)
|
|
34
|
+
redaction.py # Secret + PII redaction engine
|
|
35
|
+
formatters/
|
|
36
|
+
__init__.py
|
|
37
|
+
json_formatter.py # Structured JSON Lines formatter
|
|
38
|
+
text_formatter.py # Human-readable formatter (dev mode)
|
|
39
|
+
handlers/
|
|
40
|
+
__init__.py
|
|
41
|
+
rotating_file.py # Size + time-based rotating file handler
|
|
42
|
+
stdout_handler.py # Stdout/stderr handler (containers)
|
|
43
|
+
dual_handler.py # File + stdout simultaneously
|
|
44
|
+
middleware/
|
|
45
|
+
__init__.py
|
|
46
|
+
fastapi.py # FastAPI request logging + correlation ID middleware
|
|
47
|
+
health/
|
|
48
|
+
__init__.py
|
|
49
|
+
reporter.py # Log file size, rotation status, audit event count
|
|
50
|
+
compat.py # Backward-compatible setup_logger() function
|
|
51
|
+
errors.py # Logging-specific exceptions
|
|
52
|
+
sinks/
|
|
53
|
+
__init__.py
|
|
54
|
+
base.py # AuditSink protocol (FR1.18)
|
|
55
|
+
file_sink.py # FileSink — JSONL file output
|
|
56
|
+
stdout_sink.py # StdoutSink — stdout output
|
|
57
|
+
db_sink.py # DatabaseSink — DB table output (optional)
|
|
58
|
+
fan_out.py # FanOutSink — multiple sinks simultaneously
|
|
59
|
+
signing.py # Audit signing hooks (FR1.19)
|
|
60
|
+
tool_events.py # log_tool_event() helper (FR1.20)
|
|
61
|
+
presets.py # Redaction presets (FR1.21)
|
|
62
|
+
sampling.py # Log sampling filter (FR1.22)
|
|
63
|
+
batching.py # Audit event batching (FR1.23)
|
|
64
|
+
exceptions.py # Structured exception serialisation (FR1.24)
|
|
65
|
+
event_catalogue.py # Optional event catalogue validator (AU-2/AU-6)
|
|
66
|
+
integrity.py # Audit integrity verifier (FR1.25/FR1.26)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## SA2 — Component Diagram
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
┌──────────────────────────────────────────────────────────────────┐
|
|
75
|
+
│ Service (FastAPI / CLI) │
|
|
76
|
+
│ │
|
|
77
|
+
│ middleware/fastapi.py ──→ inject correlation_id │
|
|
78
|
+
│ │ log request/response │
|
|
79
|
+
│ ▼ │
|
|
80
|
+
│ ┌──────────────┐ ┌──────────────┐ │
|
|
81
|
+
│ │ get_logger() │ │ get_audit_ │ │
|
|
82
|
+
│ │ → app_logger │ │ logger() │ │
|
|
83
|
+
│ │ │ │ → audit_ │ │
|
|
84
|
+
│ │ │ │ logger │ │
|
|
85
|
+
│ └──────┬───────┘ └──────┬───────┘ │
|
|
86
|
+
│ │ │ │
|
|
87
|
+
│ │ redaction.py ◄────────┤ │
|
|
88
|
+
│ │ (applied to both) │ │
|
|
89
|
+
│ │ │ │
|
|
90
|
+
│ ▼ ▼ │
|
|
91
|
+
│ ┌──────────────┐ ┌──────────────┐ │
|
|
92
|
+
│ │ formatters/ │ │ formatters/ │ │
|
|
93
|
+
│ │ json or text │ │ json only │ │
|
|
94
|
+
│ └──────┬───────┘ └──────┬───────┘ │
|
|
95
|
+
│ │ │ │
|
|
96
|
+
│ ▼ ▼ │
|
|
97
|
+
│ ┌──────────────┐ ┌──────────────┐ │
|
|
98
|
+
│ │ handlers/ │ │ handlers/ │ │
|
|
99
|
+
│ │ rotating_file│ │ rotating_file│ │
|
|
100
|
+
│ │ + stdout │ │ (append-only)│ │
|
|
101
|
+
│ └──────────────┘ └──────────────┘ │
|
|
102
|
+
│ │ │ │
|
|
103
|
+
│ ▼ ▼ │
|
|
104
|
+
│ logs/app.log logs/audit.log.jsonl │
|
|
105
|
+
│ (or stdout) (or stdout) │
|
|
106
|
+
└──────────────────────────────────────────────────────────────────┘
|
|
107
|
+
│
|
|
108
|
+
└──→ correlation.py (contextvars: correlation_id per request)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## CC1 — Core Components
|
|
114
|
+
|
|
115
|
+
### CC1.1 Application Logger (`app_logger.py`)
|
|
116
|
+
|
|
117
|
+
Standard Python logger with structured JSON output:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
class AppLogger:
|
|
121
|
+
"""Structured application logger."""
|
|
122
|
+
|
|
123
|
+
def debug(self, msg, **extra): ...
|
|
124
|
+
def info(self, msg, **extra): ...
|
|
125
|
+
def warning(self, msg, **extra): ...
|
|
126
|
+
def error(self, msg, **extra): ...
|
|
127
|
+
def critical(self, msg, **extra): ...
|
|
128
|
+
def exception(self, msg, **extra): ...
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
- All entries include: `timestamp`, `level`, `logger`, `message`, `correlation_id`, `service`, `extra`.
|
|
132
|
+
- Extra fields are redacted before output.
|
|
133
|
+
- Built on stdlib `logging.Logger` — compatible with existing Python logging ecosystem.
|
|
134
|
+
|
|
135
|
+
### CC1.2 Audit Logger (`audit_logger.py`)
|
|
136
|
+
|
|
137
|
+
Typed audit event logger with mandatory schema:
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
class AuditLogger:
|
|
141
|
+
"""Append-only audit event logger."""
|
|
142
|
+
|
|
143
|
+
def emit(self, event: AuditEvent) -> None:
|
|
144
|
+
"""Emit a raw audit event."""
|
|
145
|
+
|
|
146
|
+
def log_login(self, actor: Actor, outcome: str, **details) -> None: ...
|
|
147
|
+
def log_crud(self, actor: Actor, action: str, target: Target, outcome: str, **details) -> None: ...
|
|
148
|
+
def log_config_change(self, actor: Actor, diff_summary: dict, outcome: str, **details) -> None: ...
|
|
149
|
+
def log_tool_call(self, actor: Actor, tool: str, params: dict, outcome: str, duration_ms: int, **details) -> None: ...
|
|
150
|
+
def log_security(self, actor: Actor, action: str, target: Target, outcome: str, **details) -> None: ...
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
- All events validated against `AuditEvent` schema before writing.
|
|
154
|
+
- Details field scanned for secrets and redacted.
|
|
155
|
+
- Append-only: handler configured to not truncate/overwrite.
|
|
156
|
+
|
|
157
|
+
### CC1.3 Audit Event Schema (`audit_schema.py`)
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
@dataclass
|
|
161
|
+
class Actor:
|
|
162
|
+
type: str # "user" | "service" | "system"
|
|
163
|
+
id: str # Stable user/service identifier
|
|
164
|
+
roles: list[str] | None = None
|
|
165
|
+
|
|
166
|
+
@dataclass
|
|
167
|
+
class Target:
|
|
168
|
+
type: str # "user" | "session" | "config" | "api_key" | etc.
|
|
169
|
+
id: str # Target entity identifier
|
|
170
|
+
|
|
171
|
+
@dataclass
|
|
172
|
+
class AuditEvent:
|
|
173
|
+
timestamp: str # ISO 8601 UTC
|
|
174
|
+
event_type: str # e.g., "user.login", "config.reload"
|
|
175
|
+
actor: Actor
|
|
176
|
+
action: str # "create" | "update" | "delete" | "login" | etc.
|
|
177
|
+
outcome: str # "success" | "failure" | "error"
|
|
178
|
+
correlation_id: str
|
|
179
|
+
service: str
|
|
180
|
+
target: Target | None = None
|
|
181
|
+
details: dict | None = None
|
|
182
|
+
duration_ms: int | None = None
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### CC1.4 Correlation ID (`correlation.py`)
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
# Context-local correlation ID using contextvars
|
|
189
|
+
correlation_id_var: ContextVar[str]
|
|
190
|
+
|
|
191
|
+
def get_correlation_id() -> str:
|
|
192
|
+
"""Get current correlation ID (generates new if none set)."""
|
|
193
|
+
|
|
194
|
+
def set_correlation_id(cid: str) -> None:
|
|
195
|
+
"""Set correlation ID for current context."""
|
|
196
|
+
|
|
197
|
+
def correlation_id_middleware(header_name: str = "X-Request-Id"):
|
|
198
|
+
"""Extract or generate correlation ID from request header."""
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
- `contextvars.ContextVar` ensures async-safety.
|
|
202
|
+
- All loggers automatically read from context.
|
|
203
|
+
|
|
204
|
+
### CC1.5 Redaction Engine (`redaction.py`)
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
class RedactionEngine:
|
|
208
|
+
"""Redact secrets and PII from log data."""
|
|
209
|
+
|
|
210
|
+
def __init__(self, patterns: list[str] | None = None): ...
|
|
211
|
+
def redact(self, data: dict) -> dict: ...
|
|
212
|
+
def redact_string(self, value: str) -> str: ...
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
- Default patterns: keys containing `secret`, `password`, `key`, `token`, `credential`, `api_key`.
|
|
216
|
+
- Configurable additional patterns per project.
|
|
217
|
+
- Recursive dict/list scanning.
|
|
218
|
+
- Values replaced with `***REDACTED***`.
|
|
219
|
+
|
|
220
|
+
### CC1.6 JSON Formatter (`formatters/json_formatter.py`)
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
class JSONFormatter(logging.Formatter):
|
|
224
|
+
"""Structured JSON Lines formatter for both log streams."""
|
|
225
|
+
|
|
226
|
+
def format(self, record: LogRecord) -> str: ...
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
- One JSON object per line.
|
|
230
|
+
- Includes all required fields from FR1.3/FR1.4.
|
|
231
|
+
- Handles exceptions (serialises traceback as string).
|
|
232
|
+
|
|
233
|
+
### CC1.7 Rotating File Handler (`handlers/rotating_file.py`)
|
|
234
|
+
|
|
235
|
+
```python
|
|
236
|
+
class ConfigurableRotatingHandler(logging.Handler):
|
|
237
|
+
"""Size + time-based rotation with retention."""
|
|
238
|
+
|
|
239
|
+
def __init__(
|
|
240
|
+
self,
|
|
241
|
+
filename: str,
|
|
242
|
+
max_bytes: int = 10_485_760, # 10MB
|
|
243
|
+
backup_count: int = 5,
|
|
244
|
+
when: str = "midnight", # time-based trigger
|
|
245
|
+
interval: int = 1,
|
|
246
|
+
): ...
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
- Combines `RotatingFileHandler` and `TimedRotatingFileHandler` behaviour.
|
|
250
|
+
- Configurable via platform config (`log.rotation.*`).
|
|
251
|
+
- Rotation MUST NOT lose log entries.
|
|
252
|
+
|
|
253
|
+
### CC1.8 FastAPI Middleware (`middleware/fastapi.py`)
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
class LoggingMiddleware:
|
|
257
|
+
"""FastAPI middleware for request logging and correlation ID."""
|
|
258
|
+
|
|
259
|
+
async def dispatch(self, request, call_next):
|
|
260
|
+
# 1. Extract or generate correlation ID
|
|
261
|
+
# 2. Set in contextvars
|
|
262
|
+
# 3. Log request start
|
|
263
|
+
# 4. Call next
|
|
264
|
+
# 5. Log request end (status, duration, client IP)
|
|
265
|
+
# 6. Add X-Request-Id to response headers
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### CC1.9 Backward Compatibility (`compat.py`)
|
|
269
|
+
|
|
270
|
+
```python
|
|
271
|
+
def setup_logger(
|
|
272
|
+
name: str,
|
|
273
|
+
log_file: str,
|
|
274
|
+
log_level: str = "INFO",
|
|
275
|
+
log_format: str = "json",
|
|
276
|
+
console: bool = True,
|
|
277
|
+
) -> logging.Logger:
|
|
278
|
+
"""Backward-compatible setup matching existing project patterns."""
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Drop-in replacement for the `setup_logger()` function used in notification-agent and expert-agent.
|
|
282
|
+
|
|
283
|
+
### CC1.10 Health Reporter (`health/reporter.py`)
|
|
284
|
+
|
|
285
|
+
```python
|
|
286
|
+
class LogHealthReporter:
|
|
287
|
+
"""Observability data for log subsystem."""
|
|
288
|
+
|
|
289
|
+
def get_status(self) -> dict:
|
|
290
|
+
"""Returns: file sizes, rotation status, audit event count, last audit timestamp."""
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### CC1.11 Audit Sink Interface (`sinks/base.py`)
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
class AuditSink(Protocol):
|
|
297
|
+
def emit(self, event: AuditEvent) -> None: ...
|
|
298
|
+
def flush(self) -> None: ...
|
|
299
|
+
def close(self) -> None: ...
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Built-in implementations:
|
|
303
|
+
- `FileSink` — writes JSONL to file (existing behaviour, extracted).
|
|
304
|
+
- `StdoutSink` — writes to stdout.
|
|
305
|
+
- `DatabaseSink` — writes to DB table via repository protocol (optional dependency).
|
|
306
|
+
- `FanOutSink` — dispatches to multiple sinks simultaneously.
|
|
307
|
+
|
|
308
|
+
### CC1.12 Audit Signing (`signing.py`)
|
|
309
|
+
|
|
310
|
+
```python
|
|
311
|
+
class AuditSigner(Protocol):
|
|
312
|
+
def pre_persist(self, event: AuditEvent) -> AuditEvent: ...
|
|
313
|
+
def post_persist(self, event: AuditEvent) -> None: ...
|
|
314
|
+
|
|
315
|
+
class HMACSigner:
|
|
316
|
+
"""HMAC-SHA256 signing for tamper-evident audit records."""
|
|
317
|
+
def __init__(self, secret_key: str): ...
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
- Invoked by `AuditLogger` before/after sink emit.
|
|
321
|
+
- Disabled by default; enabled via config (`log.audit.signing.enabled`).
|
|
322
|
+
|
|
323
|
+
### CC1.13 Tool Event Helper (`tool_events.py`)
|
|
324
|
+
|
|
325
|
+
```python
|
|
326
|
+
def log_tool_event(
|
|
327
|
+
tool: str,
|
|
328
|
+
profile: str | None = None,
|
|
329
|
+
duration_ms: int | None = None,
|
|
330
|
+
paths: list[str] | None = None,
|
|
331
|
+
outcome: str = "success",
|
|
332
|
+
**details,
|
|
333
|
+
) -> None:
|
|
334
|
+
"""Convenience helper for MCP/tool audit events."""
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### CC1.14 Redaction Presets (`presets.py`)
|
|
338
|
+
|
|
339
|
+
```python
|
|
340
|
+
class RedactionPreset:
|
|
341
|
+
name: str
|
|
342
|
+
patterns: list[str]
|
|
343
|
+
|
|
344
|
+
BUILTIN_PRESETS: dict[str, RedactionPreset] = {
|
|
345
|
+
"default": RedactionPreset(name="default", patterns=[...]),
|
|
346
|
+
"file_tools": RedactionPreset(name="file_tools", patterns=[...]),
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
def load_presets(config: dict) -> list[RedactionPreset]:
|
|
350
|
+
"""Load and compose redaction presets from config."""
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### CC1.15 Log Sampling (`sampling.py`)
|
|
354
|
+
|
|
355
|
+
```python
|
|
356
|
+
class SamplingFilter(logging.Filter):
|
|
357
|
+
"""Per-logger sampling for high-volume DEBUG logs."""
|
|
358
|
+
|
|
359
|
+
def __init__(self, rates: dict[str, float]): ...
|
|
360
|
+
def filter(self, record: LogRecord) -> bool: ...
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
- Only applies to DEBUG level; WARNING+ always passes.
|
|
364
|
+
- Counted metrics for sampled-out entries.
|
|
365
|
+
|
|
366
|
+
### CC1.16 Audit Batching (`batching.py`)
|
|
367
|
+
|
|
368
|
+
```python
|
|
369
|
+
class BatchingSink:
|
|
370
|
+
"""Wraps a sink with batch flush semantics."""
|
|
371
|
+
|
|
372
|
+
def __init__(self, sink: AuditSink, batch_size: int = 100, flush_interval_s: float = 5.0): ...
|
|
373
|
+
def emit(self, event: AuditEvent) -> None: ...
|
|
374
|
+
def flush(self) -> None: ...
|
|
375
|
+
def close(self) -> None: ...
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
- Flush on batch size reached, interval elapsed, or shutdown signal.
|
|
379
|
+
- Ordering preserved within batch.
|
|
380
|
+
|
|
381
|
+
### CC1.17 Structured Exception Serialisation (`exceptions.py`)
|
|
382
|
+
|
|
383
|
+
```python
|
|
384
|
+
def format_exception(exc: BaseException) -> dict:
|
|
385
|
+
"""Serialise exception with type, message, stack_hash, traceback."""
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
- `stack_hash` = SHA-256 of normalised traceback string (for dedup).
|
|
389
|
+
- Used by `AppLogger.exception()` and audit event details.
|
|
390
|
+
|
|
391
|
+
### CC1.18 Audit Integrity Verifier (`integrity.py`)
|
|
392
|
+
|
|
393
|
+
```python
|
|
394
|
+
class AuditIntegrityVerifier:
|
|
395
|
+
"""Periodic hash verification for audit log files."""
|
|
396
|
+
|
|
397
|
+
def start(self) -> None: ...
|
|
398
|
+
def stop(self) -> None: ...
|
|
399
|
+
def compute_now(self, trigger: str = "manual") -> dict: ...
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
- Computes `sha256` (default), `sha512`, or `crc32` hashes over full audit log content.
|
|
403
|
+
- Writes integrity records to both application logs and `logs/audit-integrity.log`.
|
|
404
|
+
- Emits records on `startup`, `periodic`, `rotation`, `manual`, and `shutdown`.
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
## DM1 — Data Model
|
|
409
|
+
|
|
410
|
+
No persistent database. Logs are file-based or stdout-based.
|
|
411
|
+
|
|
412
|
+
### Log File Convention
|
|
413
|
+
|
|
414
|
+
```
|
|
415
|
+
logs/
|
|
416
|
+
app.log # Application log (current)
|
|
417
|
+
app.log.1 # Rotated application logs
|
|
418
|
+
app.log.2
|
|
419
|
+
audit.log.jsonl # Audit log (current, append-only)
|
|
420
|
+
audit.log.jsonl.1 # Rotated audit logs
|
|
421
|
+
audit-integrity.log # Integrity verification JSONL records
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
File paths configurable via platform config (`log.app_log`, `log.audit_log`).
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
## DP1 — Dependency Policy
|
|
429
|
+
|
|
430
|
+
| Dependency | Status | Notes |
|
|
431
|
+
|-----------|--------|-------|
|
|
432
|
+
| stdlib `logging` | Required | Core logging framework |
|
|
433
|
+
| `python-json-logger` | Optional | Enhanced JSON formatting (falls back to built-in) |
|
|
434
|
+
|
|
435
|
+
No web framework dependency. No database dependency. No external service dependency.
|
|
436
|
+
|
|
437
|
+
---
|
|
438
|
+
|
|
439
|
+
## SE1 — Security Architecture
|
|
440
|
+
|
|
441
|
+
- Secret redaction applied to all log output paths before writing.
|
|
442
|
+
- Audit log is append-only — handlers configured to not truncate.
|
|
443
|
+
- Correlation IDs contain no sensitive information (UUID or hex token).
|
|
444
|
+
- PII redaction configurable per project.
|
|
445
|
+
- Log files SHOULD have restricted filesystem permissions (configurable).
|
|
446
|
+
|
|
447
|
+
---
|
|
448
|
+
|
|
449
|
+
## Integration Pattern
|
|
450
|
+
|
|
451
|
+
Services consume the package as follows:
|
|
452
|
+
|
|
453
|
+
```python
|
|
454
|
+
from cloud_dog_logging import get_logger, get_audit_logger, setup_logging
|
|
455
|
+
from cloud_dog_logging.audit_schema import Actor, Target
|
|
456
|
+
|
|
457
|
+
# At startup (once)
|
|
458
|
+
setup_logging(config) # Reads log.* from GlobalConfig
|
|
459
|
+
|
|
460
|
+
# In application code
|
|
461
|
+
logger = get_logger(__name__)
|
|
462
|
+
logger.info("Processing request", extra={"user_id": user.id})
|
|
463
|
+
|
|
464
|
+
# For security events
|
|
465
|
+
audit = get_audit_logger()
|
|
466
|
+
audit.log_login(
|
|
467
|
+
actor=Actor(type="user", id=str(user.id)),
|
|
468
|
+
outcome="success",
|
|
469
|
+
ip=request.client.host,
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
# For FastAPI
|
|
473
|
+
from cloud_dog_logging.middleware.fastapi import LoggingMiddleware
|
|
474
|
+
app.add_middleware(LoggingMiddleware)
|
|
475
|
+
```
|