File size: 3,392 Bytes
dbd79bd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# - x - x - x - x - x - x - x - x - x - x - x - x - x - x - #
#                                                           #
#   This file was created by: Alberto Palomo Alonso         #
# Universidad de Alcalá - Escuela Politécnica Superior      #
#                                                           #
# - x - x - x - x - x - x - x - x - x - x - x - x - x - x - #
# Import statements:
import logging
import os


def get_logger(log_path: str, level: int | str = logging.INFO) -> logging.Logger:
    """
    Sets up a logger for debugging with colored output to the console and output to a specified log file.
    Creates the directory if it does not exist.

    Args:
        log_path (str): The file path where the log file 'logfile.log' will be stored.
        level (int | str): The logging level to be printed on the logger.

    Raises:
        ValueError: If the log_path is not valid.
    """
    # Check if log_path exists, create it if not
    if not os.path.exists(log_path):
        os.makedirs(log_path, exist_ok=True)
    elif not os.path.isdir(log_path):
        raise ValueError(f"Provided path '{log_path}' is not a directory.")

    full_log_path = os.path.join(log_path, 'logfile.log')

    # Transform level:
    if isinstance(level, str):
        level = level.upper()
        if hasattr(logging, level):
            level = getattr(logging, level)
        else:
            raise ValueError(f'The provided level for the logger <<{level}>> is not a valid level for logging.')
    elif not isinstance(level, int):
        raise ValueError(f'The provided level for the logger <<{level}>> is not a string or int, '
                         f'the given type is <<{type(level)}>>.')

    # Create a logger object
    logger = logging.getLogger(__name__)
    logger.handlers.clear()  # Avoid duplicates
    logger.setLevel(level)  # Set the logging level to the given level
    logger.propagate = False  # Prevent duplication in logging output

    # Create file handler which logs even debug messages
    fh = logging.FileHandler(full_log_path)
    fh.setLevel(level)
    fh.setFormatter(logging.Formatter('%(asctime)s: [%(levelname)s] %(message)s'))

    # Create console handler with a colored formatter
    ch = logging.StreamHandler()
    ch.setLevel(level)
    ch.setFormatter(ColoredFormatter())

    # Add handlers to the logger
    logger.addHandler(fh)
    logger.addHandler(ch)

    logger.info(f'Logger initialized with writer handler at: {full_log_path}')

    return logger


class ColoredFormatter(logging.Formatter):
    grey = "\x1b[38;20m"
    blue = "\x1b[34;20m"
    cyan = "\x1b[36;20m"
    orange = "\x1b[33;20m"
    red = "\x1b[31;20m"
    reset = "\x1b[0m"
    format = '%(asctime)s: [%(levelname)s] %(message)s'

    FORMATS = {
        logging.DEBUG: blue + format + reset,
        logging.INFO: cyan + format + reset,
        logging.WARNING: orange + format + reset,
        logging.ERROR: red + format + reset,
        logging.CRITICAL: red + format + reset
    }

    def format(self, record):
        log_fmt = self.FORMATS.get(record.levelno)
        formatter = logging.Formatter(log_fmt, "%Y-%m-%d %H:%M:%S")
        return formatter.format(record)
# - x - x - x - x - x - x - x - x - x - x - x - x - x - x - #
#                        END OF FILE                        #
# - x - x - x - x - x - x - x - x - x - x - x - x - x - x - #