miu-logger 0.1.5__tar.gz → 0.2.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.
- miu_logger-0.2.0/PKG-INFO +287 -0
- miu_logger-0.2.0/README.md +270 -0
- {miu_logger-0.1.5 → miu_logger-0.2.0}/pyproject.toml +1 -1
- miu_logger-0.2.0/src/miu_logger/stubgen.py +62 -0
- miu_logger-0.2.0/src/miu_logger.egg-info/PKG-INFO +287 -0
- miu_logger-0.1.5/PKG-INFO +0 -151
- miu_logger-0.1.5/README.md +0 -134
- miu_logger-0.1.5/src/miu_logger/stubgen.py +0 -40
- miu_logger-0.1.5/src/miu_logger.egg-info/PKG-INFO +0 -151
- {miu_logger-0.1.5 → miu_logger-0.2.0}/LICENSE +0 -0
- {miu_logger-0.1.5 → miu_logger-0.2.0}/setup.cfg +0 -0
- {miu_logger-0.1.5 → miu_logger-0.2.0}/src/miu_logger/__init__.py +0 -0
- {miu_logger-0.1.5 → miu_logger-0.2.0}/src/miu_logger/conditional.py +0 -0
- {miu_logger-0.1.5 → miu_logger-0.2.0}/src/miu_logger/config.py +0 -0
- {miu_logger-0.1.5 → miu_logger-0.2.0}/src/miu_logger/filters.py +0 -0
- {miu_logger-0.1.5 → miu_logger-0.2.0}/src/miu_logger/formatters.py +0 -0
- {miu_logger-0.1.5 → miu_logger-0.2.0}/src/miu_logger/listener.py +0 -0
- {miu_logger-0.1.5 → miu_logger-0.2.0}/src/miu_logger/logger_factory.py +0 -0
- {miu_logger-0.1.5 → miu_logger-0.2.0}/src/miu_logger/repository.py +0 -0
- {miu_logger-0.1.5 → miu_logger-0.2.0}/src/miu_logger.egg-info/SOURCES.txt +0 -0
- {miu_logger-0.1.5 → miu_logger-0.2.0}/src/miu_logger.egg-info/dependency_links.txt +0 -0
- {miu_logger-0.1.5 → miu_logger-0.2.0}/src/miu_logger.egg-info/requires.txt +0 -0
- {miu_logger-0.1.5 → miu_logger-0.2.0}/src/miu_logger.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: miu-logger
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Multiprocessing-safe domain-based logging framework with QueueListener architecture
|
|
5
|
+
Author-email: Bruno Miura <brumiura@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: logging,multiprocessing,queue,structured-logging,python-logging
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Topic :: System :: Logging
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: colorama
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
|
|
18
|
+
# miu-logger
|
|
19
|
+
|
|
20
|
+
**Multiprocessing-safe, domain-driven structured logging for Python services**
|
|
21
|
+
|
|
22
|
+
`miu-logger` is a logging framework designed for **real systems**:
|
|
23
|
+
|
|
24
|
+
* Multiprocessing- and multithreading-safe
|
|
25
|
+
* Domain-separated loggers (`app`, `db`, `task`, etc.)
|
|
26
|
+
* Per-level log files (`debug.log`, `info.log`, `error.log`, …)
|
|
27
|
+
* Central queue listener to avoid file collisions
|
|
28
|
+
* Clean IDE autocomplete for both domains and log levels
|
|
29
|
+
* Minimal setup in application code
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Why use `miu-logger`
|
|
34
|
+
|
|
35
|
+
Python's standard logging is powerful but breaks down when:
|
|
36
|
+
|
|
37
|
+
* Multiple processes or threads write to the same log files
|
|
38
|
+
* You want **domain-specific log files**
|
|
39
|
+
* You want **centralized log routing**
|
|
40
|
+
* You want IDE autocomplete for domains and levels
|
|
41
|
+
|
|
42
|
+
`miu-logger` solves this by:
|
|
43
|
+
|
|
44
|
+
> Logging through a **central queue listener**, separating **domains** and **levels**, while exposing a **typed repository** for IDE-friendly access.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
uv add miu-logger
|
|
52
|
+
# or
|
|
53
|
+
pip install miu-logger
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Configuring Logging Domains
|
|
59
|
+
|
|
60
|
+
Domains represent **areas of your system** (not log levels). Examples: `app`, `db`, `redis`, `task`.
|
|
61
|
+
|
|
62
|
+
Define them in `LogConfig`:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
import logging
|
|
66
|
+
from miu_logger.config import LogConfig
|
|
67
|
+
from miu_logger.repository import LoggingRepository
|
|
68
|
+
|
|
69
|
+
config = LogConfig(
|
|
70
|
+
log_dir="logs", # folder to store logs
|
|
71
|
+
domains=["app", "db", "redis"], # your custom domains
|
|
72
|
+
level=logging.DEBUG, # base log level for all domains
|
|
73
|
+
debug_enabled=True, # toggle debug messages
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
repo = LoggingRepository(config)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Accessing Domain Loggers
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
repo.app.info("Application started")
|
|
83
|
+
repo.db.error("Database connection failed")
|
|
84
|
+
repo.redis.debug("Cache initialized")
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
* Accessing a domain not defined in `config.domains` raises a `ValueError`:
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
repo.get("api") # ❌ ValueError if "api" not in config.domains
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
* Domains can also be dynamically retrieved:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
task_logger = repo.get("task") # Must be defined in config.domains
|
|
97
|
+
task_logger.info("Task started")
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## IDE Autocomplete with Stub Generation
|
|
103
|
+
|
|
104
|
+
Domains are dynamic, so editors cannot know them automatically.
|
|
105
|
+
Use the **stub generator** to enable autocomplete:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from miu_logger.stubgen import generate_repository_stub
|
|
109
|
+
|
|
110
|
+
generate_repository_stub(
|
|
111
|
+
domains=["app", "db", "redis", "task"], # all your domains
|
|
112
|
+
out_dir="typings", # directory for generated stubs
|
|
113
|
+
)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
This creates:
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
typings/miu_logger/repository.pyi
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Tell your editor where the typings are:
|
|
123
|
+
|
|
124
|
+
* **VSCode / Pyright**:
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"python.analysis.extraPaths": ["typings"]
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
* **pyproject.toml (Pyright)**:
|
|
133
|
+
|
|
134
|
+
```toml
|
|
135
|
+
[tool.pyright]
|
|
136
|
+
extraPaths = ["typings"]
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
After this, your IDE knows both:
|
|
140
|
+
|
|
141
|
+
* Domains: `repo.app`, `repo.db`, …
|
|
142
|
+
* Log methods: `.debug()`, `.info()`, `.warning()`, `.error()`, `.exception()`
|
|
143
|
+
|
|
144
|
+
Regenerate stubs whenever you add new domains.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Multiprocessing Usage
|
|
149
|
+
|
|
150
|
+
The repository uses a **central QueueListener** for safe multiprocessing logging.
|
|
151
|
+
|
|
152
|
+
**Main process:**
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
repo = LoggingRepository(config)
|
|
156
|
+
queue = repo.get_queue()
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Worker processes:**
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
worker_repo = LoggingRepository(config, use_listener=False, queue=queue)
|
|
163
|
+
worker_repo.task.info("Worker started")
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
All processes log safely to the same listener and files.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Debug Control
|
|
171
|
+
|
|
172
|
+
Only `.debug()` messages are conditional via `debug_enabled`:
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
config.debug_enabled = False
|
|
176
|
+
repo.app.debug("Won't appear")
|
|
177
|
+
repo.app.info("Will appear")
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Useful for toggling debug messages in production.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Output Structure
|
|
185
|
+
|
|
186
|
+
Logs are written in:
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
logs/
|
|
190
|
+
├─ app.log # domain logs
|
|
191
|
+
├─ db.log
|
|
192
|
+
├─ redis.log
|
|
193
|
+
├─ debug.log # per-level logs
|
|
194
|
+
├─ info.log
|
|
195
|
+
├─ warning.log
|
|
196
|
+
└─ error.log
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Console output is **colorized** by level:
|
|
200
|
+
|
|
201
|
+
* DEBUG → blue
|
|
202
|
+
* INFO → green
|
|
203
|
+
* WARNING → yellow
|
|
204
|
+
* ERROR → red
|
|
205
|
+
|
|
206
|
+
Files remain clean.
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## Graceful Shutdown
|
|
211
|
+
|
|
212
|
+
The repository automatically shuts down on process exit and supports manual shutdown:
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
repo.shutdown()
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Typical Usage Example
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
repo.app.info("Service starting")
|
|
224
|
+
|
|
225
|
+
try:
|
|
226
|
+
connect_db()
|
|
227
|
+
except Exception:
|
|
228
|
+
repo.db.exception("DB connection failed")
|
|
229
|
+
|
|
230
|
+
repo.redis.debug("Cache size: %d", cache_size)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**Multiprocessing workers:**
|
|
234
|
+
|
|
235
|
+
```python
|
|
236
|
+
def worker(queue):
|
|
237
|
+
repo = LoggingRepository(config, use_listener=False, queue=queue)
|
|
238
|
+
repo.task.info("Worker task started")
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Project Structure
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
miu_logger/
|
|
247
|
+
├─ repository.py # main LoggingRepository
|
|
248
|
+
├─ listener.py # QueueListener and handler setup
|
|
249
|
+
├─ logger_factory.py # logger creation functions
|
|
250
|
+
├─ conditional.py # ConditionalLogger
|
|
251
|
+
├─ filters.py # Logger filters
|
|
252
|
+
├─ formatters.py # ColoredFormatter
|
|
253
|
+
├─ config.py # LogConfig definition
|
|
254
|
+
└─ stubgen.py # stub generator for IDE autocomplete
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## When to Use
|
|
260
|
+
|
|
261
|
+
* Services with many subsystems
|
|
262
|
+
* Multiprocessing ingestion pipelines
|
|
263
|
+
* Long-running daemons
|
|
264
|
+
* Kubernetes / systemd services
|
|
265
|
+
* Need for clear operational logs
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## When Not to Use
|
|
270
|
+
|
|
271
|
+
* Single-file scripts
|
|
272
|
+
* No multiprocessing
|
|
273
|
+
* No domain separation required
|
|
274
|
+
|
|
275
|
+
Plain `logging` is sufficient in those cases.
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## License
|
|
280
|
+
|
|
281
|
+
MIT
|
|
282
|
+
|
|
283
|
+
---
|
|
284
|
+
|
|
285
|
+
## Author
|
|
286
|
+
|
|
287
|
+
**Bruno Miura**
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# miu-logger
|
|
2
|
+
|
|
3
|
+
**Multiprocessing-safe, domain-driven structured logging for Python services**
|
|
4
|
+
|
|
5
|
+
`miu-logger` is a logging framework designed for **real systems**:
|
|
6
|
+
|
|
7
|
+
* Multiprocessing- and multithreading-safe
|
|
8
|
+
* Domain-separated loggers (`app`, `db`, `task`, etc.)
|
|
9
|
+
* Per-level log files (`debug.log`, `info.log`, `error.log`, …)
|
|
10
|
+
* Central queue listener to avoid file collisions
|
|
11
|
+
* Clean IDE autocomplete for both domains and log levels
|
|
12
|
+
* Minimal setup in application code
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Why use `miu-logger`
|
|
17
|
+
|
|
18
|
+
Python's standard logging is powerful but breaks down when:
|
|
19
|
+
|
|
20
|
+
* Multiple processes or threads write to the same log files
|
|
21
|
+
* You want **domain-specific log files**
|
|
22
|
+
* You want **centralized log routing**
|
|
23
|
+
* You want IDE autocomplete for domains and levels
|
|
24
|
+
|
|
25
|
+
`miu-logger` solves this by:
|
|
26
|
+
|
|
27
|
+
> Logging through a **central queue listener**, separating **domains** and **levels**, while exposing a **typed repository** for IDE-friendly access.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
uv add miu-logger
|
|
35
|
+
# or
|
|
36
|
+
pip install miu-logger
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Configuring Logging Domains
|
|
42
|
+
|
|
43
|
+
Domains represent **areas of your system** (not log levels). Examples: `app`, `db`, `redis`, `task`.
|
|
44
|
+
|
|
45
|
+
Define them in `LogConfig`:
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
import logging
|
|
49
|
+
from miu_logger.config import LogConfig
|
|
50
|
+
from miu_logger.repository import LoggingRepository
|
|
51
|
+
|
|
52
|
+
config = LogConfig(
|
|
53
|
+
log_dir="logs", # folder to store logs
|
|
54
|
+
domains=["app", "db", "redis"], # your custom domains
|
|
55
|
+
level=logging.DEBUG, # base log level for all domains
|
|
56
|
+
debug_enabled=True, # toggle debug messages
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
repo = LoggingRepository(config)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Accessing Domain Loggers
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
repo.app.info("Application started")
|
|
66
|
+
repo.db.error("Database connection failed")
|
|
67
|
+
repo.redis.debug("Cache initialized")
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
* Accessing a domain not defined in `config.domains` raises a `ValueError`:
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
repo.get("api") # ❌ ValueError if "api" not in config.domains
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
* Domains can also be dynamically retrieved:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
task_logger = repo.get("task") # Must be defined in config.domains
|
|
80
|
+
task_logger.info("Task started")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## IDE Autocomplete with Stub Generation
|
|
86
|
+
|
|
87
|
+
Domains are dynamic, so editors cannot know them automatically.
|
|
88
|
+
Use the **stub generator** to enable autocomplete:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from miu_logger.stubgen import generate_repository_stub
|
|
92
|
+
|
|
93
|
+
generate_repository_stub(
|
|
94
|
+
domains=["app", "db", "redis", "task"], # all your domains
|
|
95
|
+
out_dir="typings", # directory for generated stubs
|
|
96
|
+
)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
This creates:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
typings/miu_logger/repository.pyi
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Tell your editor where the typings are:
|
|
106
|
+
|
|
107
|
+
* **VSCode / Pyright**:
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"python.analysis.extraPaths": ["typings"]
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
* **pyproject.toml (Pyright)**:
|
|
116
|
+
|
|
117
|
+
```toml
|
|
118
|
+
[tool.pyright]
|
|
119
|
+
extraPaths = ["typings"]
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
After this, your IDE knows both:
|
|
123
|
+
|
|
124
|
+
* Domains: `repo.app`, `repo.db`, …
|
|
125
|
+
* Log methods: `.debug()`, `.info()`, `.warning()`, `.error()`, `.exception()`
|
|
126
|
+
|
|
127
|
+
Regenerate stubs whenever you add new domains.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Multiprocessing Usage
|
|
132
|
+
|
|
133
|
+
The repository uses a **central QueueListener** for safe multiprocessing logging.
|
|
134
|
+
|
|
135
|
+
**Main process:**
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
repo = LoggingRepository(config)
|
|
139
|
+
queue = repo.get_queue()
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Worker processes:**
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
worker_repo = LoggingRepository(config, use_listener=False, queue=queue)
|
|
146
|
+
worker_repo.task.info("Worker started")
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
All processes log safely to the same listener and files.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Debug Control
|
|
154
|
+
|
|
155
|
+
Only `.debug()` messages are conditional via `debug_enabled`:
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
config.debug_enabled = False
|
|
159
|
+
repo.app.debug("Won't appear")
|
|
160
|
+
repo.app.info("Will appear")
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Useful for toggling debug messages in production.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Output Structure
|
|
168
|
+
|
|
169
|
+
Logs are written in:
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
logs/
|
|
173
|
+
├─ app.log # domain logs
|
|
174
|
+
├─ db.log
|
|
175
|
+
├─ redis.log
|
|
176
|
+
├─ debug.log # per-level logs
|
|
177
|
+
├─ info.log
|
|
178
|
+
├─ warning.log
|
|
179
|
+
└─ error.log
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Console output is **colorized** by level:
|
|
183
|
+
|
|
184
|
+
* DEBUG → blue
|
|
185
|
+
* INFO → green
|
|
186
|
+
* WARNING → yellow
|
|
187
|
+
* ERROR → red
|
|
188
|
+
|
|
189
|
+
Files remain clean.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Graceful Shutdown
|
|
194
|
+
|
|
195
|
+
The repository automatically shuts down on process exit and supports manual shutdown:
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
repo.shutdown()
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Typical Usage Example
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
repo.app.info("Service starting")
|
|
207
|
+
|
|
208
|
+
try:
|
|
209
|
+
connect_db()
|
|
210
|
+
except Exception:
|
|
211
|
+
repo.db.exception("DB connection failed")
|
|
212
|
+
|
|
213
|
+
repo.redis.debug("Cache size: %d", cache_size)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**Multiprocessing workers:**
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
def worker(queue):
|
|
220
|
+
repo = LoggingRepository(config, use_listener=False, queue=queue)
|
|
221
|
+
repo.task.info("Worker task started")
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Project Structure
|
|
227
|
+
|
|
228
|
+
```
|
|
229
|
+
miu_logger/
|
|
230
|
+
├─ repository.py # main LoggingRepository
|
|
231
|
+
├─ listener.py # QueueListener and handler setup
|
|
232
|
+
├─ logger_factory.py # logger creation functions
|
|
233
|
+
├─ conditional.py # ConditionalLogger
|
|
234
|
+
├─ filters.py # Logger filters
|
|
235
|
+
├─ formatters.py # ColoredFormatter
|
|
236
|
+
├─ config.py # LogConfig definition
|
|
237
|
+
└─ stubgen.py # stub generator for IDE autocomplete
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## When to Use
|
|
243
|
+
|
|
244
|
+
* Services with many subsystems
|
|
245
|
+
* Multiprocessing ingestion pipelines
|
|
246
|
+
* Long-running daemons
|
|
247
|
+
* Kubernetes / systemd services
|
|
248
|
+
* Need for clear operational logs
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## When Not to Use
|
|
253
|
+
|
|
254
|
+
* Single-file scripts
|
|
255
|
+
* No multiprocessing
|
|
256
|
+
* No domain separation required
|
|
257
|
+
|
|
258
|
+
Plain `logging` is sufficient in those cases.
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## License
|
|
263
|
+
|
|
264
|
+
MIT
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Author
|
|
269
|
+
|
|
270
|
+
**Bruno Miura**
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Iterable
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
REPOSITORY_TEMPLATE = """from typing import Optional
|
|
6
|
+
from queue import Queue
|
|
7
|
+
from miu_logger.conditional import ConditionalLogger
|
|
8
|
+
from miu_logger.config import LogConfig
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class LoggingRepository:
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
config: LogConfig,
|
|
15
|
+
*,
|
|
16
|
+
use_listener: bool = True,
|
|
17
|
+
queue: Optional[Queue] = None
|
|
18
|
+
) -> None: ...
|
|
19
|
+
|
|
20
|
+
{domains}
|
|
21
|
+
|
|
22
|
+
def get(self, domain: str) -> ConditionalLogger: ...
|
|
23
|
+
def get_queue(self) -> Queue: ...
|
|
24
|
+
def shutdown(self) -> None: ...
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
CONDITIONAL_LOGGER_STUB = """from typing import Any
|
|
28
|
+
import logging
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ConditionalLogger:
|
|
32
|
+
def __init__(self, logger: logging.Logger, should_log_debug: Any) -> None: ...
|
|
33
|
+
|
|
34
|
+
def debug(self, msg: str, *args: Any, **kwargs: Any) -> None: ...
|
|
35
|
+
def info(self, msg: str, *args: Any, **kwargs: Any) -> None: ...
|
|
36
|
+
def warning(self, msg: str, *args: Any, **kwargs: Any) -> None: ...
|
|
37
|
+
def error(self, msg: str, *args: Any, **kwargs: Any) -> None: ...
|
|
38
|
+
def critical(self, msg: str, *args: Any, **kwargs: Any) -> None: ...
|
|
39
|
+
def exception(self, msg: str, *args: Any, **kwargs: Any) -> None: ...
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def generate_repository_stub(domains: Iterable[str], out_dir: str = "typings") -> None:
|
|
44
|
+
out_path = Path(out_dir) / "miu_logger"
|
|
45
|
+
out_path.mkdir(parents=True, exist_ok=True)
|
|
46
|
+
|
|
47
|
+
# Generate repository.pyi
|
|
48
|
+
lines = []
|
|
49
|
+
for d in domains:
|
|
50
|
+
lines.append(f" @property")
|
|
51
|
+
lines.append(f" def {d}(self) -> ConditionalLogger: ...")
|
|
52
|
+
|
|
53
|
+
content = REPOSITORY_TEMPLATE.format(domains="\n".join(lines))
|
|
54
|
+
|
|
55
|
+
with open(out_path / "repository.pyi", "w") as f:
|
|
56
|
+
f.write(content)
|
|
57
|
+
|
|
58
|
+
# Generate conditional.pyi
|
|
59
|
+
with open(out_path / "conditional.pyi", "w") as f:
|
|
60
|
+
f.write(CONDITIONAL_LOGGER_STUB)
|
|
61
|
+
|
|
62
|
+
print(f"✓ Generated stubs in {out_path}")
|