This implementation demonstrates Effortless Structured Logging in Python without compiler plugins or language modifications. It uses runtime frame introspection to extract both the template and variable values from f-strings.
When you write:
logger.log(f"Hello {user}, your balance is {balance}")The logger automatically extracts and outputs:
{
"template": "Hello %user%, your balance is %balance%",
"args": {
"user": "John",
"balance": 42
}
}The implementation leverages Python's powerful runtime introspection capabilities:
- Frame Introspection: Uses
inspect.currentframe()to capture the caller's execution context - Source Code Extraction: Reads the actual source line where
log()was called - F-String Parsing: Uses regex to extract variable names from the f-string syntax
- Variable Resolution: Evaluates variables in the caller's local and global scope
- Structured Output: Generates JSON with template and resolved values
- StructuredLogger: Main class that implements the logging functionality
- _get_source_line(): Extracts the source code from the calling frame
- _extract_template_and_args(): Parses f-strings and resolves variable values
from logger import logger
user = "John"
balance = 42
logger.log(f"Hello {user}, your balance is {balance}")Output:
{
"template": "Hello %user%, your balance is %balance%",
"args": {
"user": "John",
"balance": 42
}
}name = "Alice"
age = 30
score = 95.5
active = True
logger.log(f"User {name} is {age} years old with score {score} (active: {active})")x = 10
y = 5
logger.log(f"The sum of {x} and {y} is {x + y}")Output:
{
"template": "The sum of %x% and %y% is %x + y%",
"args": {
"x": 10,
"y": 5,
"x + y": 15
}
}data = {"key": "value", "count": 123}
items = [1, 2, 3]
logger.log(f"Data: {data}, Items: {items}")cd python/structured_logging
python example.pyPython provides excellent runtime introspection capabilities that make this implementation possible:
- inspect module: Gives access to live frame objects with source code information
- f_locals and f_globals: Provide access to the caller's variable scope
- eval(): Allows dynamic evaluation of expressions in the caller's context
- Source code access: Python can read its own source files at runtime
- Requires source code to be available (won't work with compiled-only distributions)
- F-string must be on a single line
- Complex multi-line f-strings may need special handling
- Performance overhead from frame introspection (not suitable for hot paths)
Unlike macro-based approaches in languages like Rust or Scala, this is a pure library solution with no:
- Compiler plugins
- Build-time code generation
- Language extensions
- Special syntax
The implementation properly manages frame references to avoid reference cycles:
try:
# Work with frames
...
finally:
del caller_frame
del frameThe logger uses assertions for critical failures and graceful fallbacks for variable resolution:
- Assertions ensure frames are available
- Try-except blocks handle variable evaluation errors
- Multiple fallback strategies for source code extraction
This implementation proves that Python can achieve structured logging through library-level metaprogramming, using its powerful runtime introspection capabilities. While it has some limitations compared to compile-time macro systems, it demonstrates that sophisticated metaprogramming features can be implemented without language modifications.
✅ Implemented - Pure library solution using frame introspection