5151# pylint:disable=invalid-name
5252
5353import time
54+ import sys
5455
5556__version__ = "0.0.0-auto.0"
5657__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Logger.git"
6364 "WARNING" ,
6465 "ERROR" ,
6566 "CRITICAL" ,
66- "level_for " ,
67- "LoggingHandler " ,
68- "PrintHandler " ,
67+ "_level_for " ,
68+ "Handler " ,
69+ "StreamHandler " ,
6970 "logger_cache" ,
70- "null_logger" ,
7171 "getLogger" ,
7272 "Logger" ,
73- "NullLogger" ,
73+ "NullHandler" ,
74+ "FileHandler" ,
7475]
7576
7677
8788 globals ()[__name ] = __value
8889
8990
90- def level_for (value : int ) -> str :
91+ def _level_for (value : int ) -> str :
9192 """Convert a numeric level to the most appropriate name.
9293
9394 :param int value: a numeric level
@@ -101,62 +102,80 @@ def level_for(value: int) -> str:
101102 return LEVELS [0 ][1 ]
102103
103104
104- class LoggingHandler :
105+ # pylint: disable=too-few-public-methods
106+ class Handler :
105107 """Abstract logging message handler."""
106108
107- def format (self , log_level : int , message : str ) -> str :
109+ def __init__ (self , level = NOTSET ): # pylint: disable=undefined-variable
110+ self .level = level
111+ """Level of the handler; this is currently unused, and
112+ only the level of the logger is used"""
113+
114+ def _format (self , log_level : int , message : str ) -> str :
108115 """Generate a timestamped message.
109116
110117 :param int log_level: the logging level
111118 :param str message: the message to log
112119
113120 """
114121 return "{0:<0.3f}: {1} - {2}" .format (
115- time .monotonic (), level_for (log_level ), message
122+ time .monotonic (), _level_for (log_level ), message
116123 )
117124
118- def emit (self , log_level : int , message : str ):
125+ def _emit (self , log_level : int , message : str ):
119126 """Send a message where it should go.
120127 Placeholder for subclass implementations.
121128 """
122129 raise NotImplementedError ()
123130
124131
125- class PrintHandler (LoggingHandler ):
126- """Send logging messages to the console by using print."""
132+ # pylint: disable=too-few-public-methods
133+ class StreamHandler (Handler ):
134+ """Send logging messages to a stream, `sys.stderr` (typically
135+ the serial console) by default.
136+
137+ :param stream: The stream to log to, default is `sys.stderr`
138+ """
139+
140+ def __init__ (self , stream = None ):
141+ super ().__init__ ()
142+ if stream is None :
143+ stream = sys .stderr
144+ self .stream = stream
145+ """The stream to log to"""
127146
128- def emit (self , log_level : int , message : str ):
147+ def _emit (self , log_level : int , message : str ):
129148 """Send a message to the console.
130149
131150 :param int log_level: the logging level
132151 :param str message: the message to log
133152
134153 """
135- print (self .format (log_level , message ))
154+ print (self ._format (log_level , message ))
136155
137156
138157# The level module-global variables get created when loaded
139158# pylint:disable=undefined-variable
140159
141160logger_cache = {}
142- null_logger = None
161+
162+
163+ def _addLogger (logger_name : str ):
164+ """Adds the logger if it doesn't already exist"""
165+ if logger_name not in logger_cache :
166+ logger_cache [logger_name ] = Logger (logger_name )
167+
143168
144169# pylint:disable=global-statement
145170def getLogger (logger_name : str ) -> "Logger" :
146- """Create or retrieve a logger by name.
171+ """Create or retrieve a logger by name; only retrieves loggers
172+ made using this function; if a Logger with this name does not
173+ exist it is created
147174
148- :param str logger_name: The name of the `Logger` to create/retrieve. `None`
149- will cause the `NullLogger` instance to be returned.
175+ :param str logger_name: The name of the `Logger` to create/retrieve.
150176
151177 """
152- global null_logger
153- if not logger_name or logger_name == "" :
154- if not null_logger :
155- null_logger = NullLogger ()
156- return null_logger
157-
158- if logger_name not in logger_cache :
159- logger_cache [logger_name ] = Logger ()
178+ _addLogger (logger_name )
160179 return logger_cache [logger_name ]
161180
162181
@@ -166,10 +185,13 @@ def getLogger(logger_name: str) -> "Logger":
166185class Logger :
167186 """Provide a logging api."""
168187
169- def __init__ (self ):
188+ def __init__ (self , name : str , level = NOTSET ):
170189 """Create an instance."""
171- self ._level = NOTSET
172- self ._handler = PrintHandler ()
190+ self ._level = level
191+ self .name = name
192+ """The name of the logger, this should be unique for proper
193+ functionality of `getLogger()`"""
194+ self ._handler = None
173195
174196 def setLevel (self , log_level : int ):
175197 """Set the logging cutoff level.
@@ -187,86 +209,89 @@ def getEffectiveLevel(self) -> int:
187209 """
188210 return self ._level
189211
190- def addHandler (self , handler : LoggingHandler ):
212+ def addHandler (self , hdlr : Handler ):
191213 """Sets the handler of this logger to the specified handler.
214+
192215 *NOTE* this is slightly different from the CPython equivalent which adds
193216 the handler rather than replacing it.
194217
195218 :param LoggingHandler handler: the handler
196219
197220 """
198- self ._handler = handler
221+ self ._handler = hdlr
199222
200- def log (self , log_level : int , format_string : str , * args ):
223+ def hasHandlers (self ) -> bool :
224+ """Whether any handlers have been set for this logger"""
225+ return self ._handler is not None
226+
227+ def log (self , level : int , msg : str , * args ):
201228 """Log a message.
202229
203- :param int log_level : the priority level at which to log
204- :param str format_string : the core message string with embedded
230+ :param int level : the priority level at which to log
231+ :param str msg : the core message string with embedded
205232 formatting directives
206233 :param args: arguments to ``format_string.format()``; can be empty
207234
208235 """
209- if log_level >= self ._level :
210- self ._handler .emit ( log_level , format_string % args )
236+ if level >= self ._level :
237+ self ._handler ._emit ( level , msg % args ) # pylint: disable=protected-access
211238
212- def debug (self , format_string : str , * args ):
239+ def debug (self , msg : str , * args ):
213240 """Log a debug message.
214241
215- :param str format_string : the core message string with embedded
242+ :param str fmsg : the core message string with embedded
216243 formatting directives
217244 :param args: arguments to ``format_string.format()``; can be empty
218245
219246 """
220- self .log (DEBUG , format_string , * args )
247+ self .log (DEBUG , msg , * args )
221248
222- def info (self , format_string : str , * args ):
249+ def info (self , msg : str , * args ):
223250 """Log a info message.
224251
225- :param str format_string : the core message string with embedded
252+ :param str msg : the core message string with embedded
226253 formatting directives
227254 :param args: arguments to ``format_string.format()``; can be empty
228255
229256 """
230- self .log (INFO , format_string , * args )
257+ self .log (INFO , msg , * args )
231258
232- def warning (self , format_string : str , * args ):
259+ def warning (self , msg : str , * args ):
233260 """Log a warning message.
234261
235- :param str format_string : the core message string with embedded
262+ :param str msg : the core message string with embedded
236263 formatting directives
237264 :param args: arguments to ``format_string.format()``; can be empty
238265
239266 """
240- self .log (WARNING , format_string , * args )
267+ self .log (WARNING , msg , * args )
241268
242- def error (self , format_string : str , * args ):
269+ def error (self , msg : str , * args ):
243270 """Log a error message.
244271
245- :param str format_string : the core message string with embedded
272+ :param str msg : the core message string with embedded
246273 formatting directives
247274 :param args: arguments to ``format_string.format()``; can be empty
248275
249276 """
250- self .log (ERROR , format_string , * args )
277+ self .log (ERROR , msg , * args )
251278
252- def critical (self , format_string : str , * args ):
279+ def critical (self , msg : str , * args ):
253280 """Log a critical message.
254281
255- :param str format_string : the core message string with embedded
282+ :param str msg : the core message string with embedded
256283 formatting directives
257284 :param args: arguments to ``format_string.format()``; can be empty
258285
259286 """
260- self .log (CRITICAL , format_string , * args )
287+ self .log (CRITICAL , msg , * args )
261288
262289
263- class NullLogger :
264- """Provide an empty logger.
265- This can be used in place of a real logger to more efficiently disable
266- logging."""
290+ class NullHandler (Handler ):
291+ """Provide an empty log handler.
267292
268- def __init__ ( self ):
269- """Dummy implementation ."""
293+ This can be used in place of a real log handler to more efficiently disable
294+ logging ."""
270295
271296 def setLevel (self , log_level : int ):
272297 """Dummy implementation."""
@@ -275,23 +300,59 @@ def getEffectiveLevel(self) -> int:
275300 """Dummy implementation."""
276301 return NOTSET
277302
278- def addHandler (self , handler : LoggingHandler ):
303+ def addHandler (self , handler : Handler ):
279304 """Dummy implementation."""
280305
281306 def log (self , log_level : int , format_string : str , * args ):
282307 """Dummy implementation."""
283308
284- def debug (self , format_string : str , * args ):
309+ def debug (self , msg : str , * args ):
285310 """Dummy implementation."""
286311
287- def info (self , format_string : str , * args ):
312+ def info (self , msg : str , * args ):
288313 """Dummy implementation."""
289314
290- def warning (self , format_string : str , * args ):
315+ def warning (self , msg : str , * args ):
291316 """Dummy implementation."""
292317
293- def error (self , format_string : str , * args ):
318+ def error (self , msg : str , * args ):
294319 """Dummy implementation."""
295320
296- def critical (self , format_string : str , * args ):
321+ def critical (self , msg : str , * args ):
297322 """Dummy implementation."""
323+
324+ def _emit (self , log_level : int , message : str ):
325+ """Dummy implementation"""
326+
327+
328+ class FileHandler (StreamHandler ):
329+ """File handler for working with log files off of the microcontroller (like
330+ an SD card)
331+
332+ :param str filename: The filename of the log file
333+ :param str mode: Whether to write ('w') or append ('a'); default is to append
334+ """
335+
336+ def __init__ (self , filename : str , mode : str = "a" ) -> None :
337+ # pylint: disable=consider-using-with
338+ super ().__init__ (open (filename , mode = mode ))
339+
340+ def close (self ):
341+ """Closes the file"""
342+ self .stream .close ()
343+
344+ def _format (self , log_level : int , message : str ):
345+ """Generate a string to log
346+
347+ :param level: The level of the message
348+ :param msg: The message to format
349+ """
350+ return super ()._format (log_level , message ) + "\r \n "
351+
352+ def _emit (self , log_level : int , message : str ):
353+ """Generate the message and write it to the UART.
354+
355+ :param level: The level of the message
356+ :param msg: The message to log
357+ """
358+ self .stream .write (self ._format (log_level , message ))
0 commit comments