Async queue-based structured JSONL logging with thread-safe performance and auto-detected module names
Project description
JSONL_LOGGER
Async queue-based structured logging with JSONL output — single file, zero dependencies beyond python-dotenv.
Installation
pip install JSONL_LOGGER
Quick Start
Set environment variables in .env
PROJECT_DIRECTORY=/path/to/your/project
LOGS_LOCAL_TIMEZONE=Asia/Kolkata
LOGGER_FILE_NAME=orders # Optional: default log file name (default: "LOGS")
Alternatively, set LOGGER_FILE_NAME in your module
# In your_module.py
import os
os.environ["LOGGER_FILE_NAME"] = "orders"
from JSONL_LOGGER import log_info
log_info("Order placed", order_id=123)
# logfile_name="orders", module_name="your_module" (auto-detected from __name__)
LOG FILE NAMING
-
logfile_name: The logical group for log files. Set via:
LOGGER_FILE_NAMEenv var in .envos.environ["LOGGER_FILE_NAME"]in your modulelogfile_name=parameter in function callFalls backto "LOGS" if none set
-
module_name: The Python module name. Auto-detected from caller's
__name__. Can be overridden viamodule_name=parameter. -
source_file: The actual Python filename. Auto-detected from call stack. Can be overridden via
source_file=parameter.
Import and use
from JSONL_LOGGER import log_info, log_warn, log_error, log_metric
from JSONL_LOGGER import send_notification, send_notification_async
# Info logging with structured fields
log_info("User logged in", user_id=123, email="user@example.com")
# Warning logging
log_warn("Rate limit approaching", remaining=10, reset_seconds=60)
# Error logging with error codes
log_error("Payment failed", error_code=500, error="insufficient_funds")
# Metrics logging (custom METR level between INFO and WARNING)
log_metric("api_latency_ms", 142.5, unit="ms", endpoint="/checkout", method="POST")
log_metric("request_count", 1000, logfile_name="orders", module_name="order_service", status="success")
# Notifications to main_logger.jsonl
send_notification("Application started", source_file="main.py")
await send_notification_async("Deployment completed", source_file="deploy.py")
All functions support optional parameters
logfile_name: Log file name (auto-detected from LOGGER_FILE_NAME env/globals)module_name: Source module name (auto-detected from caller's__name__)source_file: Actual source filename (auto-detected from call stack)
Output
{PROJECT_DIRECTORY}/_LOGS_DIRECTORY/{YYYY_MM_DD}/LOGS/{logfile_name}.jsonl
Example: /path/to/logs/_LOGS_DIRECTORY/2026_04_03/LOGS/orders.jsonl
JSONL fields
timestamp, timestamp_local, level, logfile_name, module_name, source_file, message, **extra_fields
Configuration
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
PROJECT_DIRECTORY |
Yes | — | Root directory for log storage |
LOGS_LOCAL_TIMEZONE |
Yes | — | Local timezone (e.g. Asia/Kolkata, UTC) |
LOGGER_FILE_NAME |
No | LOGS |
Default log file name |
CONSOLE_LOGGING_ENABLED |
No | false |
Enable colored console output |
LOGGER_REGISTER_SIGNALS |
No | false |
Register SIGINT/SIGTERM handlers for flush |
LOGGER_DAEMON_THREAD |
No | true |
Writer thread daemon status |
LOGGER_MAX_BUFFER_SIZE |
No | 200000 |
Maximum buffer entries before drop |
Common Timezones
Asia: Asia/Kolkata, Asia/Dubai, Asia/Singapore, Asia/Tokyo
Europe: Europe/London, Europe/Paris, Europe/Berlin
Americas: America/New_York, America/Chicago, America/Los_Angeles
UTC: UTC
Output
File Structure
{PROJECT_DIRECTORY}/_LOGS_DIRECTORY/{YYYY_MM_DD}/LOGS/{logfile_name}.jsonl
Example: /path/to/logs/_LOGS_DIRECTORY/2026_04_03/LOGS/orders.jsonl
JSONL Format
Each log entry is a valid JSON object on a single line:
{"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}
Dual-Write Behavior
| Function | Writes To |
|---|---|
log_info() |
{module}.jsonl |
log_warn() |
{module}.jsonl, {module}.warn.jsonl |
log_error() |
{module}.jsonl, {module}.errors.jsonl |
log_metric() |
{module}.metrics.jsonl |
send_notification() |
main_logger.jsonl |
Key Features
- Queue-Based Async Logging — Background thread writes to disk; API calls return immediately
- Per-Module Log Files — Each module gets its own JSONL file for independent retention
- Dual Timestamps — UTC for machine parsing, local time for human readability
- Retry with Exponential Backoff + Jitter — 3 attempts with exponential backoff and ±25% jitter
- Zero Data Loss — Buffer capped at 200k entries; oldest dropped on overflow
- Signal Handlers — Optional SIGINT/SIGTERM flush on shutdown
- Lazy Config Loading — No side effects on import; validated on first use
Performance
┌───────────────────┬──────────┬──────────┬────────────┬─────────────────────────┐
│ Test │ Logs │ Time │ Throughput │ Status │
├───────────────────┼──────────┼──────────┼────────────┼─────────────────────────┤
│ Single-thread │ 10,000 │ 1.04 sec │ 9,643/sec │ ✅ PASS │
│ Multi-thread │ 100,000 │ 16.87 sec│ 5,902/sec │ ✅ PASS │
└───────────────────┴──────────┴──────────┴────────────┴─────────────────────────┘
System: Ubuntu 24.04.4 LTS | AMD Ryzen 7 5800H (16 cores, 13Gi RAM) | Python 3.12.3
Tested: 2026-04-03 12:38 UTC (10 runs average)
API Reference
log_info(message, logfile_name=None, module_name=None, **extra_fields)
Log an info message with optional structured fields.
message: Log messagelogfile_name: Log file name (auto-detected if omitted)module_name: Source module name (auto-detected if omitted)**extra_fields: Additional structured fields
log_warn(message, logfile_name=None, module_name=None, **extra_fields)
Log a warning message. Writes to both main and .warn.jsonl files.
- Same parameters as
log_info
log_error(message, logfile_name=None, module_name=None, **extra_fields)
Log an error message. Writes to both main and .errors.jsonl files.
- Same parameters as
log_info
log_metric(metric_name, value, unit="", logfile_name=None, module_name=None, **tags)
Log a metric with custom METR level (between INFO and WARNING).
metric_name: Metric identifier (e.g. "api_latency_ms")value: Numeric value (int or float)unit: Unit label (e.g. "ms", "bytes")logfile_name: Log file name (auto-detected if omitted)module_name: Source module name (auto-detected if omitted)**tags: Additional fields for grouping/filtering
send_notification(message, logfile_name=None, module_name=None, source_file=None)
Send a notification to the main_logger.jsonl file (blocking).
message: Notification textsource_file: Actual source filename (auto-detected if omitted)
send_notification_async(message, logfile_name=None, module_name=None)
Send a notification to the main_logger.jsonl file (non-blocking).
- Same parameters as
send_notification
_flush_logs()
Manually flush all buffered logs to disk.
Error Handling
The logger will raise ValueError if:
PROJECT_DIRECTORYis not set or doesn't exist (on first use, not at import)LOGS_LOCAL_TIMEZONEis not set- Directory is not writable
Non-primitive types in extra fields will emit a stderr warning and be stringified.
Retry Policy
Disk writes use _with_file_retry():
- Max attempts: 3
- Delay: Exponential backoff (0.1s, 0.2s, 0.4s) with ±25% jitter
- Retryable: All exceptions during file write
- On exhaustion: Lines re-buffered; oldest dropped if buffer exceeds cap
Test Coverage
python3 -m pytest JSONL_LOGGER.py -v
| Function | Tier | Tests | What is tested |
|---|---|---|---|
log_info() |
1 | 6 | level, message, auto-detect, empty, special chars, 10k msg, dual-source |
log_warn() |
1 | 6 | level, message, auto-detect, empty, special chars, 10k msg, dual-source |
log_error() |
1 | 8 | level, message, auto-detect, empty, special chars, 10k msg, dual-write handlers, info/warn isolation, dual-source |
log_metric() |
1 | 10 | METR level, float/int value, tags, auto-detect, unit default, zero, negative, metric_name field, audit isolation, dual-source |
send_notification() |
1 | 8 | INFO level, message, module_name routing, source_file, empty msg, emoji/unicode, audit isolation, async variant |
_get_logfile_name() |
2 | 3 | LOGGER_FILE_NAME priority, filename fallback, exception safety |
_get_actual_source_file() |
2 | 2 | bypasses LOGGER_FILE_NAME, exception safety |
_get_log_path() |
2 | 8 | suffix routing, ValueError on missing dirs, path construction, .py stripped, LOGS subdir |
_with_file_retry() |
2 | 5 | success on attempt 1, success on attempt 3, exhaustion sentinel, all exception types, exponential backoff |
_flush_buffer() |
2 | 5 | no-op on empty, cleared after write, re-buffered on exhaustion, buffer cap drop, stderr warning |
QueueHandler.emit() |
2 | 3 | queue.put_nowait called, .errors suffix, full queue prints to stderr |
_flush_logs() |
2 | 2 | sets _shutdown flag, drains queue when thread alive |
_get_timestamp() |
3 | 4 | returns dict, UTC ends with Z, ISO-8601 ms precision, real clock |
_debug_print() |
3 | 2 | stderr when True, silent when False |
_warn_non_primitive_fields() |
3 | 5 | list warning, dict warning, datetime warning, primitives silent, caller module name |
ColoredFormatter.format() |
3 | 6 | INFO green, WARNING yellow, ERROR red, METR emoji, message preserved, RESET present |
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 |
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jsonl_logger-1.7.10.tar.gz.
File metadata
- Download URL: jsonl_logger-1.7.10.tar.gz
- Upload date:
- Size: 33.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c4157c6b1a2909e0cc29ebdfd5268a438823b7cca9002010ec68b3952095db8a
|
|
| MD5 |
19686fee118a9e92bed45d4c2ae8c83b
|
|
| BLAKE2b-256 |
6bca4c2d5583facd85aad27d36cdc579911f037fae80a4f75d5d41524824f9c6
|
File details
Details for the file jsonl_logger-1.7.10-py3-none-any.whl.
File metadata
- Download URL: jsonl_logger-1.7.10-py3-none-any.whl
- Upload date:
- Size: 30.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
71285ea9266f98e7e5ff35b9d815cd9d5fad83c673c78ceed221f1e3ba53a8bb
|
|
| MD5 |
b4658c18327b98dd5b1462b0ee606466
|
|
| BLAKE2b-256 |
6afeb889c083ae266fc54bd07f6f574d95cedbeaaedcd43591e5cf579758b95f
|