A json configured logger for Finalsa projects
Project description
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 >= 4.0.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
Project details
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 finalsa_common_logger-1.0.2.tar.gz.
File metadata
- Download URL: finalsa_common_logger-1.0.2.tar.gz
- Upload date:
- Size: 7.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.9.28 {"installer":{"name":"uv","version":"0.9.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
635cbdf4262d10ef6566bcf3a0085f096e417430d2680fc84acfb1ec33b4175e
|
|
| MD5 |
eed62aa92d4be6310f9866353f7dfa9b
|
|
| BLAKE2b-256 |
462591ed218d9a44e699684b23c9fcff99704022ad98b61fe99d82aa5304238b
|
File details
Details for the file finalsa_common_logger-1.0.2-py3-none-any.whl.
File metadata
- Download URL: finalsa_common_logger-1.0.2-py3-none-any.whl
- Upload date:
- Size: 9.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.9.28 {"installer":{"name":"uv","version":"0.9.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0e71b274a2de7ba227d470cdf174b3d53c238c206530692c7b8a7183535d0893
|
|
| MD5 |
7e5b1170ab102eaa569b7b5fa98f1935
|
|
| BLAKE2b-256 |
ec3bfa6eb1471c4ffb7fe7590ea4b9ed359b46969616a7e80e6f9432ef48550b
|