File size: 3,700 Bytes
75309ed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File: logging_utils.py

import logging
import colorlog
from functools import wraps
import time
from typing import Callable, Any, Union
import json

def setup_logging(level=logging.INFO, log_file=None):
    """
    Set up logging configuration with colored output and improved formatting.
    
    Args:
    level (int): The logging level (e.g., logging.DEBUG, logging.INFO)
    log_file (str, optional): Path to a log file. If None, log to console only.
    """
    formatter = colorlog.ColoredFormatter(
        "%(log_color)s%(levelname)-8s%(reset)s %(blue)s%(asctime)s%(reset)s - %(message)s",
        datefmt="%Y-%m-%d %H:%M:%S",
        reset=True,
        log_colors={
            'DEBUG':    'cyan',
            'INFO':     'green',
            'WARNING':  'yellow',
            'ERROR':    'red',
            'CRITICAL': 'red,bg_white',
        },
        secondary_log_colors={},
        style='%'
    )

    console_handler = colorlog.StreamHandler()
    console_handler.setFormatter(formatter)

    logger = colorlog.getLogger()
    logger.setLevel(level)
    logger.addHandler(console_handler)

    if log_file:
        file_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
        file_handler = logging.FileHandler(log_file)
        file_handler.setFormatter(file_formatter)
        logger.addHandler(file_handler)

def format_dict(d, indent=0):
    """Format a dictionary for pretty printing."""
    return '\n'.join(f"{'  ' * indent}{k}: {format_dict(v, indent+1) if isinstance(v, dict) else v}" for k, v in d.items())

def log_function(logger: logging.Logger):
    """
    A decorator that logs function entry, exit, arguments, and execution time with improved formatting.
    
    Args:
    logger (logging.Logger): The logger to use for logging.
    """
    def decorator(func: Callable) -> Callable:
        @wraps(func)
        def wrapper(*args: Any, **kwargs: Any) -> Any:
            func_name = func.__name__
            logger.info(f"{'=' * 40}")
            logger.info(f"Starting: {func_name}")
            
            # Log arguments in a more readable format
            if args or kwargs:
                logger.debug("Arguments:")
                if args:
                    for i, arg in enumerate(args):
                        if isinstance(arg, dict):
                            logger.debug(f"  arg{i}:\n{format_dict(arg, 2)}")
                        else:
                            logger.debug(f"  arg{i}: {arg}")
                if kwargs:
                    for key, value in kwargs.items():
                        if isinstance(value, dict):
                            logger.debug(f"  {key}:\n{format_dict(value, 2)}")
                        else:
                            logger.debug(f"  {key}: {value}")
            
            start_time = time.time()
            try:
                result = func(*args, **kwargs)
                logger.info(f"Completed: {func_name}")
                
                # Log the result
                if result:
                    if isinstance(result, dict):
                        logger.info(f"Output:\n{format_dict(result, 1)}")
                    else:
                        logger.info(f"Output: {result}")
                
                return result
            except Exception as e:
                logger.exception(f"Exception in {func_name}:")
                logger.exception(f"  {str(e)}")
                raise
            finally:
                duration = time.time() - start_time
                logger.debug(f"Execution time: {duration:.2f} seconds")
                logger.info(f"{'=' * 40}\n")
        
        return wrapper
    return decorator