JSONL-LOGGER 0.0.1__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.
- jsonl_logger-0.0.1/JSONL_LOGGER.egg-info/PKG-INFO +258 -0
- jsonl_logger-0.0.1/JSONL_LOGGER.egg-info/SOURCES.txt +10 -0
- jsonl_logger-0.0.1/JSONL_LOGGER.egg-info/dependency_links.txt +1 -0
- jsonl_logger-0.0.1/JSONL_LOGGER.egg-info/requires.txt +5 -0
- jsonl_logger-0.0.1/JSONL_LOGGER.egg-info/top_level.txt +1 -0
- jsonl_logger-0.0.1/JSONL_LOGGER.py +2635 -0
- jsonl_logger-0.0.1/LICENSE +21 -0
- jsonl_logger-0.0.1/PKG-INFO +258 -0
- jsonl_logger-0.0.1/README.md +231 -0
- jsonl_logger-0.0.1/pyproject.toml +48 -0
- jsonl_logger-0.0.1/setup.cfg +4 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: JSONL-LOGGER
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Async queue-based structured JSONL logging with thread-safe performance and auto-detected module names
|
|
5
|
+
Author-email: rocky <rocky@null.net>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/rocky/JSONL-LOGGER
|
|
8
|
+
Project-URL: Bug Reports, https://github.com/rocky/JSONL-LOGGER/issues
|
|
9
|
+
Project-URL: Source, https://github.com/rocky/JSONL-LOGGER
|
|
10
|
+
Keywords: logging,jsonl,structured-logging,async-logging,audit-logging
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: System :: Logging
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: python-dotenv>=1.2.2
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest>=9.0.2; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest-cov>=7.1.0; extra == "dev"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# JSONL_LOGGER
|
|
29
|
+
|
|
30
|
+
Async queue-based structured logging with JSONL output — single file, zero dependencies beyond python-dotenv.
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install JSONL_LOGGER
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
### Set environment variables in .env
|
|
41
|
+
```bash
|
|
42
|
+
PROJECT_DIRECTORY=/path/to/your/project
|
|
43
|
+
LOGS_LOCAL_TIMEZONE=Asia/Kolkata
|
|
44
|
+
LOGGER_FILE_NAME=orders # Optional: default log file name (default: "LOGS")
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Alternatively, set LOGGER_FILE_NAME in your module
|
|
48
|
+
```python
|
|
49
|
+
# In your_module.py
|
|
50
|
+
import os
|
|
51
|
+
os.environ["LOGGER_FILE_NAME"] = "orders"
|
|
52
|
+
from JSONL_LOGGER import log_info
|
|
53
|
+
log_info("Order placed", order_id=123)
|
|
54
|
+
# logfile_name="orders", module_name="your_module" (auto-detected from __name__)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### LOG FILE NAMING
|
|
58
|
+
- **logfile_name**: The logical group for log files. Set via:
|
|
59
|
+
1. `LOGGER_FILE_NAME` env var in .env
|
|
60
|
+
2. `os.environ["LOGGER_FILE_NAME"]` in your module
|
|
61
|
+
3. `logfile_name=` parameter in function call
|
|
62
|
+
4. `Falls back` to "LOGS" if none set
|
|
63
|
+
|
|
64
|
+
- **module_name**: The Python module name. Auto-detected from caller's `__name__`. Can be overridden via `module_name=` parameter.
|
|
65
|
+
|
|
66
|
+
- **source_file**: The actual Python filename. Auto-detected from call stack. Can be overridden via `source_file=` parameter.
|
|
67
|
+
|
|
68
|
+
### Import and use
|
|
69
|
+
```python
|
|
70
|
+
from JSONL_LOGGER import log_info, log_warn, log_error, log_metric
|
|
71
|
+
from JSONL_LOGGER import send_notification, send_notification_async
|
|
72
|
+
|
|
73
|
+
# Info logging with structured fields
|
|
74
|
+
log_info("User logged in", user_id=123, email="user@example.com")
|
|
75
|
+
|
|
76
|
+
# Warning logging
|
|
77
|
+
log_warn("Rate limit approaching", remaining=10, reset_seconds=60)
|
|
78
|
+
|
|
79
|
+
# Error logging with error codes
|
|
80
|
+
log_error("Payment failed", error_code=500, error="insufficient_funds")
|
|
81
|
+
|
|
82
|
+
# Metrics logging (custom METR level between INFO and WARNING)
|
|
83
|
+
log_metric("api_latency_ms", 142.5, unit="ms", endpoint="/checkout", method="POST")
|
|
84
|
+
log_metric("request_count", 1000, logfile_name="orders", module_name="order_service", status="success")
|
|
85
|
+
|
|
86
|
+
# Notifications to main_logger.jsonl
|
|
87
|
+
send_notification("Application started", source_file="main.py")
|
|
88
|
+
await send_notification_async("Deployment completed", source_file="deploy.py")
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### All functions support optional parameters
|
|
92
|
+
- `logfile_name`: Log file name (auto-detected from LOGGER_FILE_NAME env/globals)
|
|
93
|
+
- `module_name`: Source module name (auto-detected from caller's `__name__`)
|
|
94
|
+
- `source_file`: Actual source filename (auto-detected from call stack)
|
|
95
|
+
|
|
96
|
+
### Output
|
|
97
|
+
```
|
|
98
|
+
{PROJECT_DIRECTORY}/_LOGS_DIRECTORY/{EXCHANGE}/{YYYY_MM_DD}/LOGS/{logfile_name}.jsonl
|
|
99
|
+
```
|
|
100
|
+
Example: `/path/to/logs/_LOGS_DIRECTORY/NSE/2026_04_03/LOGS/orders.jsonl`
|
|
101
|
+
|
|
102
|
+
### JSONL fields
|
|
103
|
+
`timestamp`, `timestamp_local`, `level`, `logfile_name`, `module_name`, `source_file`, `message`, `**extra_fields`
|
|
104
|
+
|
|
105
|
+
## Configuration
|
|
106
|
+
|
|
107
|
+
### Environment Variables
|
|
108
|
+
|
|
109
|
+
| Variable | Required | Default | Description |
|
|
110
|
+
|----------|----------|---------|-------------|
|
|
111
|
+
| `PROJECT_DIRECTORY` | Yes | — | Root directory for log storage |
|
|
112
|
+
| `LOGS_LOCAL_TIMEZONE` | Yes | — | Local timezone (e.g. `Asia/Kolkata`, `UTC`) |
|
|
113
|
+
| `LOGGER_FILE_NAME` | No | `LOGS` | Default log file name |
|
|
114
|
+
| `CONSOLE_LOGGING_ENABLED` | No | `false` | Enable colored console output |
|
|
115
|
+
| `LOGGER_REGISTER_SIGNALS` | No | `false` | Register SIGINT/SIGTERM handlers for flush |
|
|
116
|
+
| `LOGGER_DAEMON_THREAD` | No | `true` | Writer thread daemon status |
|
|
117
|
+
| `LOGGER_MAX_BUFFER_SIZE` | No | `200000` | Maximum buffer entries before drop |
|
|
118
|
+
|
|
119
|
+
### Common Timezones
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
Asia: Asia/Kolkata, Asia/Dubai, Asia/Singapore, Asia/Tokyo
|
|
123
|
+
Europe: Europe/London, Europe/Paris, Europe/Berlin
|
|
124
|
+
Americas: America/New_York, America/Chicago, America/Los_Angeles
|
|
125
|
+
UTC: UTC
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Output
|
|
129
|
+
|
|
130
|
+
### File Structure
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
{PROJECT_DIRECTORY}/_LOGS_DIRECTORY/{EXCHANGE}/{YYYY_MM_DD}/LOGS/{logfile_name}.jsonl
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Example: `/path/to/logs/_LOGS_DIRECTORY/NSE/2026_04_03/LOGS/orders.jsonl`
|
|
137
|
+
|
|
138
|
+
### JSONL Format
|
|
139
|
+
|
|
140
|
+
Each log entry is a valid JSON object on a single line:
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{"timestamp":"2026-04-03T05:30:00.000Z","timestamp_local":"2026-04-03T11:00:00.000+05:30","level":"INFO","logfile_name":"orders","module_name":"orders","source_file":"order_service.py","message":"User logged in","user_id":123}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Dual-Write Behavior
|
|
147
|
+
|
|
148
|
+
| Function | Writes To |
|
|
149
|
+
|----------|------------|
|
|
150
|
+
| `log_info()` | `{module}.jsonl` |
|
|
151
|
+
| `log_warn()` | `{module}.jsonl`, `{module}.warn.jsonl` |
|
|
152
|
+
| `log_error()` | `{module}.jsonl`, `{module}.errors.jsonl` |
|
|
153
|
+
| `log_metric()` | `{module}.metrics.jsonl` |
|
|
154
|
+
| `send_notification()` | `main_logger.jsonl` |
|
|
155
|
+
|
|
156
|
+
## Key Features
|
|
157
|
+
|
|
158
|
+
1. **Queue-Based Async Logging** — Background thread writes to disk; API calls return immediately
|
|
159
|
+
2. **Per-Module Log Files** — Each module gets its own JSONL file for independent retention
|
|
160
|
+
3. **Dual Timestamps** — UTC for machine parsing, local time for human readability
|
|
161
|
+
4. **Retry with Exponential Backoff + Jitter** — 3 attempts with exponential backoff and ±25% jitter
|
|
162
|
+
5. **Zero Data Loss** — Buffer capped at 200k entries; oldest dropped on overflow
|
|
163
|
+
6. **Signal Handlers** — Optional SIGINT/SIGTERM flush on shutdown
|
|
164
|
+
7. **Lazy Config Loading** — No side effects on import; validated on first use
|
|
165
|
+
|
|
166
|
+
## Performance
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
┌───────────────────┬──────────┬──────────┬────────────┬─────────────────────────┐
|
|
170
|
+
│ Test │ Logs │ Time │ Throughput │ Status │
|
|
171
|
+
├───────────────────┼──────────┼──────────┼────────────┼─────────────────────────┤
|
|
172
|
+
│ Single-thread │ 10,000 │ 1.04 sec │ 9,643/sec │ ✅ PASS │
|
|
173
|
+
│ Multi-thread │ 100,000 │ 16.87 sec│ 5,902/sec │ ✅ PASS │
|
|
174
|
+
└───────────────────┴──────────┴──────────┴────────────┴─────────────────────────┘
|
|
175
|
+
System: Ubuntu 24.04.4 LTS | AMD Ryzen 7 5800H (16 cores, 13Gi RAM) | Python 3.12.3
|
|
176
|
+
Tested: 2026-04-03 12:38 UTC (10 runs average)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## API Reference
|
|
180
|
+
|
|
181
|
+
### log_info(message, logfile_name=None, module_name=None, **extra_fields)
|
|
182
|
+
Log an info message with optional structured fields.
|
|
183
|
+
- `message`: Log message
|
|
184
|
+
- `logfile_name`: Log file name (auto-detected if omitted)
|
|
185
|
+
- `module_name`: Source module name (auto-detected if omitted)
|
|
186
|
+
- `**extra_fields`: Additional structured fields
|
|
187
|
+
|
|
188
|
+
### log_warn(message, logfile_name=None, module_name=None, **extra_fields)
|
|
189
|
+
Log a warning message. Writes to both main and `.warn.jsonl` files.
|
|
190
|
+
- Same parameters as `log_info`
|
|
191
|
+
|
|
192
|
+
### log_error(message, logfile_name=None, module_name=None, **extra_fields)
|
|
193
|
+
Log an error message. Writes to both main and `.errors.jsonl` files.
|
|
194
|
+
- Same parameters as `log_info`
|
|
195
|
+
|
|
196
|
+
### log_metric(metric_name, value, unit="", logfile_name=None, module_name=None, **tags)
|
|
197
|
+
Log a metric with custom METR level (between INFO and WARNING).
|
|
198
|
+
- `metric_name`: Metric identifier (e.g. "api_latency_ms")
|
|
199
|
+
- `value`: Numeric value (int or float)
|
|
200
|
+
- `unit`: Unit label (e.g. "ms", "bytes")
|
|
201
|
+
- `logfile_name`: Log file name (auto-detected if omitted)
|
|
202
|
+
- `module_name`: Source module name (auto-detected if omitted)
|
|
203
|
+
- `**tags`: Additional fields for grouping/filtering
|
|
204
|
+
|
|
205
|
+
### send_notification(message, logfile_name=None, module_name=None, source_file=None)
|
|
206
|
+
Send a notification to the main_logger.jsonl file (blocking).
|
|
207
|
+
- `message`: Notification text
|
|
208
|
+
- `source_file`: Actual source filename (auto-detected if omitted)
|
|
209
|
+
|
|
210
|
+
### send_notification_async(message, logfile_name=None, module_name=None)
|
|
211
|
+
Send a notification to the main_logger.jsonl file (non-blocking).
|
|
212
|
+
- Same parameters as `send_notification`
|
|
213
|
+
|
|
214
|
+
### _flush_logs()
|
|
215
|
+
Manually flush all buffered logs to disk.
|
|
216
|
+
|
|
217
|
+
## Error Handling
|
|
218
|
+
|
|
219
|
+
The logger will raise `ValueError` if:
|
|
220
|
+
- `PROJECT_DIRECTORY` is not set or doesn't exist (on first use, not at import)
|
|
221
|
+
- `LOGS_LOCAL_TIMEZONE` is not set
|
|
222
|
+
- Directory is not writable
|
|
223
|
+
|
|
224
|
+
Non-primitive types in extra fields will emit a stderr warning and be stringified.
|
|
225
|
+
|
|
226
|
+
## Retry Policy
|
|
227
|
+
|
|
228
|
+
Disk writes use `_with_file_retry()`:
|
|
229
|
+
- **Max attempts**: 3
|
|
230
|
+
- **Delay**: Exponential backoff (0.1s, 0.2s, 0.4s) with ±25% jitter
|
|
231
|
+
- **Retryable**: All exceptions during file write
|
|
232
|
+
- **On exhaustion**: Lines re-buffered; oldest dropped if buffer exceeds cap
|
|
233
|
+
|
|
234
|
+
## Test Coverage
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
python3 -m pytest JSONL_LOGGER.py -v
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
| Function | Tier | Tests | What is tested |
|
|
241
|
+
|----------|------|-------|----------------|
|
|
242
|
+
| `log_info()` | 1 | 6 | level, message, auto-detect, empty, special chars, 10k msg, dual-source |
|
|
243
|
+
| `log_warn()` | 1 | 6 | level, message, auto-detect, empty, special chars, 10k msg, dual-source |
|
|
244
|
+
| `log_error()` | 1 | 8 | level, message, auto-detect, empty, special chars, 10k msg, dual-write handlers, info/warn isolation, dual-source |
|
|
245
|
+
| `log_metric()` | 1 | 10 | METR level, float/int value, tags, auto-detect, unit default, zero, negative, metric_name field, audit isolation, dual-source |
|
|
246
|
+
| `send_notification()` | 1 | 8 | INFO level, message, module_name routing, source_file, empty msg, emoji/unicode, audit isolation, async variant |
|
|
247
|
+
| `_get_logfile_name()` | 2 | 3 | LOGGER_FILE_NAME priority, filename fallback, exception safety |
|
|
248
|
+
| `_get_actual_source_file()` | 2 | 2 | bypasses LOGGER_FILE_NAME, exception safety |
|
|
249
|
+
| `_get_log_path()` | 2 | 8 | suffix routing, ValueError on missing dirs, path construction, .py stripped, LOGS subdir |
|
|
250
|
+
| `_with_file_retry()` | 2 | 5 | success on attempt 1, success on attempt 3, exhaustion sentinel, all exception types, exponential backoff |
|
|
251
|
+
| `_flush_buffer()` | 2 | 5 | no-op on empty, cleared after write, re-buffered on exhaustion, buffer cap drop, stderr warning |
|
|
252
|
+
| `QueueHandler.emit()` | 2 | 3 | queue.put_nowait called, .errors suffix, full queue prints to stderr |
|
|
253
|
+
| `_flush_logs()` | 2 | 2 | sets _shutdown flag, drains queue when thread alive |
|
|
254
|
+
| `_get_timestamp()` | 3 | 4 | returns dict, UTC ends with Z, ISO-8601 ms precision, real clock |
|
|
255
|
+
| `_debug_print()` | 3 | 2 | stderr when True, silent when False |
|
|
256
|
+
| `_warn_non_primitive_fields()` | 3 | 5 | list warning, dict warning, datetime warning, primitives silent, caller module name |
|
|
257
|
+
| `ColoredFormatter.format()` | 3 | 6 | INFO green, WARNING yellow, ERROR red, METR emoji, message preserved, RESET present |
|
|
258
|
+
| `UniformLevelFormatter.format()` | 3 | 9 | valid JSON, required keys, compact separators, WARN level, METR level, extra fields, no reserved leaks, source absent/present, non-serialisable stringified |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
JSONL_LOGGER
|