hexastack-logging 0.0.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.
- hexastack_logging-0.0.0/PKG-INFO +152 -0
- hexastack_logging-0.0.0/README.md +132 -0
- hexastack_logging-0.0.0/pyproject.toml +34 -0
- hexastack_logging-0.0.0/pyproject.toml.orig +35 -0
- hexastack_logging-0.0.0/src/hexastack_logging/__init__.py +6 -0
- hexastack_logging-0.0.0/src/hexastack_logging/adapters/__init__.py +13 -0
- hexastack_logging-0.0.0/src/hexastack_logging/adapters/logger/__init__.py +11 -0
- hexastack_logging-0.0.0/src/hexastack_logging/adapters/logger/loguru.py +133 -0
- hexastack_logging-0.0.0/src/hexastack_logging/adapters/logger/rich.py +131 -0
- hexastack_logging-0.0.0/src/hexastack_logging/adapters/logger/structlog.py +128 -0
- hexastack_logging-0.0.0/src/hexastack_logging/adapters/logger/structured.py +139 -0
- hexastack_logging-0.0.0/src/hexastack_logging/infra/__init__.py +31 -0
- hexastack_logging-0.0.0/src/hexastack_logging/infra/bootstrap.py +55 -0
- hexastack_logging-0.0.0/src/hexastack_logging/infra/config.py +254 -0
- hexastack_logging-0.0.0/src/hexastack_logging/infra/filters.py +131 -0
- hexastack_logging-0.0.0/src/hexastack_logging/infra/formatters/__init__.py +9 -0
- hexastack_logging-0.0.0/src/hexastack_logging/infra/formatters/console.py +82 -0
- hexastack_logging-0.0.0/src/hexastack_logging/infra/formatters/json.py +51 -0
- hexastack_logging-0.0.0/src/hexastack_logging/infra/sanitizer.py +154 -0
- hexastack_logging-0.0.0/src/hexastack_logging/py.typed +0 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: hexastack-logging
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
Author: Richard West
|
|
6
|
+
Author-email: Richard West <dopplereffect.us@gmail.com>
|
|
7
|
+
Requires-Dist: hexastack-core
|
|
8
|
+
Requires-Dist: loguru>=0.7.0 ; extra == 'all'
|
|
9
|
+
Requires-Dist: rich>=13.0.0 ; extra == 'all'
|
|
10
|
+
Requires-Dist: structlog>=24.0.0 ; extra == 'all'
|
|
11
|
+
Requires-Dist: loguru>=0.7.0 ; extra == 'loguru'
|
|
12
|
+
Requires-Dist: rich>=13.0.0 ; extra == 'rich'
|
|
13
|
+
Requires-Dist: structlog>=24.0.0 ; extra == 'structlog'
|
|
14
|
+
Requires-Python: >=3.13
|
|
15
|
+
Provides-Extra: all
|
|
16
|
+
Provides-Extra: loguru
|
|
17
|
+
Provides-Extra: rich
|
|
18
|
+
Provides-Extra: structlog
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# hexastack-logging
|
|
22
|
+
|
|
23
|
+
> Structured logging, security sanitization, and adapter integrations (Loguru, Rich, Structlog) for Hexastack.
|
|
24
|
+
|
|
25
|
+
[](https://www.python.org/downloads/)
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 1. Overview & Capabilities
|
|
30
|
+
|
|
31
|
+
`hexastack-logging` provides high-performance, structured telemetry across the entire Hexastack lifecycle:
|
|
32
|
+
|
|
33
|
+
- **Multiple Backend Adapters**: Native implementations for `Loguru`, `Rich`, `Structlog`, and standard library structured logging.
|
|
34
|
+
- **Security & PII Sanitization**: Automatic masking of sensitive fields (passwords, tokens, authorization headers, credit cards).
|
|
35
|
+
- **Formatters**: JSON formatting for cloud aggregators (Datadog, CloudWatch) and colored console formatting for local development.
|
|
36
|
+
- **Context & Correlation Integration**: Injects active `correlation_id` from async context into all log outputs.
|
|
37
|
+
- **Log Filtering & Levels**: Fine-grained level filtering and module-based log routing.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 2. Package Anatomy & Key Components
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
hexastack_logging/
|
|
45
|
+
├── domain/ # LogRecord, LogLevel, Logging exceptions
|
|
46
|
+
├── ports/ # LoggerPort, FormatterPort, FilterPort, SanitizerPort
|
|
47
|
+
├── adapters/ # LoguruAdapter, RichAdapter, StructlogAdapter, StructuredLogger
|
|
48
|
+
└── infra/ # LoggingBootstrapper (order=10), Formatters, Sanitizers, Config
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Key Exports
|
|
52
|
+
|
|
53
|
+
| Category | Exports |
|
|
54
|
+
|---|---|
|
|
55
|
+
| **Adapters** | `StructuredLogger`, `LoguruAdapter`, `RichAdapter`, `StructlogAdapter` |
|
|
56
|
+
| **Bootstrap** | `LoggingBootstrapper` (order=10), `HexastackLoggingConfig` |
|
|
57
|
+
| **Formatters** | `JsonFormatter`, `ConsoleFormatter` |
|
|
58
|
+
| **Sanitization** | `SanitizerFilter`, `mask_sensitive_data` |
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 3. Monorepo & Sibling Relationships
|
|
63
|
+
|
|
64
|
+
```mermaid
|
|
65
|
+
graph TD
|
|
66
|
+
subgraph SiblingConsumers ["Consumers of LoggerPort"]
|
|
67
|
+
CQRS["hexastack-cqrs (LoggingMiddleware)"]
|
|
68
|
+
FASTAPI["hexastack-fastapi (HttpLoggingMiddleware)"]
|
|
69
|
+
GRPC["hexastack-grpc (LoggingServerInterceptor)"]
|
|
70
|
+
GRAPHQL["hexastack-graphql (CorrelationExtension)"]
|
|
71
|
+
CORE_CTX["hexastack-core (get_correlation_id)"]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
subgraph LoggingSubsystem ["hexastack-logging"]
|
|
75
|
+
BOOT["LoggingBootstrapper (order=10)"]
|
|
76
|
+
ADAPTERS["Adapters (Loguru / Rich / Structlog / StdLib)"]
|
|
77
|
+
SAN["PII Sanitizer & JSON Formatter"]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
BOOT --> ADAPTERS
|
|
81
|
+
ADAPTERS --> SAN
|
|
82
|
+
ADAPTERS -. reads context .-> CORE_CTX
|
|
83
|
+
|
|
84
|
+
CQRS -. resolves from DI .-> ADAPTERS
|
|
85
|
+
FASTAPI -. resolves from DI .-> ADAPTERS
|
|
86
|
+
GRPC -. resolves from DI .-> ADAPTERS
|
|
87
|
+
GRAPHQL -. resolves from DI .-> ADAPTERS
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Explicit Dependencies (Direct)
|
|
91
|
+
- `hexastack-core`: Abstract `LoggerPort`, DI container, and async context tracking.
|
|
92
|
+
|
|
93
|
+
### Implied / Behavioral Relationships (DI-Mediated)
|
|
94
|
+
- **Dependency Provider**: Binds `LoggerPort` into the DI container at `order=10` (before CQRS `order=20`), ensuring telemetry is available to all subsequent bootstrappers and middlewares.
|
|
95
|
+
- **Correlation ID Synchronization**: Automatically reads and outputs `get_correlation_id()` stored in `hexastack_core.utils.context`.
|
|
96
|
+
|
|
97
|
+
### Optional Integrations (Extras)
|
|
98
|
+
- `[loguru]`: Enables `loguru>=0.7.0`.
|
|
99
|
+
- `[rich]`: Enables `rich>=13.0.0` console formatting.
|
|
100
|
+
- `[structlog]`: Enables `structlog>=24.0.0`.
|
|
101
|
+
- `[all]`: Installs all optional logging backends.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 4. Installation
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Standalone standard library logging
|
|
109
|
+
pip install hexastack-logging
|
|
110
|
+
|
|
111
|
+
# With Loguru and Rich support
|
|
112
|
+
pip install "hexastack-logging[loguru,rich]"
|
|
113
|
+
|
|
114
|
+
# All logging backends
|
|
115
|
+
pip install "hexastack-logging[all]"
|
|
116
|
+
|
|
117
|
+
# Via umbrella package
|
|
118
|
+
pip install hexastack
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 5. Configuration Reference
|
|
124
|
+
|
|
125
|
+
```toml
|
|
126
|
+
[hexastack.logging]
|
|
127
|
+
level = "INFO" # "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"
|
|
128
|
+
format = "json" # "json", "console"
|
|
129
|
+
backend = "structlog" # "standard", "loguru", "rich", "structlog"
|
|
130
|
+
sanitize_keys = ["password", "token", "secret", "authorization", "api_key"]
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## 6. Quickstart Example
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from hexastack_core.infra.bootstrap import bootstrap
|
|
139
|
+
from hexastack_core.ports.logging import LoggerPort
|
|
140
|
+
|
|
141
|
+
runtime = bootstrap(
|
|
142
|
+
config_overrides={
|
|
143
|
+
"logging": {
|
|
144
|
+
"level": "DEBUG",
|
|
145
|
+
"format": "console",
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
logger = runtime.container.get(LoggerPort)
|
|
151
|
+
logger.info("Application initialized successfully", extra={"user_count": 42})
|
|
152
|
+
```
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# hexastack-logging
|
|
2
|
+
|
|
3
|
+
> Structured logging, security sanitization, and adapter integrations (Loguru, Rich, Structlog) for Hexastack.
|
|
4
|
+
|
|
5
|
+
[](https://www.python.org/downloads/)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Overview & Capabilities
|
|
10
|
+
|
|
11
|
+
`hexastack-logging` provides high-performance, structured telemetry across the entire Hexastack lifecycle:
|
|
12
|
+
|
|
13
|
+
- **Multiple Backend Adapters**: Native implementations for `Loguru`, `Rich`, `Structlog`, and standard library structured logging.
|
|
14
|
+
- **Security & PII Sanitization**: Automatic masking of sensitive fields (passwords, tokens, authorization headers, credit cards).
|
|
15
|
+
- **Formatters**: JSON formatting for cloud aggregators (Datadog, CloudWatch) and colored console formatting for local development.
|
|
16
|
+
- **Context & Correlation Integration**: Injects active `correlation_id` from async context into all log outputs.
|
|
17
|
+
- **Log Filtering & Levels**: Fine-grained level filtering and module-based log routing.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 2. Package Anatomy & Key Components
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
hexastack_logging/
|
|
25
|
+
├── domain/ # LogRecord, LogLevel, Logging exceptions
|
|
26
|
+
├── ports/ # LoggerPort, FormatterPort, FilterPort, SanitizerPort
|
|
27
|
+
├── adapters/ # LoguruAdapter, RichAdapter, StructlogAdapter, StructuredLogger
|
|
28
|
+
└── infra/ # LoggingBootstrapper (order=10), Formatters, Sanitizers, Config
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Key Exports
|
|
32
|
+
|
|
33
|
+
| Category | Exports |
|
|
34
|
+
|---|---|
|
|
35
|
+
| **Adapters** | `StructuredLogger`, `LoguruAdapter`, `RichAdapter`, `StructlogAdapter` |
|
|
36
|
+
| **Bootstrap** | `LoggingBootstrapper` (order=10), `HexastackLoggingConfig` |
|
|
37
|
+
| **Formatters** | `JsonFormatter`, `ConsoleFormatter` |
|
|
38
|
+
| **Sanitization** | `SanitizerFilter`, `mask_sensitive_data` |
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 3. Monorepo & Sibling Relationships
|
|
43
|
+
|
|
44
|
+
```mermaid
|
|
45
|
+
graph TD
|
|
46
|
+
subgraph SiblingConsumers ["Consumers of LoggerPort"]
|
|
47
|
+
CQRS["hexastack-cqrs (LoggingMiddleware)"]
|
|
48
|
+
FASTAPI["hexastack-fastapi (HttpLoggingMiddleware)"]
|
|
49
|
+
GRPC["hexastack-grpc (LoggingServerInterceptor)"]
|
|
50
|
+
GRAPHQL["hexastack-graphql (CorrelationExtension)"]
|
|
51
|
+
CORE_CTX["hexastack-core (get_correlation_id)"]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
subgraph LoggingSubsystem ["hexastack-logging"]
|
|
55
|
+
BOOT["LoggingBootstrapper (order=10)"]
|
|
56
|
+
ADAPTERS["Adapters (Loguru / Rich / Structlog / StdLib)"]
|
|
57
|
+
SAN["PII Sanitizer & JSON Formatter"]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
BOOT --> ADAPTERS
|
|
61
|
+
ADAPTERS --> SAN
|
|
62
|
+
ADAPTERS -. reads context .-> CORE_CTX
|
|
63
|
+
|
|
64
|
+
CQRS -. resolves from DI .-> ADAPTERS
|
|
65
|
+
FASTAPI -. resolves from DI .-> ADAPTERS
|
|
66
|
+
GRPC -. resolves from DI .-> ADAPTERS
|
|
67
|
+
GRAPHQL -. resolves from DI .-> ADAPTERS
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Explicit Dependencies (Direct)
|
|
71
|
+
- `hexastack-core`: Abstract `LoggerPort`, DI container, and async context tracking.
|
|
72
|
+
|
|
73
|
+
### Implied / Behavioral Relationships (DI-Mediated)
|
|
74
|
+
- **Dependency Provider**: Binds `LoggerPort` into the DI container at `order=10` (before CQRS `order=20`), ensuring telemetry is available to all subsequent bootstrappers and middlewares.
|
|
75
|
+
- **Correlation ID Synchronization**: Automatically reads and outputs `get_correlation_id()` stored in `hexastack_core.utils.context`.
|
|
76
|
+
|
|
77
|
+
### Optional Integrations (Extras)
|
|
78
|
+
- `[loguru]`: Enables `loguru>=0.7.0`.
|
|
79
|
+
- `[rich]`: Enables `rich>=13.0.0` console formatting.
|
|
80
|
+
- `[structlog]`: Enables `structlog>=24.0.0`.
|
|
81
|
+
- `[all]`: Installs all optional logging backends.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## 4. Installation
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
# Standalone standard library logging
|
|
89
|
+
pip install hexastack-logging
|
|
90
|
+
|
|
91
|
+
# With Loguru and Rich support
|
|
92
|
+
pip install "hexastack-logging[loguru,rich]"
|
|
93
|
+
|
|
94
|
+
# All logging backends
|
|
95
|
+
pip install "hexastack-logging[all]"
|
|
96
|
+
|
|
97
|
+
# Via umbrella package
|
|
98
|
+
pip install hexastack
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 5. Configuration Reference
|
|
104
|
+
|
|
105
|
+
```toml
|
|
106
|
+
[hexastack.logging]
|
|
107
|
+
level = "INFO" # "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"
|
|
108
|
+
format = "json" # "json", "console"
|
|
109
|
+
backend = "structlog" # "standard", "loguru", "rich", "structlog"
|
|
110
|
+
sanitize_keys = ["password", "token", "secret", "authorization", "api_key"]
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 6. Quickstart Example
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
from hexastack_core.infra.bootstrap import bootstrap
|
|
119
|
+
from hexastack_core.ports.logging import LoggerPort
|
|
120
|
+
|
|
121
|
+
runtime = bootstrap(
|
|
122
|
+
config_overrides={
|
|
123
|
+
"logging": {
|
|
124
|
+
"level": "DEBUG",
|
|
125
|
+
"format": "console",
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
logger = runtime.container.get(LoggerPort)
|
|
131
|
+
logger.info("Application initialized successfully", extra={"user_count": 42})
|
|
132
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "hexastack-logging"
|
|
3
|
+
version = "0.0.0"
|
|
4
|
+
description = "Add your description here"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.13"
|
|
7
|
+
dependencies = ["hexastack-core"]
|
|
8
|
+
|
|
9
|
+
[[project.authors]]
|
|
10
|
+
name = "Richard West"
|
|
11
|
+
email = "dopplereffect.us@gmail.com"
|
|
12
|
+
|
|
13
|
+
[project.optional-dependencies]
|
|
14
|
+
loguru = ["loguru>=0.7.0"]
|
|
15
|
+
rich = ["rich>=13.0.0"]
|
|
16
|
+
structlog = ["structlog>=24.0.0"]
|
|
17
|
+
all = [
|
|
18
|
+
"loguru>=0.7.0",
|
|
19
|
+
"rich>=13.0.0",
|
|
20
|
+
"structlog>=24.0.0",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.entry-points."hexastack.bootstrappers"]
|
|
24
|
+
logging = "hexastack_logging.infra.bootstrap:LoggingBootstrapper"
|
|
25
|
+
|
|
26
|
+
[build-system]
|
|
27
|
+
requires = ["uv_build>=0.12.3,<0.13.0"]
|
|
28
|
+
build-backend = "uv_build"
|
|
29
|
+
|
|
30
|
+
[tool.uv.sources.hexastack-core]
|
|
31
|
+
workspace = true
|
|
32
|
+
|
|
33
|
+
[tool.importlinter]
|
|
34
|
+
root_packages = ["hexastack_logging"]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "hexastack-logging"
|
|
3
|
+
version = "0.0.0"
|
|
4
|
+
description = "Add your description here"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "Richard West", email = "dopplereffect.us@gmail.com" }
|
|
8
|
+
]
|
|
9
|
+
requires-python = ">=3.13"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"hexastack-core",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[project.optional-dependencies]
|
|
15
|
+
loguru = ["loguru>=0.7.0"]
|
|
16
|
+
rich = ["rich>=13.0.0"]
|
|
17
|
+
structlog = ["structlog>=24.0.0"]
|
|
18
|
+
all = [
|
|
19
|
+
"loguru>=0.7.0",
|
|
20
|
+
"rich>=13.0.0",
|
|
21
|
+
"structlog>=24.0.0",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.entry-points."hexastack.bootstrappers"]
|
|
25
|
+
logging = "hexastack_logging.infra.bootstrap:LoggingBootstrapper"
|
|
26
|
+
|
|
27
|
+
[build-system]
|
|
28
|
+
requires = ["uv_build>=0.12.3,<0.13.0"]
|
|
29
|
+
build-backend = "uv_build"
|
|
30
|
+
|
|
31
|
+
[tool.uv.sources]
|
|
32
|
+
hexastack-core = { workspace = true }
|
|
33
|
+
|
|
34
|
+
[tool.importlinter]
|
|
35
|
+
root_packages = ["hexastack_logging"]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from hexastack_logging.adapters.logger.loguru import LoguruAdapter
|
|
2
|
+
from hexastack_logging.adapters.logger.rich import RichLogger
|
|
3
|
+
from hexastack_logging.adapters.logger.structlog import StructlogAdapter
|
|
4
|
+
from hexastack_logging.adapters.logger.structured import StructuredLogger
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"LoguruAdapter",
|
|
8
|
+
"RichLogger",
|
|
9
|
+
"StructlogAdapter",
|
|
10
|
+
"StructuredLogger",
|
|
11
|
+
]
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from hexastack_core.domain.exceptions import MissingDependencyError
|
|
5
|
+
from hexastack_core.ports.logging import Extras, LoggingPort
|
|
6
|
+
from hexastack_core.utils.context import get_correlation_id, get_user_context
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class LoguruAdapter(LoggingPort):
|
|
10
|
+
"""Optional Loguru adapter implementing LoggingPort.
|
|
11
|
+
|
|
12
|
+
Notes/Architectural Intent:
|
|
13
|
+
Delegates logging to loguru while binding Hexastack correlation
|
|
14
|
+
and user context.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, logger: Any = None) -> None:
|
|
18
|
+
"""Initialize LoguruAdapter.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
logger: Optional loguru logger instance.
|
|
22
|
+
|
|
23
|
+
Raises:
|
|
24
|
+
MissingDependencyError: If loguru package is not installed.
|
|
25
|
+
"""
|
|
26
|
+
if logger is None:
|
|
27
|
+
try:
|
|
28
|
+
loguru_mod = importlib.import_module("loguru")
|
|
29
|
+
self._logger: Any = loguru_mod.logger
|
|
30
|
+
except ImportError as err:
|
|
31
|
+
raise MissingDependencyError(
|
|
32
|
+
"loguru is required for LoguruAdapter. Install via 'pip install hexastack-logging[loguru]'."
|
|
33
|
+
) from err
|
|
34
|
+
else:
|
|
35
|
+
self._logger = logger
|
|
36
|
+
|
|
37
|
+
def _get_bound_logger(self, extra: Extras | None = None) -> Any:
|
|
38
|
+
bind_dict: dict[str, Any] = dict(extra) if extra else {}
|
|
39
|
+
cid = get_correlation_id()
|
|
40
|
+
if cid:
|
|
41
|
+
bind_dict["correlation_id"] = cid
|
|
42
|
+
user = get_user_context()
|
|
43
|
+
if user:
|
|
44
|
+
bind_dict["user_id"] = user.user_id
|
|
45
|
+
if user.tenant_id:
|
|
46
|
+
bind_dict["tenant_id"] = user.tenant_id
|
|
47
|
+
return self._logger.bind(**bind_dict)
|
|
48
|
+
|
|
49
|
+
def critical(
|
|
50
|
+
self, message: str, extra: Extras | None = None, exc: Exception | None = None
|
|
51
|
+
) -> None:
|
|
52
|
+
"""Log a critical message through loguru.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
message: Text message to log.
|
|
56
|
+
extra: Optional key-value dictionary of contextual metadata.
|
|
57
|
+
exc: Optional exception instance to attach traceback.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
None.
|
|
61
|
+
|
|
62
|
+
Raises:
|
|
63
|
+
None.
|
|
64
|
+
"""
|
|
65
|
+
log = self._get_bound_logger(extra)
|
|
66
|
+
log.opt(exception=exc).critical(message)
|
|
67
|
+
|
|
68
|
+
def debug(self, message: str, extra: Extras | None = None) -> None:
|
|
69
|
+
"""Log a debug message through loguru.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
message: Text message to log.
|
|
73
|
+
extra: Optional key-value dictionary of contextual metadata.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
None.
|
|
77
|
+
|
|
78
|
+
Raises:
|
|
79
|
+
None.
|
|
80
|
+
"""
|
|
81
|
+
log = self._get_bound_logger(extra)
|
|
82
|
+
log.debug(message)
|
|
83
|
+
|
|
84
|
+
def error(
|
|
85
|
+
self, message: str, extra: Extras | None = None, exc: Exception | None = None
|
|
86
|
+
) -> None:
|
|
87
|
+
"""Log an error message through loguru.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
message: Text message to log.
|
|
91
|
+
extra: Optional key-value dictionary of contextual metadata.
|
|
92
|
+
exc: Optional exception instance to attach traceback.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
None.
|
|
96
|
+
|
|
97
|
+
Raises:
|
|
98
|
+
None.
|
|
99
|
+
"""
|
|
100
|
+
log = self._get_bound_logger(extra)
|
|
101
|
+
log.opt(exception=exc).error(message)
|
|
102
|
+
|
|
103
|
+
def info(self, message: str, extra: Extras | None = None) -> None:
|
|
104
|
+
"""Log an informational message through loguru.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
message: Text message to log.
|
|
108
|
+
extra: Optional key-value dictionary of contextual metadata.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
None.
|
|
112
|
+
|
|
113
|
+
Raises:
|
|
114
|
+
None.
|
|
115
|
+
"""
|
|
116
|
+
log = self._get_bound_logger(extra)
|
|
117
|
+
log.info(message)
|
|
118
|
+
|
|
119
|
+
def warning(self, message: str, extra: Extras | None = None) -> None:
|
|
120
|
+
"""Log a warning message through loguru.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
message: Text message to log.
|
|
124
|
+
extra: Optional key-value dictionary of contextual metadata.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
None.
|
|
128
|
+
|
|
129
|
+
Raises:
|
|
130
|
+
None.
|
|
131
|
+
"""
|
|
132
|
+
log = self._get_bound_logger(extra)
|
|
133
|
+
log.warning(message)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from hexastack_core.domain.exceptions import MissingDependencyError
|
|
5
|
+
from hexastack_core.ports.logging import Extras, LoggingPort
|
|
6
|
+
from hexastack_core.utils.context import get_correlation_id
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RichLogger(LoggingPort):
|
|
10
|
+
"""Optional Rich terminal logger implementing LoggingPort.
|
|
11
|
+
|
|
12
|
+
Notes/Architectural Intent:
|
|
13
|
+
Renders rich stylized terminal logs with correlation ID tags when rich is installed.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, console: Any = None) -> None:
|
|
17
|
+
"""Initialize RichLogger.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
console: Optional rich.console.Console instance.
|
|
21
|
+
|
|
22
|
+
Raises:
|
|
23
|
+
MissingDependencyError: If rich package is not installed.
|
|
24
|
+
"""
|
|
25
|
+
if console is None:
|
|
26
|
+
try:
|
|
27
|
+
rich_console_mod = importlib.import_module("rich.console")
|
|
28
|
+
self._console: Any = rich_console_mod.Console()
|
|
29
|
+
except ImportError as err:
|
|
30
|
+
raise MissingDependencyError(
|
|
31
|
+
"rich is required for RichLogger. Install via 'pip install hexastack-logging[rich]'."
|
|
32
|
+
) from err
|
|
33
|
+
else:
|
|
34
|
+
self._console = console
|
|
35
|
+
|
|
36
|
+
def _render(
|
|
37
|
+
self,
|
|
38
|
+
level: str,
|
|
39
|
+
color: str,
|
|
40
|
+
message: str,
|
|
41
|
+
extra: Extras | None = None,
|
|
42
|
+
exc: Exception | None = None,
|
|
43
|
+
) -> None:
|
|
44
|
+
cid = get_correlation_id()
|
|
45
|
+
cid_tag = f"[dim][corr:{cid[:8]}][/dim] " if cid else ""
|
|
46
|
+
extra_str = f" [dim]{extra}[/dim]" if extra else ""
|
|
47
|
+
exc_str = f"\n[red]{exc}[/red]" if exc else ""
|
|
48
|
+
self._console.print(
|
|
49
|
+
f"[{color}][{level:<8}][/{color}] {cid_tag}{message}{extra_str}{exc_str}"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
def critical(
|
|
53
|
+
self, message: str, extra: Extras | None = None, exc: Exception | None = None
|
|
54
|
+
) -> None:
|
|
55
|
+
"""Log a critical message with Rich formatting.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
message: Text message to log.
|
|
59
|
+
extra: Optional key-value dictionary of contextual metadata.
|
|
60
|
+
exc: Optional exception instance to attach traceback.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
None.
|
|
64
|
+
|
|
65
|
+
Raises:
|
|
66
|
+
None.
|
|
67
|
+
"""
|
|
68
|
+
self._render("CRITICAL", "bold red", message, extra=extra, exc=exc)
|
|
69
|
+
|
|
70
|
+
def debug(self, message: str, extra: Extras | None = None) -> None:
|
|
71
|
+
"""Log a debug message with Rich formatting.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
message: Text message to log.
|
|
75
|
+
extra: Optional key-value dictionary of contextual metadata.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
None.
|
|
79
|
+
|
|
80
|
+
Raises:
|
|
81
|
+
None.
|
|
82
|
+
"""
|
|
83
|
+
self._render("DEBUG", "cyan", message, extra=extra)
|
|
84
|
+
|
|
85
|
+
def error(
|
|
86
|
+
self, message: str, extra: Extras | None = None, exc: Exception | None = None
|
|
87
|
+
) -> None:
|
|
88
|
+
"""Log an error message with Rich formatting.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
message: Text message to log.
|
|
92
|
+
extra: Optional key-value dictionary of contextual metadata.
|
|
93
|
+
exc: Optional exception instance to attach traceback.
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
None.
|
|
97
|
+
|
|
98
|
+
Raises:
|
|
99
|
+
None.
|
|
100
|
+
"""
|
|
101
|
+
self._render("ERROR", "red", message, extra=extra, exc=exc)
|
|
102
|
+
|
|
103
|
+
def info(self, message: str, extra: Extras | None = None) -> None:
|
|
104
|
+
"""Log an informational message with Rich formatting.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
message: Text message to log.
|
|
108
|
+
extra: Optional key-value dictionary of contextual metadata.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
None.
|
|
112
|
+
|
|
113
|
+
Raises:
|
|
114
|
+
None.
|
|
115
|
+
"""
|
|
116
|
+
self._render("INFO", "green", message, extra=extra)
|
|
117
|
+
|
|
118
|
+
def warning(self, message: str, extra: Extras | None = None) -> None:
|
|
119
|
+
"""Log a warning message with Rich formatting.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
message: Text message to log.
|
|
123
|
+
extra: Optional key-value dictionary of contextual metadata.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
None.
|
|
127
|
+
|
|
128
|
+
Raises:
|
|
129
|
+
None.
|
|
130
|
+
"""
|
|
131
|
+
self._render("WARNING", "yellow", message, extra=extra)
|