Coverage for src/lib.mys : 100%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1func get_logger(name: string) -> Logger:
2 """Get the logger with given name. Creates a new logger if it does not
3 already exist. The global variable LOGGERS contains all loggers
4 created by this function.
6 """
8 logger = LOGGERS.get(name, None)
10 if logger is None:
11 logger = Logger(name, Level.Warning)
12 LOGGERS[name] = logger
14 return logger
16class Logger:
17 """A logger.
19 This class is normally not instantiated directly by the
20 user. Instead, use `get_logger()` to create instances.
22 """
24 _name: string
25 level: Level
27 func __init__(self, name: string, level: Level = Level.Warning):
28 """Initialize the logger object with given name and level.
30 """
32 self._name = name
33 self.level = level
35 func is_enabled_for(self, level: Level) -> bool:
36 """Returns True is given level is enabled, otherwise False.
38 """
40 return level >= self.level
42 macro ERROR(self, message: string):
43 """Log given error message.
45 """
47 self._LOG(message, Level.Error)
49 macro WARNING(self, message: string):
50 """Log given warning message.
52 """
54 self._LOG(message, Level.Warning)
56 macro INFO(self, message: string):
57 """Log given info message.
59 """
61 self._LOG(message, Level.Info)
63 macro DEBUG(self, message: string):
64 """Log given debug message.
66 """
68 self._LOG(message, Level.Debug)
70 macro _LOG(self, message: string, level: Level):
71 if self.is_enabled_for(level):
72 HANDLER.write(FORMATTER.format(self._name, level, message))
74LOGGERS: {string: Logger} = {}
75"""A dictionary of all known loggers.
77"""
79HANDLER: Handler = StdoutHandler()
80"""The handler which all log messages are written to.
82"""
84FORMATTER: Formatter = DefaultFormatter()
85"""The formatter that formats all messages.
87"""
89enum Level:
90 """Logging levels.
92 """
94 Debug = 0
95 Info = 1
96 Warning = 2
97 Error = 3
99# Use Enum values later?
100_LEVEL_STRINGS: [string] = [
101 "DEBUG",
102 "INFO",
103 "WARNING",
104 "ERROR"
105]
107trait Handler:
108 """Log entry output handler trait.
110 """
112 func write(self, message: string):
113 """Write given message to desired location.
115 """
117class StdoutHandler(Handler):
118 """Writes log messages to standard output.
120 """
122 func write(self, message: string):
123 """Writes given log message to standard output.
125 """
127 print(message)
129trait Formatter:
130 """Log entry formatter trait.
132 """
134 func format(self, logger_name: string, level: Level, message: string) -> string:
135 """Returns a formatted log message.
137 """
139class DefaultFormatter(Formatter):
140 """Default log entry formatter.
142 """
144 func format(self, logger_name: string, level: Level, message: string) -> string:
145 """Formats a log message on the format <logger name> <level> <message>
146 and returns it.
148 """
150 return f"{logger_name} {_LEVEL_STRINGS[i64(level)]} {message}"
152test default_handler():
153 logger = Logger("my-logger")
155 logger.ERROR(f"Message: {1 + 1}")
156 logger.WARNING(f"Message: {2 + 2}")
157 logger.INFO(f"Message: {3 + 3}")
158 logger.DEBUG(f"Message: {4 + 4}")
160class _BufferHandler(Handler):
161 entries: [string]
163 func write(self, message: string):
164 self.entries.append(message)
166func _setup_buffer_handler() -> (_BufferHandler, Handler):
167 default_handler = HANDLER
168 handler = _BufferHandler([])
169 HANDLER = handler
171 return handler, default_handler
173test buffer_handler():
174 handler, default_handler = _setup_buffer_handler()
175 logger = Logger("my-logger", Level.Info)
177 logger.INFO("Message 1")
178 logger.DEBUG("Message 2")
179 logger.INFO("Message 3")
181 assert handler.entries == ["my-logger INFO Message 1",
182 "my-logger INFO Message 3"]
184 HANDLER = default_handler
186class _CounterFormatter(Formatter):
187 count: i64
189 func format(self, logger_name: string, level: Level, message: string) -> string:
190 self.count += 1
192 return f"{self.count}: {message}"
194func _setup_counter_formatter() -> (_CounterFormatter, Formatter):
195 default_formatter = FORMATTER
196 formatter = _CounterFormatter(0)
197 FORMATTER = formatter
199 return formatter, default_formatter
201test my_formatter():
202 handler, default_handler = _setup_buffer_handler()
203 formatter, default_formatter = _setup_counter_formatter()
204 logger = Logger("my-logger")
206 logger.WARNING("Message a")
207 logger.ERROR("Message b")
209 assert handler.entries == ["1: Message a",
210 "2: Message b"]
212 HANDLER = default_handler
213 FORMATTER = default_formatter
215test levels():
216 handler, default_handler = _setup_buffer_handler()
217 logger = Logger("my-logger")
219 # Debug.
220 logger.level = Level.Debug
221 handler.entries = []
223 logger.DEBUG("1")
224 logger.INFO("2")
225 logger.WARNING("3")
226 logger.ERROR("4")
228 assert handler.entries == ["my-logger DEBUG 1",
229 "my-logger INFO 2",
230 "my-logger WARNING 3",
231 "my-logger ERROR 4"]
233 assert logger.is_enabled_for(Level.Debug)
234 assert logger.is_enabled_for(Level.Info)
235 assert logger.is_enabled_for(Level.Warning)
236 assert logger.is_enabled_for(Level.Error)
238 # Info.
239 logger.level = Level.Info
240 handler.entries = []
242 logger.DEBUG("5")
243 logger.INFO("6")
244 logger.WARNING("7")
245 logger.ERROR("8")
247 assert handler.entries == ["my-logger INFO 6",
248 "my-logger WARNING 7",
249 "my-logger ERROR 8"]
251 assert not logger.is_enabled_for(Level.Debug)
252 assert logger.is_enabled_for(Level.Info)
253 assert logger.is_enabled_for(Level.Warning)
254 assert logger.is_enabled_for(Level.Error)
256 # Warning.
257 logger.level = Level.Warning
258 handler.entries = []
260 logger.DEBUG("9")
261 logger.INFO("10")
262 logger.WARNING("11")
263 logger.ERROR("12")
265 assert handler.entries == ["my-logger WARNING 11",
266 "my-logger ERROR 12"]
268 assert not logger.is_enabled_for(Level.Debug)
269 assert not logger.is_enabled_for(Level.Info)
270 assert logger.is_enabled_for(Level.Warning)
271 assert logger.is_enabled_for(Level.Error)
273 # Error.
274 logger.level = Level.Error
275 handler.entries = []
277 logger.DEBUG("13")
278 logger.INFO("14")
279 logger.WARNING("15")
280 logger.ERROR("16")
282 assert handler.entries == ["my-logger ERROR 16"]
284 assert not logger.is_enabled_for(Level.Debug)
285 assert not logger.is_enabled_for(Level.Info)
286 assert not logger.is_enabled_for(Level.Warning)
287 assert logger.is_enabled_for(Level.Error)
289 HANDLER = default_handler
291test get_logger():
292 LOGGERS = {}
294 foo = get_logger("foo")
295 bar = get_logger("bar")
297 assert foo is not bar
298 assert foo is get_logger("foo")
300 assert LOGGERS["foo"] is foo
301 assert LOGGERS["bar"] is bar
303 LOGGERS = {}