Finalsa Common Logger
A robust, JSON-configured logger designed for Finalsa projects with built-in correlation ID tracking, structured logging, and AWS Lambda compatibility.
Features
- 🔗 Correlation ID Tracking: Automatic correlation, trace, and span ID injection
- 📊 Structured JSON Logging: Clean, parseable JSON output for log aggregation
- ⚡ AWS Lambda Optimized: Pre-configured for serverless environments
- 🌐 Access Log Support: Specialized formatter for HTTP access logs
- 🎯 High Performance: Built on
orjsonfor fast JSON serialization - 🔧 Flexible Configuration: Easy setup with sensible defaults
- 🧪 Well Tested: 98% test coverage with comprehensive integration tests
Installation
# Using uv (recommended)
uv add finalsa-common-logger
# Using pip
pip install finalsa-common-logger
Quick Start
Basic Usage
import logging
from logging.config import dictConfig
from finalsa.common.logger import LAMBDA_DEFAULT_LOGGER
# Apply the default configuration
dictConfig(LAMBDA_DEFAULT_LOGGER)
# Get a logger and start logging
logger = logging.getLogger(__name__)
logger.info("Hello, structured logging!")
logger.error("Something went wrong", extra={"user_id": 12345, "action": "login"})
Output:
{"timestamp": "2024-01-15T12:30:45.123456Z", "level": "INFO", "message": "Hello, structured logging!", "correlation_id": "abc-123", "trace_id": "def-456", "span_id": "ghi-789"}
{"timestamp": "2024-01-15T12:30:46.789012Z", "level": "ERROR", "message": "Something went wrong", "user_id": 12345, "action": "login", "correlation_id": "abc-123", "trace_id": "def-456", "span_id": "ghi-789"}
AWS Lambda Example
import logging
from logging.config import dictConfig
from finalsa.common.logger import LAMBDA_DEFAULT_LOGGER
# Configure logging once at module level
dictConfig(LAMBDA_DEFAULT_LOGGER)
logger = logging.getLogger(__name__)
def lambda_handler(event, context):
logger.info("Lambda invoked", extra={
"request_id": context.aws_request_id,
"function_name": context.function_name
})
try:
# Your business logic here
result = process_event(event)
logger.info("Lambda completed successfully", extra={"result_count": len(result)})
return {"statusCode": 200, "body": result}
except Exception as e:
logger.exception("Lambda execution failed", extra={"event": event})
return {"statusCode": 500, "body": "Internal server error"}
Access Log Example
import logging
from logging.config import dictConfig
from finalsa.common.logger import LAMBDA_DEFAULT_LOGGER
from finalsa.common.logger.formatter import AccessJsonFormatter
# Setup with access log formatter
config = LAMBDA_DEFAULT_LOGGER.copy()
config['formatters']['access'] = {
'class': 'finalsa.common.logger.formatter.AccessJsonFormatter'
}
config['handlers']['access'] = {
'class': 'logging.StreamHandler',
'formatter': 'access',
'filters': ['correlation_id']
}
config['loggers']['access'] = {
'handlers': ['access'],
'level': 'INFO',
'propagate': False
}
dictConfig(config)
access_logger = logging.getLogger('access')
# Log HTTP access (ip, method, path, protocol, status_code)
access_logger.info("HTTP Request", "192.168.1.100", "POST", "/api/users", "HTTP/1.1", 201)
Output:
{"timestamp": "2024-01-15T12:30:45.123456Z", "level": "INFO", "client_addr": "192.168.1.100", "method": "POST", "path": "/api/users", "protocol": "HTTP/1.1", "status_code": 201, "correlation_id": "abc-123"}
Configuration
Default Configuration
The LAMBDA_DEFAULT_LOGGER provides a production-ready configuration:
LAMBDA_DEFAULT_LOGGER = {
'version': 1,
'disable_existing_loggers': True,
'filters': {
'correlation_id': {'()': 'finalsa.common.logger.filter.CorrelationIdFilter'},
},
'formatters': {
'console': {
'class': 'finalsa.common.logger.CustomJsonFormatter',
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'filters': ['correlation_id'],
'formatter': 'console',
},
},
'loggers': {
'root': {'handlers': ['console'], 'level': 'INFO', 'propagate': True},
},
}
Custom Configuration
import logging
from logging.config import dictConfig
from finalsa.common.logger import CorrelationIdFilter, CustomJsonFormatter
# Create your own configuration
CUSTOM_CONFIG = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'correlation_id': {'()': CorrelationIdFilter},
},
'formatters': {
'detailed': {
'class': CustomJsonFormatter,
'format': '%(timestamp)s %(level)s %(name)s %(funcName)s:%(lineno)d %(message)s'
},
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'level': 'DEBUG',
'filters': ['correlation_id'],
'formatter': 'detailed',
},
'file': {
'class': 'logging.FileHandler',
'filename': 'app.log',
'level': 'INFO',
'filters': ['correlation_id'],
'formatter': 'detailed',
}
},
'loggers': {
'myapp': {
'handlers': ['console', 'file'],
'level': 'DEBUG',
'propagate': False
},
'root': {
'handlers': ['console'],
'level': 'WARNING',
}
},
}
dictConfig(CUSTOM_CONFIG)
Components
CorrelationIdFilter
Automatically injects correlation, trace, and span IDs into log records using the finalsa-traceability context.
from finalsa.common.logger import CorrelationIdFilter
# The filter automatically adds these fields to each log record:
# - correlation_id
# - trace_id
# - span_id
CustomJsonFormatter
A JSON formatter that adds timestamps and normalizes log levels.
from finalsa.common.logger import CustomJsonFormatter
formatter = CustomJsonFormatter()
# Automatically adds:
# - timestamp (ISO 8601 format with microseconds)
# - level (normalized to uppercase)
# - Converts correlation_id to string if present
AccessJsonFormatter
Specialized formatter for HTTP access logs that parses request details from log record arguments.
from finalsa.common.logger.formatter import AccessJsonFormatter
# Expects log record args in this order:
# (client_ip, http_method, path, protocol, status_code)
access_logger.info("Request", "192.168.1.1", "GET", "/api/health", "HTTP/1.1", 200)
Integration with Finalsa Traceability
This logger integrates seamlessly with finalsa-traceability for distributed tracing:
from finalsa.traceability import set_correlation_id, set_trace_id
from finalsa.common.logger import LAMBDA_DEFAULT_LOGGER
import logging
dictConfig(LAMBDA_DEFAULT_LOGGER)
logger = logging.getLogger(__name__)
# Set tracing context
set_correlation_id("user-session-123")
set_trace_id("request-trace-456")
# All subsequent logs will include these IDs
logger.info("Processing user request") # Will include correlation_id and trace_id
Error Handling
The logger gracefully handles missing dependencies and malformed configurations:
# If finalsa-traceability is not available, correlation IDs will be None
# If log record args are malformed for AccessJsonFormatter, it raises appropriate exceptions
# The logger continues to function even with missing optional fields
Development
Setup
git clone https://github.com/finalsa/finalsa-common-logger.git
cd finalsa-common-logger
uv sync
Running Tests
# Run all tests
uv run pytest
# Run with coverage
uv run pytest --cov=finalsa.common.logger --cov-report=term-missing
# Run specific test file
uv run pytest tests/test_formatter.py -v
Type Checking
uv run mypy finalsa/
Requirements
- Python >= 3.10
finalsa-traceability >= 1.0.0orjson >= 3.10.15python-json-logger == 3.3.0
License
MIT License - see LICENSE.md for details.
Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes with tests
- Run the test suite (
uv run pytest) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Changelog
v0.1.0
- Initial release with basic JSON logging
- Correlation ID filtering
- AWS Lambda configuration
- Access log formatter
- Comprehensive test suite
Release files for finalsa-common-logger 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| finalsa_common_logger-1.0.1.tar.gz | 7.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| finalsa_common_logger-1.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 17.4 kB
Release files / finalsa_common_logger-1.0.1.tar.gz
| Download URL | finalsa_common_logger-1.0.1.tar.gz |
|---|---|
| Size | 7.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
30f26ecc5bee385ef9a6a7bf8f8a37394b167554de852c86fd689b4d9698b499
|
|
BLAKE2b-256 checksum How to use checksums |
94598484effb0808933070e12760cb753c0642881bc0c88620c62e7bbe1ec396
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.8.11
|
Release files / finalsa_common_logger-1.0.1-py3-none-any.whl
| Download URL | finalsa_common_logger-1.0.1-py3-none-any.whl |
|---|---|
| Size | 9.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
22227df78c5c8ce44b50ace848d0e45fbc6ecf5c5a791efe1d238cafc73becbd
|
|
BLAKE2b-256 checksum How to use checksums |
007f32a654ff916b46695fcd19d5a42aa186c5469d67e23edfcb22d269d73c84
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.8.11
|