Logging

The logging module provides advanced logging capabilities with support for console, file, and JSON logging formats. It includes color formatting for console output and customizable JSON fields for structured logging.

Features

  • Console logging with optional color formatting

  • File logging with multiple format options

  • JSON logging with customizable fields

  • Automatic log directory creation

  • Configurable date and message formats

Best Practices

Centralized Logger Configuration

The recommended approach is to create a dedicated script for logger configuration (e.g., logger.py) and import it across other modules. This ensures consistent logging behavior throughout your application by having a single source of truth for logger setup.

Creating the Logger Script

Create a script named logger.py in your source directory to configure and return the application’s central logger:

# Configuration for the application logger
from mango.logging import get_configured_logger

# Define log paths and create logger instance
logger = get_configured_logger(
    logger_type="my_app",
    log_console_level=logging.INFO,
    mango_color=True,
    log_file_path="logs/app.log",
    log_file_level=logging.DEBUG,
)

Using the Logger in Scripts

In any script where you need logging, simply import the logger from logger.py:

# Import the configured logger from the central configuration
from logger import logger

def main():
    # Log the start of script execution
    logger.info("Script started")
    value = 42
    # Example using f-string for logging
    logger.debug(f"Processing value: {value}")
    # Your code here

if __name__ == "__main__":
    main()

This approach: - Centralizes logger configuration in a single script - Ensures consistent logger setup across all modules - Simplifies logger usage by importing from logger.py - Avoids redundant logger configuration code

Logging Guidelines

  • Use appropriate log levels:
    • DEBUG (10): Detailed information for debugging

    • INFO (20): General operational events

    • WARNING (30): Unexpected but handled situations

    • ERROR (40): Errors that prevent normal operation

    • CRITICAL (50): Critical issues requiring immediate attention

  • Include contextual information in log messages

  • Use structured logging (JSON) for machine parsing

  • Avoid logging sensitive information

Basic Usage

Here’s a simple example of using the logger:

from mango.logging import get_configured_logger
import logging

# Basic console logger with colors
logger = get_configured_logger(
    log_console_level=logging.DEBUG,
    mango_color=True
)

logger.debug("Debug message")
logger.info("Info message")
logger.warning("Warning message")
logger.error("Error message")

File Logging

To log to both console and file:

logger = get_configured_logger(
    log_console_level=logging.INFO,
    log_file_path="logs/app.log",
    log_file_level=logging.DEBUG,
    log_file_format="%(asctime)s | %(levelname)s || %(name)s: %(message)s",
    log_file_datefmt="%Y%m%d %H:%M:%S"
)

logger.info("This goes to both console and file")
logger.debug("This only goes to file")

JSON Logging

For structured logging in JSON format:

logger = get_configured_logger(
    log_file_path="logs/app.json",
    json_fields=["level", "message", "time", "module"]
)

logger.info("This will be logged in JSON format")

API Reference

get_configured_logger

mango.logging.logger.get_configured_logger(logger_type: str = 'mango_logging', config_dict: Dict | None = None, log_console_level: int | None = None, log_console_format: str | None = None, log_console_datefmt: str | None = None, mango_color: bool = False, log_file_path: str | None = None, log_file_mode: str = 'a', log_file_level: int | None = None, log_file_format: str | None = None, log_file_datefmt: str | None = None, json_fields: List[str] | None = None) Logger

Configure and return a logger with flexible logging settings.

Provides a comprehensive way to configure logging with support for console and file output, including advanced features like JSON formatting and colored console logs. Supports both root logger and named loggers with extensive customization options.

Parameters:
  • logger_type (str) – The type of logger to configure (default: “mango_logging”)

  • config_dict (Optional[Dict]) – Custom configuration dictionary for advanced setups

  • log_console_level (Optional[int]) – Console logging level (e.g., logging.INFO)

  • log_console_format (Optional[str]) – Console log message format string

  • log_console_datefmt (Optional[str]) – Console date format string

  • mango_color (bool) – Enable colored console output

  • log_file_path (Optional[str]) – Path to log file (supports .txt, .json, .log extensions)

  • log_file_mode (str) – File open mode (‘a’ for append, ‘w’ for overwrite)

  • log_file_level (Optional[int]) – File logging level (e.g., logging.DEBUG)

  • log_file_format (Optional[str]) – File log message format string

  • log_file_datefmt (Optional[str]) – File date format string

  • json_fields (Optional[List[str]]) – Fields to include in JSON output. Available options: - level: Log level name (e.g., INFO, DEBUG) (default) - message: Log message (default) - time: Timestamp of the log record (default) - name: Name of the logger (default) - module: Module name where the log was generated (default) - filename: Filename where the log was generated (default) - lineno: Line number in the source code where the log was generated (default) - funcName: Function name where the log was generated - pathname: Full pathname of the source file where the log was generated - process: Process ID of the process where the log was generated - processName: Process name where the log was generated - thread: Thread ID of the thread where the log was generated - threadName: Thread name where the log was generated

Returns:

Configured logger instance

Return type:

logging.Logger

Raises:

ValueError – If log file extension is invalid or logger type not found

Example:
>>> # Basic colored console logger
>>> logger = get_configured_logger(mango_color=True)
>>>
>>> # Logger with file output
>>> logger = get_configured_logger(
...     log_file_path="logs/app.log",
...     log_console_level=logging.DEBUG
... )
>>>
>>> # JSON logger
>>> logger = get_configured_logger(
...     log_file_path="logs/app.json",
...     json_fields=['level', 'message', 'time']
... )

ColorFormatter

class mango.logging.logger.ColorFormatter(fmt: str | None = None, datefmt: str | None = None, style: str = '%', format: str | None = None)

Enhanced log formatter with color support for console output.

This formatter applies different colors to log messages based on their severity level, making it easier to distinguish between different types of log entries in console output. Colors are automatically reset after each message to prevent color bleeding.

Variables:
  • _ESCAPE_CODES (Dict[str, str]) – Terminal color escape codes for text formatting

  • _LEVEL_COLORS (Dict[int, str]) – Mapping of log levels to their corresponding colors

Example:
>>> formatter = ColorFormatter()
>>> handler = logging.StreamHandler()
>>> handler.setFormatter(formatter)
>>> logger.addHandler(handler)
format(record: LogRecord) str

Format the log record with appropriate color based on log level.

Applies color formatting to the log message based on the record’s log level. The color is automatically reset after the message to prevent color bleeding in subsequent output.

Parameters:

record (logging.LogRecord) – The log record to format

Returns:

The formatted log message with color escape codes

Return type:

str

JSONFormatter

class mango.logging.logger.JSONFormatter(fields: List[str] | None = None, datefmt: str | None = None)

Formatter that outputs log records as structured JSON.

This formatter creates structured log output in JSON format with customizable fields. It maintains a list of log entries that can be serialized as a JSON array, making it suitable for log aggregation systems and structured logging applications.

Variables:
  • fields (List[str]) – List of fields to include in JSON output

  • datefmt (str) – Date format string for timestamp formatting

  • _log_entries (List[Dict]) – Internal list of formatted log entries

Available fields:
  • level: Log level name (e.g., INFO, DEBUG) (default)

  • message: Log message (default)

  • time: Timestamp of the log record (default)

  • name: Name of the logger (default)

  • module: Module name where the log was generated (default)

  • filename: Filename where the log was generated (default)

  • lineno: Line number in the source code where the log was generated (default)

  • funcName: Function name where the log was generated

  • pathname: Full pathname of the source file where the log was generated

  • process: Process ID of the process where the log was generated

  • processName: Process name where the log was generated

  • thread: Thread ID of the thread where the log was generated

  • threadName: Thread name where the log was generated

Example:
>>> formatter = JSONFormatter(fields=['level', 'message', 'time'])
>>> handler = logging.FileHandler('app.json')
>>> handler.setFormatter(formatter)
>>> logger.addHandler(handler)
format(record: LogRecord) str

Format the log record as a JSON object and add to entries list.

Processes the log record and extracts the specified fields into a JSON object. The object is added to the internal entries list and the complete list is returned as a JSON string.

Parameters:

record (logging.LogRecord) – The log record to format

Returns:

JSON string containing all formatted log entries

Return type:

str

JSONFileHandler

class mango.logging.logger.JSONFileHandler(filename: str, mode: str = 'w', encoding: str | None = None, delay: bool = False)

File handler that overwrites the file with each log entry for valid JSON.

This handler is specifically designed for JSON logging, ensuring that the output file always contains valid JSON by overwriting it completely with each update. This prevents malformed JSON files that could occur with standard file handlers when the application terminates unexpectedly.

Example:
>>> handler = JSONFileHandler('logs.json')
>>> formatter = JSONFormatter()
>>> handler.setFormatter(formatter)
>>> logger.addHandler(handler)
emit(record: LogRecord) None

Write the formatted log record to file, overwriting previous content.

Formats the log record and writes the complete JSON array to the file, overwriting any previous content to ensure valid JSON format.

Parameters:

record (logging.LogRecord) – The log record to write

Raises:

Exception – If file writing fails, handled by parent class

Deprecated Functions

get_basic_logger

mango.logging.logger.get_basic_logger(log_file: str | None = None, console: bool = True, level: int = 20, format_str: str = '%(asctime)s | %(levelname)s | %(name)s: %(message)s', datefmt: str = '%Y-%m-%d') Logger

Get a basic logger with console and/or file output (Deprecated).

This function is deprecated and will be removed in a future version. Use get_configured_logger instead for more advanced configuration options.

Parameters:
  • log_file (Optional[str]) – Path to the log file

  • console (bool) – Enable console output

  • level (int) – Logging level (e.g., logging.INFO)

  • format_str (str) – Log message format string

  • datefmt (str) – Date format string

Returns:

Configured logger instance

Return type:

logging.Logger

Deprecated:

Version 0.4.0: Use get_configured_logger instead

Raises:

DeprecationWarning – Always raises deprecation warning

Caution

The get_basic_logger function is deprecated and will be removed in a future version. Use get_configured_logger instead.

Chrono

class mango.logging.chrono.Chrono(name: str, silent: bool = False, precision: int = 2, logger: str = 'root')

High-precision timing utility for measuring operation durations.

This class provides functionality to measure and track execution times for multiple named operations. It supports individual timing, batch operations, and can be used as a decorator for automatic function timing. All timing information is logged using the configured logger.

Parameters:
  • name (str) – Name of the initial chronometer to create

  • silent (bool) – Whether to suppress automatic logging of timing events

  • precision (int) – Number of decimal places for time reporting

  • logger (str) – Name of the logger to use for output

Example:
>>> chrono = Chrono("operation1")
>>> chrono.start("data_processing")
>>> # ... perform operation ...
>>> duration = chrono.stop("data_processing")
>>> print(f"Operation took {duration:.2f} seconds")
new(name: str)

Create a new chronometer entry without starting it.

Initializes a new chronometer with the specified name but does not start timing. Use start() to begin timing this chronometer.

Parameters:

name (str) – Name of the new chronometer to create

Returns:

None

Example:
>>> chrono = Chrono("main")
>>> chrono.new("sub_operation")
>>> chrono.start("sub_operation")
start(name, silent=True)

Start timing a chronometer.

Begins timing the specified chronometer. If the chronometer doesn’t exist, it will be created first. Optionally logs the start event.

Parameters:
  • name (str) – Name of the chronometer to start

  • silent (bool) – Whether to suppress the start log message

Returns:

None

Example:
>>> chrono = Chrono("main")
>>> chrono.start("data_processing", silent=False)
stop(name: str, report: bool = True)

Stop timing a chronometer and return the elapsed duration.

Stops the specified chronometer and calculates the elapsed time. Optionally logs the completion with the duration.

Parameters:
  • name (str) – Name of the chronometer to stop

  • report (bool) – Whether to log the completion message

Returns:

Elapsed time in seconds

Return type:

float

Raises:

KeyError – If the chronometer name doesn’t exist

Example:
>>> chrono = Chrono("main")
>>> chrono.start("operation")
>>> # ... perform operation ...
>>> duration = chrono.stop("operation")
>>> print(f"Completed in {duration:.3f} seconds")
stop_all(report: bool = True)

Stop all running chronometers and return their durations.

Stops all chronometers that are currently running and returns a dictionary mapping chronometer names to their elapsed durations. Only chronometers that haven’t been stopped yet are affected.

Parameters:

report (bool) – Whether to log completion messages for each chronometer

Returns:

Dictionary mapping chronometer names to durations in seconds

Return type:

dict

Example:
>>> chrono = Chrono("main")
>>> chrono.start("op1")
>>> chrono.start("op2")
>>> durations = chrono.stop_all()
>>> print(f"Total operations: {len(durations)}")
report(name: str, message: str = None)

Report the current status and duration of a chronometer.

Logs the current timing information for the specified chronometer. If the chronometer has been stopped, reports the final duration. If still running, reports the current elapsed time.

Parameters:
  • name (str) – Name of the chronometer to report

  • message (str, optional) – Optional additional message to include in the log

Returns:

Elapsed time in seconds (final duration if stopped, current if running)

Return type:

float

Raises:

KeyError – If the chronometer name doesn’t exist

Example:
>>> chrono = Chrono("main")
>>> chrono.start("long_operation")
>>> # ... some time later ...
>>> elapsed = chrono.report("long_operation", "Still processing...")
report_all()

Report the status and durations of all chronometers.

Logs timing information for all chronometers and returns a dictionary with their current durations. For stopped chronometers, reports final duration; for running ones, reports current elapsed time.

Returns:

Dictionary mapping chronometer names to durations in seconds

Return type:

dict

Example:
>>> chrono = Chrono("main")
>>> chrono.start("op1")
>>> chrono.start("op2")
>>> chrono.stop("op1")
>>> durations = chrono.report_all()
>>> # Reports final duration for op1, current elapsed for op2

Time decorator

mango.logging.decorators.log_time(logger: str = 'root', level: int | str = 'INFO')

Decorator to automatically log function execution time.

Creates a decorator that measures and logs the execution time of the decorated function. The timing information is logged using the specified logger and log level.

Parameters:
  • logger (str) – Name of the logger to use for timing messages

  • level (Union[int, str]) – Logging level for timing messages (default: “INFO”)

Returns:

Decorator function that logs execution time

Return type:

callable

Example:
>>> from mango.logging.decorators import log_time
>>>
>>> @log_time("my_logger", "DEBUG")
>>> def slow_function():
...     time.sleep(1)
...     return "done"
>>>
>>> result = slow_function()  # Logs: "slow_function took 1.00 seconds"