Skip to main content

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.

Tests Coverage

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 orjson for 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.0
  • orjson >= 3.10.15
  • python-json-logger == 3.3.0

License

MIT License - see LICENSE.md for details.

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes with tests
  4. Run the test suite (uv run pytest)
  5. Commit your changes (git commit -m 'Add amazing feature')
  6. Push to the branch (git push origin feature/amazing-feature)
  7. 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

finalsa_common_logger-1.0.1.tar.gz (7.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

finalsa_common_logger-1.0.1-py3-none-any.whl (9.6 kB view details)

Uploaded Python 3

File details

Details for the file finalsa_common_logger-1.0.1.tar.gz.

File metadata

File hashes

Hashes for finalsa_common_logger-1.0.1.tar.gz
Algorithm Hash digest
SHA256 30f26ecc5bee385ef9a6a7bf8f8a37394b167554de852c86fd689b4d9698b499
MD5 8f9b95a945527a6c09a4dfcf75cdd7d5
BLAKE2b-256 94598484effb0808933070e12760cb753c0642881bc0c88620c62e7bbe1ec396

See more details on using hashes here.

File details

Details for the file finalsa_common_logger-1.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for finalsa_common_logger-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 22227df78c5c8ce44b50ace848d0e45fbc6ecf5c5a791efe1d238cafc73becbd
MD5 62fa97799feae2fbe3f11f077ee55038
BLAKE2b-256 007f32a654ff916b46695fcd19d5a42aa186c5469d67e23edfcb22d269d73c84

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page