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.0.tar.gz (7.9 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.0-py3-none-any.whl (9.7 kB view details)

Uploaded Python 3

File details

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

File metadata

File hashes

Hashes for finalsa_common_logger-1.0.0.tar.gz
Algorithm Hash digest
SHA256 595bd2898d4f097478d5a43b64a6a6610e6e84375e03a7acc8acbed06748e7aa
MD5 2c24c00fc552dd25d8fd5267a8d7a502
BLAKE2b-256 4fc7f9e6558f73182bc1ccfedacf5b53ff126bae67979dd1fa5512c5386d97f5

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for finalsa_common_logger-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d833c12f35390bf826254cc0cb566b5e5e1611f5d426ce3edd915898d8a47b22
MD5 55ecb2e79b2a20a7b2e5b2908dcbe072
BLAKE2b-256 ef5c81de68563a9767ad70426b24a5ad4048b7f6a1629e7cdefc1f33a462749e

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