Hide keyboard shortcuts

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. 

5 

6 """ 

7 

8 logger = LOGGERS.get(name, None) 

9 

10 if logger is None: 

11 logger = Logger(name, Level.Warning) 

12 LOGGERS[name] = logger 

13 

14 return logger 

15 

16class Logger: 

17 """A logger. 

18 

19 This class is normally not instantiated directly by the 

20 user. Instead, use `get_logger()` to create instances. 

21 

22 """ 

23 

24 _name: string 

25 level: Level 

26 

27 func __init__(self, name: string, level: Level = Level.Warning): 

28 """Initialize the logger object with given name and level. 

29 

30 """ 

31 

32 self._name = name 

33 self.level = level 

34 

35 func is_enabled_for(self, level: Level) -> bool: 

36 """Returns True is given level is enabled, otherwise False. 

37 

38 """ 

39 

40 return level >= self.level 

41 

42 macro ERROR(self, message: string): 

43 """Log given error message. 

44 

45 """ 

46 

47 self._LOG(message, Level.Error) 

48 

49 macro WARNING(self, message: string): 

50 """Log given warning message. 

51 

52 """ 

53 

54 self._LOG(message, Level.Warning) 

55 

56 macro INFO(self, message: string): 

57 """Log given info message. 

58 

59 """ 

60 

61 self._LOG(message, Level.Info) 

62 

63 macro DEBUG(self, message: string): 

64 """Log given debug message. 

65 

66 """ 

67 

68 self._LOG(message, Level.Debug) 

69 

70 macro _LOG(self, message: string, level: Level): 

71 if self.is_enabled_for(level): 

72 HANDLER.write(FORMATTER.format(self._name, level, message)) 

73 

74LOGGERS: {string: Logger} = {} 

75"""A dictionary of all known loggers. 

76 

77""" 

78 

79HANDLER: Handler = StdoutHandler() 

80"""The handler which all log messages are written to. 

81 

82""" 

83 

84FORMATTER: Formatter = DefaultFormatter() 

85"""The formatter that formats all messages. 

86 

87""" 

88 

89enum Level: 

90 """Logging levels. 

91 

92 """ 

93 

94 Debug = 0 

95 Info = 1 

96 Warning = 2 

97 Error = 3 

98 

99# Use Enum values later? 

100_LEVEL_STRINGS: [string] = [ 

101 "DEBUG", 

102 "INFO", 

103 "WARNING", 

104 "ERROR" 

105] 

106 

107trait Handler: 

108 """Log entry output handler trait. 

109 

110 """ 

111 

112 func write(self, message: string): 

113 """Write given message to desired location. 

114 

115 """ 

116 

117class StdoutHandler(Handler): 

118 """Writes log messages to standard output. 

119 

120 """ 

121 

122 func write(self, message: string): 

123 """Writes given log message to standard output. 

124 

125 """ 

126 

127 print(message) 

128 

129trait Formatter: 

130 """Log entry formatter trait. 

131 

132 """ 

133 

134 func format(self, logger_name: string, level: Level, message: string) -> string: 

135 """Returns a formatted log message. 

136 

137 """ 

138 

139class DefaultFormatter(Formatter): 

140 """Default log entry formatter. 

141 

142 """ 

143 

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. 

147 

148 """ 

149 

150 return f"{logger_name} {_LEVEL_STRINGS[i64(level)]} {message}" 

151 

152test default_handler(): 

153 logger = Logger("my-logger") 

154 

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}") 

159 

160class _BufferHandler(Handler): 

161 entries: [string] 

162 

163 func write(self, message: string): 

164 self.entries.append(message) 

165 

166func _setup_buffer_handler() -> (_BufferHandler, Handler): 

167 default_handler = HANDLER 

168 handler = _BufferHandler([]) 

169 HANDLER = handler 

170 

171 return handler, default_handler 

172 

173test buffer_handler(): 

174 handler, default_handler = _setup_buffer_handler() 

175 logger = Logger("my-logger", Level.Info) 

176 

177 logger.INFO("Message 1") 

178 logger.DEBUG("Message 2") 

179 logger.INFO("Message 3") 

180 

181 assert handler.entries == ["my-logger INFO Message 1", 

182 "my-logger INFO Message 3"] 

183 

184 HANDLER = default_handler 

185 

186class _CounterFormatter(Formatter): 

187 count: i64 

188 

189 func format(self, logger_name: string, level: Level, message: string) -> string: 

190 self.count += 1 

191 

192 return f"{self.count}: {message}" 

193 

194func _setup_counter_formatter() -> (_CounterFormatter, Formatter): 

195 default_formatter = FORMATTER 

196 formatter = _CounterFormatter(0) 

197 FORMATTER = formatter 

198 

199 return formatter, default_formatter 

200 

201test my_formatter(): 

202 handler, default_handler = _setup_buffer_handler() 

203 formatter, default_formatter = _setup_counter_formatter() 

204 logger = Logger("my-logger") 

205 

206 logger.WARNING("Message a") 

207 logger.ERROR("Message b") 

208 

209 assert handler.entries == ["1: Message a", 

210 "2: Message b"] 

211 

212 HANDLER = default_handler 

213 FORMATTER = default_formatter 

214 

215test levels(): 

216 handler, default_handler = _setup_buffer_handler() 

217 logger = Logger("my-logger") 

218 

219 # Debug. 

220 logger.level = Level.Debug 

221 handler.entries = [] 

222 

223 logger.DEBUG("1") 

224 logger.INFO("2") 

225 logger.WARNING("3") 

226 logger.ERROR("4") 

227 

228 assert handler.entries == ["my-logger DEBUG 1", 

229 "my-logger INFO 2", 

230 "my-logger WARNING 3", 

231 "my-logger ERROR 4"] 

232 

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) 

237 

238 # Info. 

239 logger.level = Level.Info 

240 handler.entries = [] 

241 

242 logger.DEBUG("5") 

243 logger.INFO("6") 

244 logger.WARNING("7") 

245 logger.ERROR("8") 

246 

247 assert handler.entries == ["my-logger INFO 6", 

248 "my-logger WARNING 7", 

249 "my-logger ERROR 8"] 

250 

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) 

255 

256 # Warning. 

257 logger.level = Level.Warning 

258 handler.entries = [] 

259 

260 logger.DEBUG("9") 

261 logger.INFO("10") 

262 logger.WARNING("11") 

263 logger.ERROR("12") 

264 

265 assert handler.entries == ["my-logger WARNING 11", 

266 "my-logger ERROR 12"] 

267 

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) 

272 

273 # Error. 

274 logger.level = Level.Error 

275 handler.entries = [] 

276 

277 logger.DEBUG("13") 

278 logger.INFO("14") 

279 logger.WARNING("15") 

280 logger.ERROR("16") 

281 

282 assert handler.entries == ["my-logger ERROR 16"] 

283 

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) 

288 

289 HANDLER = default_handler 

290 

291test get_logger(): 

292 LOGGERS = {} 

293 

294 foo = get_logger("foo") 

295 bar = get_logger("bar") 

296 

297 assert foo is not bar 

298 assert foo is get_logger("foo") 

299 

300 assert LOGGERS["foo"] is foo 

301 assert LOGGERS["bar"] is bar 

302 

303 LOGGERS = {}