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.
@@ -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,10 @@
1
+ JSONL_LOGGER.py
2
+ LICENSE
3
+ README.md
4
+ pyproject.toml
5
+ setup.cfg
6
+ JSONL_LOGGER.egg-info/PKG-INFO
7
+ JSONL_LOGGER.egg-info/SOURCES.txt
8
+ JSONL_LOGGER.egg-info/dependency_links.txt
9
+ JSONL_LOGGER.egg-info/requires.txt
10
+ JSONL_LOGGER.egg-info/top_level.txt
@@ -0,0 +1,5 @@
1
+ python-dotenv>=1.2.2
2
+
3
+ [dev]
4
+ pytest>=9.0.2
5
+ pytest-cov>=7.1.0
@@ -0,0 +1 @@
1
+ JSONL_LOGGER