Coverage for eminus/logger.py: 95.65%
46 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-18 08:43 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-18 08:43 +0000
1# SPDX-FileCopyrightText: 2021 The eminus developers
2# SPDX-License-Identifier: Apache-2.0
3"""Logger initialization and configuration."""
5import logging
6import numbers
7import sys
10class CustomLogger(logging.Logger):
11 """Custom logger for the usage outside of classes.
13 This is just a basic logger but with an added verbose property.
15 Args:
16 name: Logger name.
17 """
19 def __init__(self, name):
20 """Initialize the CustomLogger object."""
21 super().__init__(name)
23 @property
24 def verbose(self):
25 """Verbosity level."""
26 return self._verbose
28 @verbose.setter
29 def verbose(self, level):
30 self._verbose = get_level(level)
31 self.setLevel(self._verbose)
34class CustomFormatter(logging.Formatter):
35 """Custom logger formatter."""
37 def format(self, record):
38 """Use different formatting for different logging levels.
40 Args:
41 record: LogRecord object.
43 Returns:
44 Formatted log text.
45 """
46 if record.levelno >= logging.WARNING:
47 # Print the level name for errors and warnings
48 self._style._fmt = "%(levelname)s: %(msg)s"
49 else:
50 # But not for info and debug messages
51 self._style._fmt = "%(msg)s"
52 return super().format(record)
55# The following code is not guarded by a function because it has to be run once the logger is called
56# to set up the basic logger configuration
58# Create a base logger that can be used outside of classes
59logging.setLoggerClass(CustomLogger)
60#: Global logging object.
61log = logging.getLogger("eminus")
63# Basic logger setup
64__formatter = CustomFormatter()
65__handler = logging.StreamHandler(sys.stdout)
66__handler.setFormatter(__formatter)
67logging.root.addHandler(__handler)
70def create_logger(obj):
71 """Create a logger unique to an object.
73 Args:
74 obj: Instance of a class.
76 Returns:
77 Logger object.
78 """
79 # Use the ID of objects to create a unique logger
80 # Without this setting the verbosity in one instance would affect other instances
81 local_log = logging.getLogger(str(id(obj)))
82 local_log.verbose = log.verbose
83 return local_log
86def get_level(verbose):
87 """Validate logging levels.
89 Args:
90 verbose: Level of output.
92 Returns:
93 Logging level.
94 """
95 log_levels = {
96 0: "CRITICAL",
97 1: "ERROR",
98 2: "WARNING",
99 3: "INFO",
100 4: "DEBUG",
101 }
102 # Use the global logging level for None
103 if verbose is None:
104 level = log.verbose
105 # Fall back to DEBUG if the level is not available
106 elif isinstance(verbose, numbers.Number):
107 level = log_levels.get(verbose, "DEBUG")
108 else:
109 level = verbose
110 level = level.upper()
111 if level not in log_levels.values():
112 msg = f"{level} is no recognized logging level."
113 raise ValueError(msg)
114 return level
117def name(newname):
118 """Add a name to functions without evaluating them for better logging.
120 Args:
121 newname: Function name.
123 Returns:
124 Decorator.
125 """
127 def decorator(f):
128 f.__name__ = newname
129 return f
131 return decorator