python-json-logger

repository·main·Indexed 42 days ago

https://github.com/nhairs/python-json-logger

A JSON Log Formatter for the Python Logging Package (version 4.2.0) that enables the production of machine-readable JSON logs. It facilitates easier ingestion into log aggregation tools by providing a JsonFormatter that can be integrated into standard Python logging configurations, including YAML-based fileConfig.

Tokens
8.6K
Snippets
29
Records
48
Agent score
55%

What's inside python-json-logger

  1. Follow naming conventions for modules, classes, and constants

    main

    Adhere to the following naming patterns:

    • Modules: lowercase_with_underscores.py
    • Packages: lowercase
    • Classes & Type Aliases: CapWords (e.g., BaseJsonFormatter)
    • Constants: UPPERCASE_WITH_UNDERSCORES (e.g., RESERVED_ATTRS)
    • Functions, methods, and variables: Standard PEP 8 lowercase_with_underscores.
  2. Versioning and compatibility policy

    main

    The project follows semantic versioning and prioritizes backwards compatibility.

    Compatibility Rules:

    • Python Versions: Feature changes must be compatible with all security-supported Python versions. They should also be compatible with unsupported Python versions if recent downloads for those versions exceed 5% of total downloads.
    • Library Versions: Generally, only the latest major.minor version of python-json-logger is supported. Backporting bug fixes or features to older version branches requires prior discussion with maintainers.
  3. Inject Request or Trace IDs into logs

    main

    There are three primary ways to add contextual information like Request IDs to your logs:

    1. Using the extra argument: Pass the ID directly into every log call. This is explicit but requires manual effort at every call site.
    2. Using a logging.Filter: Create a filter that attaches the ID to the LogRecord object. This is highly compatible with standard Python logging machinery and works automatically for all logs handled by the logger.
    3. Overriding process_log_record: Create a custom formatter that injects the ID into the data dictionary. This modifies the JSON output without altering the original LogRecord object.
    # Solution 1: Using extra
    logger.info("message", extra={"request_id": request_id})
    
    # Solution 2: Using a Filter
    class RequestIdFilter(logging.Filter):
        def filter(self, record):
            record.request_id = get_request_id()
            return True
    
    logger.addFilter(RequestIdFilter())
    
    # Solution 3: Using process_log_record
    class MyFormatter(JsonFormatter):
        def process_log_record(self, log_data):
            log_data["request_id"] = get_request_id()
            return log_data
  4. Requirements for adding a new encoder

    main

    New encoders are only considered if they meet high popularity thresholds. To be considered, an encoder must be:

    • In the top 1000 packages on PyPI,
    • OR have monthly downloads exceeding 10M/month.

    Process: You MUST open an issue for discussion before creating a pull request to add a new encoder. For implementation reference, see orjson.py and msgspec.py in the source code.

  5. Configure formatting and linting tools

    main

    The project uses automated tools to enforce code style and catch errors. Ensure these tools are run before committing code. Configuration for these tools is located in the following files:

    • Black: Opinionated code formatting (configured in pyproject.toml).
    • Pylint: Static code analysis and error detection (configured in pylintrc).
    • MyPy: Static type checking (configured in mypy.ini).
  6. Install and use Python JSON Logger

    main
    Python JSON Logger allows you to produce machine-readable JSON logs when using Python's standard logging package. This makes logs significantly easier to parse and ingest into log aggregation tools. To get started, you can install the package via PyPI and integrate it into your existing logging configuration.
  7. Include all standard library log record fields

    main

    By default, JsonFormatter only includes fields explicitly defined in the format string. To include all standard library LogRecord attributes (like lineno, funcName, etc.) without manually listing them in the format string, set reserved_attrs to an empty list. This makes the inclusion of these fields implicit and avoids version-specific manual configuration.

    all_fields_formatter = JsonFormatter(reserved_attrs=[])
  8. Format, lint, and test code before submitting

    main

    Before submitting a pull request, you must ensure your code adheres to the project's style and passes all quality checks. Run the following commands in order:

    1. Format: Use black on the src and tests directories.
    2. Lint: Use pylint (with colorized output) on src and mypy on src and tests.
    3. Test: Run the test suite using pytest.
    # Format
    black src tests
    
    # Lint
    pylint --output-format=colorized src
    mypy src tests
    
    # Tests
    pytest
  9. Install python-json-logger

    main

    You can install the library via pip or directly from GitHub releases.

    Note: This fork uses version >=3.0.0. To use pre-fork versions, specify python-json-logger<3.

    # Install via pip
    pip install python-json-logger
    
    # Install a specific wheel from GitHub releases
    pip install 'python-json-logger@https://github.com/nhairs/python-json-logger/releases/download/v3.0.0/python_json_logger-3.0.0-py3-none-any.whl'
  10. Integrate JsonFormatter with Python's logging framework

    main

    To produce JSON output, create an instance of JsonFormatter and attach it to a logging handler (e.g., StreamHandler).

    import logging
    from pythonjsonlogger.json import JsonFormatter
    
    logger = logging.getLogger()
    
    logHandler = logging.StreamHandler()
    formatter = JsonFormatter()
    logHandler.setFormatter(formatter)
    logger.addHandler(logHandler)
  11. Use explicit return statements

    main

    All functions and methods must have an explicit return statement. If a function does not logically return a value, end it with return None or simply return to make intent clear.

    def process_data(data: dict) -> None:
        """Processes the given data."""
        # ... processing logic ...
        print(data)
        return # or return None