A minimal Python logging system built from scratch to explore how real-world logging frameworks capture execution context, structure log data, and safely persist side effects to disk.
- Why this project exists
- Features
- Example Usage
- Project Structure
- Architecture Overview
- Testing Strategy
- Key Design Decisions
- Planned Improvements
- Tech Stack
- What I learned
Most developers interact with logging systems as black boxes. This project was built to break that abstraction and understand:
- 🔍 how execution context (file, function, line number) is captured at runtime
- 📐 why structured logs are preferred over plain text
- 🧪 how side effects like file creation can be tested safely
- 🧩 how logging responsibilities can be cleanly separated
The goal is not to replace existing logging libraries, but to understand how they work internally.
| Feature | Description |
|---|---|
| 📄 Structured JSON logging | One log entry per line (newline-delimited JSON) |
| 🏷️ Enum-based log levels | Clean, type-safe severity levels via LogType |
| 🔎 Runtime metadata capture | Automatically records file, function, line number, and timestamp |
| 💾 File-based persistence | Writes logs to .log files |
| ✅ Fully tested | Pytest suite with isolated filesystem tests using tmp_path |
from logging_utility.logger import Logger
from logging_utility.log_type import LogType
logger = Logger("app")
logger.log(LogType.INFO, "Application started")
logger.log(LogType.ERROR, "Something went wrong")Produces structured log entries like:
{
"log_type": "INFO",
"message": "Application started",
"timestamp": "2026-01-25 19:42:11",
"file_name": "main.py",
"function_name": "run",
"line_number": 14
}Each entry is written as a single JSON object per line to app.log, mirroring formats commonly used in production log ingestion pipelines.
logging-utility/
├── src/
│ └── logging_utility/
│ ├── __init__.py
│ ├── logger.py
│ └── log_type.py
├── tests/
│ └── logger_test.py
├── conftest.py
└── .gitignore
Logger
├── constructs structured log record
├── captures runtime metadata via introspection
└── persists log entry to disk
LogType (Enum)
└── defines severity levels
Responsibilities are intentionally separated to keep logging logic readable, testable, and extensible.
All tests are written using pytest and focus on externally observable behavior rather than implementation details.
Tests verify:
- ✅ returned structured log data
- ✅ creation of
.logfiles - ✅ presence of runtime metadata
- ✅ isolation of filesystem side effects using
tmp_path
Run tests from the project root:
pytestAll file operations occur in temporary directories and leave no persistent artifacts.
- Runtime introspection — metadata is captured at log-call time to reflect the true execution context
- Newline-delimited JSON — supports downstream parsing and filtering
- Isolated filesystem tests — deterministic behavior with no real file I/O during testing
- Outcome-based assertions — tests assert observable results, allowing safe internal refactoring
- Log filtering and aggregation utilities
- Configurable output targets (file vs console)
- Log rotation support
- Custom formatter support
- CLI tool for querying structured log files
| Tool | Purpose |
|---|---|
| Python | Core language |
| pytest | Test framework |
| inspect | Runtime introspection |
| pathlib | Filesystem interaction |
- How logging frameworks capture execution context at runtime
- Why structured logs scale better than plain-text output
- How to test side effects without touching real files
- How separation of concerns simplifies debugging and extensibility