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