51
51
# pylint:disable=invalid-name
52
52
53
53
import time
54
+ import sys
54
55
55
56
__version__ = "0.0.0-auto.0"
56
57
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Logger.git"
63
64
"WARNING" ,
64
65
"ERROR" ,
65
66
"CRITICAL" ,
66
- "level_for " ,
67
- "LoggingHandler " ,
68
- "PrintHandler " ,
67
+ "_level_for " ,
68
+ "Handler " ,
69
+ "StreamHandler " ,
69
70
"logger_cache" ,
70
- "null_logger" ,
71
71
"getLogger" ,
72
72
"Logger" ,
73
- "NullLogger" ,
73
+ "NullHandler" ,
74
+ "FileHandler" ,
74
75
]
75
76
76
77
87
88
globals ()[__name ] = __value
88
89
89
90
90
- def level_for (value : int ) -> str :
91
+ def _level_for (value : int ) -> str :
91
92
"""Convert a numeric level to the most appropriate name.
92
93
93
94
:param int value: a numeric level
@@ -101,62 +102,80 @@ def level_for(value: int) -> str:
101
102
return LEVELS [0 ][1 ]
102
103
103
104
104
- class LoggingHandler :
105
+ # pylint: disable=too-few-public-methods
106
+ class Handler :
105
107
"""Abstract logging message handler."""
106
108
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 :
108
115
"""Generate a timestamped message.
109
116
110
117
:param int log_level: the logging level
111
118
:param str message: the message to log
112
119
113
120
"""
114
121
return "{0:<0.3f}: {1} - {2}" .format (
115
- time .monotonic (), level_for (log_level ), message
122
+ time .monotonic (), _level_for (log_level ), message
116
123
)
117
124
118
- def emit (self , log_level : int , message : str ):
125
+ def _emit (self , log_level : int , message : str ):
119
126
"""Send a message where it should go.
120
127
Placeholder for subclass implementations.
121
128
"""
122
129
raise NotImplementedError ()
123
130
124
131
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"""
127
146
128
- def emit (self , log_level : int , message : str ):
147
+ def _emit (self , log_level : int , message : str ):
129
148
"""Send a message to the console.
130
149
131
150
:param int log_level: the logging level
132
151
:param str message: the message to log
133
152
134
153
"""
135
- print (self .format (log_level , message ))
154
+ print (self ._format (log_level , message ))
136
155
137
156
138
157
# The level module-global variables get created when loaded
139
158
# pylint:disable=undefined-variable
140
159
141
160
logger_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
+
143
168
144
169
# pylint:disable=global-statement
145
170
def 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
147
174
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.
150
176
151
177
"""
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 )
160
179
return logger_cache [logger_name ]
161
180
162
181
@@ -166,10 +185,13 @@ def getLogger(logger_name: str) -> "Logger":
166
185
class Logger :
167
186
"""Provide a logging api."""
168
187
169
- def __init__ (self ):
188
+ def __init__ (self , name : str , level = NOTSET ):
170
189
"""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
173
195
174
196
def setLevel (self , log_level : int ):
175
197
"""Set the logging cutoff level.
@@ -187,86 +209,89 @@ def getEffectiveLevel(self) -> int:
187
209
"""
188
210
return self ._level
189
211
190
- def addHandler (self , handler : LoggingHandler ):
212
+ def addHandler (self , hdlr : Handler ):
191
213
"""Sets the handler of this logger to the specified handler.
214
+
192
215
*NOTE* this is slightly different from the CPython equivalent which adds
193
216
the handler rather than replacing it.
194
217
195
218
:param LoggingHandler handler: the handler
196
219
197
220
"""
198
- self ._handler = handler
221
+ self ._handler = hdlr
199
222
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 ):
201
228
"""Log a message.
202
229
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
205
232
formatting directives
206
233
:param args: arguments to ``format_string.format()``; can be empty
207
234
208
235
"""
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
211
238
212
- def debug (self , format_string : str , * args ):
239
+ def debug (self , msg : str , * args ):
213
240
"""Log a debug message.
214
241
215
- :param str format_string : the core message string with embedded
242
+ :param str fmsg : the core message string with embedded
216
243
formatting directives
217
244
:param args: arguments to ``format_string.format()``; can be empty
218
245
219
246
"""
220
- self .log (DEBUG , format_string , * args )
247
+ self .log (DEBUG , msg , * args )
221
248
222
- def info (self , format_string : str , * args ):
249
+ def info (self , msg : str , * args ):
223
250
"""Log a info message.
224
251
225
- :param str format_string : the core message string with embedded
252
+ :param str msg : the core message string with embedded
226
253
formatting directives
227
254
:param args: arguments to ``format_string.format()``; can be empty
228
255
229
256
"""
230
- self .log (INFO , format_string , * args )
257
+ self .log (INFO , msg , * args )
231
258
232
- def warning (self , format_string : str , * args ):
259
+ def warning (self , msg : str , * args ):
233
260
"""Log a warning message.
234
261
235
- :param str format_string : the core message string with embedded
262
+ :param str msg : the core message string with embedded
236
263
formatting directives
237
264
:param args: arguments to ``format_string.format()``; can be empty
238
265
239
266
"""
240
- self .log (WARNING , format_string , * args )
267
+ self .log (WARNING , msg , * args )
241
268
242
- def error (self , format_string : str , * args ):
269
+ def error (self , msg : str , * args ):
243
270
"""Log a error message.
244
271
245
- :param str format_string : the core message string with embedded
272
+ :param str msg : the core message string with embedded
246
273
formatting directives
247
274
:param args: arguments to ``format_string.format()``; can be empty
248
275
249
276
"""
250
- self .log (ERROR , format_string , * args )
277
+ self .log (ERROR , msg , * args )
251
278
252
- def critical (self , format_string : str , * args ):
279
+ def critical (self , msg : str , * args ):
253
280
"""Log a critical message.
254
281
255
- :param str format_string : the core message string with embedded
282
+ :param str msg : the core message string with embedded
256
283
formatting directives
257
284
:param args: arguments to ``format_string.format()``; can be empty
258
285
259
286
"""
260
- self .log (CRITICAL , format_string , * args )
287
+ self .log (CRITICAL , msg , * args )
261
288
262
289
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.
267
292
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 ."""
270
295
271
296
def setLevel (self , log_level : int ):
272
297
"""Dummy implementation."""
@@ -275,23 +300,59 @@ def getEffectiveLevel(self) -> int:
275
300
"""Dummy implementation."""
276
301
return NOTSET
277
302
278
- def addHandler (self , handler : LoggingHandler ):
303
+ def addHandler (self , handler : Handler ):
279
304
"""Dummy implementation."""
280
305
281
306
def log (self , log_level : int , format_string : str , * args ):
282
307
"""Dummy implementation."""
283
308
284
- def debug (self , format_string : str , * args ):
309
+ def debug (self , msg : str , * args ):
285
310
"""Dummy implementation."""
286
311
287
- def info (self , format_string : str , * args ):
312
+ def info (self , msg : str , * args ):
288
313
"""Dummy implementation."""
289
314
290
- def warning (self , format_string : str , * args ):
315
+ def warning (self , msg : str , * args ):
291
316
"""Dummy implementation."""
292
317
293
- def error (self , format_string : str , * args ):
318
+ def error (self , msg : str , * args ):
294
319
"""Dummy implementation."""
295
320
296
- def critical (self , format_string : str , * args ):
321
+ def critical (self , msg : str , * args ):
297
322
"""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