Python Logging Module Documentation
November 18, 2025 · View on GitHub
Overview
This logging module provides a structured, JSON-based logging system with in-memory buffering. It is designed for runtime environments where log data needs to be serialized, transmitted, or parsed consistently. The module integrates seamlessly with the Python logging standard library but extends it with JSON formatting, a shared buffer, and parsing utilities.
Module Structure
logger/
│
├── __init__.py # Main entry point, global configuration
├── logger.py # Factory for logger instances
├── config.py # configuration variables and thread safety lock
├── formatter.py # JSON formatter for structured logs
├── bufferhandler.py # In-memory log buffer (not shown above)
└── parser.py # Parses and normalizes incoming log lines
Components
1. get_logger() (from __init__.py and logger.py)
Purpose
Creates and returns a configured logger instance. The logger outputs JSON-formatted messages and can optionally store logs in a shared buffer.
Signature
get_logger(name="runtime", use_buffer: bool = False)
Behavior
- Sets log level to
DEBUG. - Ensures a
StreamHandleris attached (outputs tostdout). - Optionally adds a shared
BufferHandlerfor in-memory collection. - Uses
JsonFormatterfor consistent output.
Returns
(logger_instance, buffer_handler)
Example
from logger import get_logger
logger, buffer = get_logger("logger", use_buffer=True)
logger.info("System initialized")
2. JsonFormatter
Location
logger/formatter.py
Purpose
Formats log records as structured JSON objects, ensuring every log entry contains id, timestamp, level, and message fields.
Key Features
- Automatically adds timestamps in ISO 8601 UTC format.
- Detects pre-formatted JSON and augments it with missing fields.
- Includes a global
log_idfor log correlation.
Example Output
{
"id": "1",
"timestamp": "2025-10-22T19:03:04.123456Z",
"level": "INFO",
"message": "System initialized"
}
3. BufferHandler
Location
logger/bufferhandler.py
Purpose
Stores log records in memory for later retrieval or transmission. Useful for APIs that expose logs or batch log uploads.
Notes
- Shared across all logger instances.
- Can be flushed or accessed by components that need recent log history.
4. LogParser
Location
logger/parser.py
Purpose
Normalizes incoming log lines into structured JSON and re-logs them via the Python logging system.
Key Features
- Detects JSON logs and preserves their structure.
- Supports pattern
[LEVEL] messageusing regex. - Defaults to INFO level if no match is found.
- Converts parsed logs to JSON and forwards to the configured logger.
Example Usage
from logger import LogParser, get_logger
logger, _ = get_logger("logger")
parser = LogParser(logger)
parser.parse_and_log("[ERROR] Connection failed")
parser.parse_and_log('{"level":"INFO","message":"Reconnected"}')
Example Output
{
"timestamp": "1729587834",
"level": "ERROR",
"message": "Connection failed"
}
Thread Safety
This logging architecture is designed to be thread-safe, following Python’s built-in logging guarantees:
- The Python
loggingmodule ensures that handler operations are protected by internal locks (threading.RLock). - Each handler (e.g.,
StreamHandler,BufferHandler) can safely receive log records from multiple threads simultaneously. - The shared
BufferHandlerensures consistent appends by relying on the thread-safelogging.Handler.emit()method. - For applications with heavy concurrency (e.g., multithreaded servers), this architecture prevents race conditions during log writes or JSON serialization.
Recommendations for Multithreaded Use:
- Avoid modifying shared handler state (like buffer lists) outside
emit()or synchronized contexts. - Use
QueueHandlerorQueueListenerfor extremely high-throughput scenarios. - Always use a dedicated logger instance per module, not per thread.
Example: Combined Usage
from logger import get_logger, LogParser
# Initialize logger
logger, buffer = get_logger("runtime", use_buffer=True)
# Log directly
logger.info("Startup complete")
# Parse and re-log an external message
parser = LogParser(logger)
parser.parse_and_log("[WARNING] Low voltage detected")
# Access buffered logs
for record in buffer.buffer:
print(record)
Sample Output:
{"id": "runtime-01", "timestamp": "2025-10-22T19:12:44.312Z", "level": "INFO", "message": "Startup complete"}
{"id": "runtime-01", "timestamp": "2025-10-22T19:12:44.325Z", "level": "WARNING", "message": "Low voltage detected"}
Development & Testing
This section guides developers on how to test and validate the logging module during development.
1. Unit Testing
The module can be tested using Python’s built-in unittest framework or pytest.
Example: test_logger.py
import unittest
from logger import get_logger, LogParser
class TestLogger(unittest.TestCase):
def setUp(self):
self.logger, self.buffer = get_logger("test_logger", use_buffer=True)
self.parser = LogParser(self.logger)
def test_info_log(self):
self.logger.info("Test info message")
self.assertIn("Test info message", self.buffer.buffer[-1])
def test_warning_log(self):
self.logger.warning("Test warning")
log_entry = self.buffer.buffer[-1]
self.assertIn('"level": "WARNING"', log_entry)
def test_parse_plain_text(self):
self.parser.parse_and_log("[ERROR] External error")
log_entry = self.buffer.buffer[-1]
self.assertIn('"level": "ERROR"', log_entry)
self.assertIn("External error", log_entry)
def test_parse_json_log(self):
json_line = '{"level": "INFO", "message": "External JSON"}'
self.parser.parse_and_log(json_line)
log_entry = self.buffer.buffer[-1]
self.assertIn("External JSON", log_entry)
if __name__ == "__main__":
unittest.main()
Run tests:
python -m unittest test_logger.py
# or with pytest
pytest test_logger.py
2. Buffer Validation
Check that logs are properly stored in the buffer and old entries are trimmed if using a capacity limit.
logger, buffer = get_logger("runtime", use_buffer=True)
for i in range(1100): # assuming default capacity 1000
logger.info(f"Message {i}")
assert len(buffer.buffer) <= 1000
3. Parser Validation
Ensure the LogParser correctly handles:
- JSON input
- Regex pattern
[LEVEL] message - Plain-text messages
Test Example:
parser.parse_and_log("[INFO] Hello World")
parser.parse_and_log('{"level":"DEBUG","message":"Debug JSON"}')
parser.parse_and_log("Just a string message")
Check that all entries are JSON-formatted in the buffer.
4. Thread-Safety Testing
Simulate concurrent logging from multiple threads:
import threading
def log_messages(logger, count=100):
for i in range(count):
logger.info(f"Thread log {i}")
logger, buffer = get_logger("thread_test", use_buffer=True)
threads = [threading.Thread(target=log_messages, args=(logger,)) for _ in range(5)]
[t.start() for t in threads]
[t.join() for t in threads]
print(f"Total logs in buffer: {len(buffer.buffer)}")
All entries should appear in the buffer without corruption or race conditions.
5. Coverage
Measure test coverage:
pytest --cov=logger test_logger.py
This ensures all log paths, parser cases, and formatter scenarios are exercised.