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"