1
+ import os
2
+ import socket
1
3
import sys
4
+ import time
2
5
3
6
CRITICAL = 50
4
7
ERROR = 40
7
10
DEBUG = 10
8
11
NOTSET = 0
9
12
13
+ SYSLOG_FORMAT = "<%(pri)s>1 %(asctime)s %(ip)s - - - - %(message)s"
14
+ SYSLOG_DATE_FORMAT = "{0}-{1:02}-{2:02}T{3:02}:{4:02}:{5:02}Z"
15
+
16
+ DEFAULT_FORMAT = "%(levelname)s:%(name)s:%(message)s"
17
+ DEFAULT_DATE_FORMAT = "%d-%02d-%02d %02d:%02d:%02d"
18
+
19
+ SYSLOG_LOCAL_0 = 16
20
+
10
21
_level_dict = {
11
22
CRITICAL : "CRIT" ,
12
23
ERROR : "ERROR" ,
15
26
DEBUG : "DEBUG" ,
16
27
}
17
28
18
- _stream = sys .stderr
29
+ _syslog_severity = {
30
+ CRITICAL : 2 ,
31
+ ERROR : 3 ,
32
+ WARNING : 4 ,
33
+ INFO : 6 ,
34
+ DEBUG : 7 ,
35
+ }
19
36
20
37
21
38
class LogRecord :
@@ -30,8 +47,11 @@ class Handler:
30
47
def __init__ (self ):
31
48
pass
32
49
33
- def setFormatter (self , fmtr ):
34
- pass
50
+ def setFormatter (self , fmt ):
51
+ self .formatter = fmt
52
+
53
+ def format (self , record ):
54
+ return self .formatter .format (record )
35
55
36
56
37
57
class Logger :
@@ -57,19 +77,20 @@ def isEnabledFor(self, level):
57
77
58
78
def log (self , level , msg , * args ):
59
79
if self .isEnabledFor (level ):
60
- levelname = self ._level_str (level )
61
- if args :
62
- msg = msg % args
80
+ d = self .record .__dict__
81
+ d ["levelname" ] = self ._level_str (level )
82
+ d ["levelno" ] = level
83
+ d ["msg" ] = msg
84
+ d ["args" ] = args
85
+ d ["name" ] = self .name
86
+ d ["created" ] = time .time ()
87
+ d ["uptime" ] = time .ticks_ms () // 1000
88
+ d ["pri" ] = (SYSLOG_LOCAL_0 << 3 ) + _syslog_severity [level ]
63
89
if self .handlers :
64
- d = self .record .__dict__
65
- d ["levelname" ] = levelname
66
- d ["levelno" ] = level
67
- d ["message" ] = msg
68
- d ["name" ] = self .name
69
90
for h in self .handlers :
70
91
h .emit (self .record )
71
92
else :
72
- print (levelname , ":" , self .name , ":" , msg , sep = "" , file = _stream )
93
+ print (_formatter . format ( self .record ) , file = _stream )
73
94
74
95
def debug (self , msg , * args ):
75
96
self .log (DEBUG , msg , * args )
@@ -97,9 +118,152 @@ def addHandler(self, hndlr):
97
118
self .handlers .append (hndlr )
98
119
99
120
121
+ class Formatter ():
122
+ def __init__ (self , fmt = None , datefmt = None , style = "%" , defaults = None ):
123
+ self .fmt = fmt or DEFAULT_FORMAT
124
+ self .datefmt = datefmt or DEFAULT_DATE_FORMAT
125
+
126
+ if style not in ("%" , "{" ):
127
+ raise ValueError ("Style must be one of: %, {" )
128
+
129
+ self .style = style
130
+ self .defaults = defaults if defaults else {}
131
+
132
+ def usesTime (self ):
133
+ if self .style == "%" :
134
+ return "%(asctime)" in self .fmt
135
+ elif self .style == "{" :
136
+ return "{asctime" in self .fmt
137
+
138
+ def format (self , record ):
139
+ # merge formatter defaults dict
140
+ if self .defaults :
141
+ record .__dict__ .update (self .defaults )
142
+
143
+ # The message attribute of the record is computed using msg % args.
144
+ if record .args :
145
+ record .__dict__ ["message" ] = record .msg % record .args
146
+ else :
147
+ record .__dict__ ["message" ] = record .msg
148
+
149
+ # If the formatting string contains "(asctime)", formatTime() is called to
150
+ # format the event time.
151
+ if self .usesTime ():
152
+ record .__dict__ ["asctime" ] = self .formatTime (record , self .datefmt )
153
+
154
+ # The record’s attribute dictionary is used as the operand to a string
155
+ # formatting operation.
156
+ if self .style == "%" :
157
+ return self .fmt % record .__dict__
158
+ else :
159
+ return self .fmt .format (** record .__dict__ )
160
+
161
+ def formatTime (self , record , datefmt = None ):
162
+ if not datefmt :
163
+ datefmt = self .datefmt
164
+ created = time .gmtime (record .created )[:6 ]
165
+ _result = datefmt .format (* created )
166
+ return _result if _result != datefmt else datefmt % created
167
+
168
+ def formatException (self , exc_info ):
169
+ raise NotImplementedError ()
170
+
171
+ def formatStack (self , stack_info ):
172
+ raise NotImplementedError ()
173
+
174
+
175
+ class StreamHandler (Handler ):
176
+ def __init__ (self , stream = None ):
177
+ self .stream = stream or sys .stderr
178
+ self .formatter = Formatter ()
179
+
180
+ def emit (self , record ):
181
+ try :
182
+ self .stream .write (self .format (record ))
183
+ except OSError :
184
+ pass
185
+
186
+ def flush (self ):
187
+ pass
188
+
189
+
190
+ class SocketHandler (Handler ):
191
+ def __init__ (self , host , port , socktype = socket .SOCK_DGRAM ):
192
+ if socktype not in (socket .SOCK_STREAM , socket .SOCK_DGRAM ):
193
+ raise ValueError ("Invalid socktype" )
194
+
195
+ self .s = socket .socket (socket .AF_INET , socktype )
196
+ self .addr = socket .getaddrinfo (host , port )[0 ][- 1 ]
197
+ self .terminator = ""
198
+ self .socktype = socktype
199
+ self .formatter = Formatter ()
200
+ self .connected = False
201
+
202
+ if socktype == socket .SOCK_STREAM :
203
+ self .terminator = "\n " # adds PUSH flag to TCP packets
204
+ try :
205
+ self .s .connect (self .addr )
206
+ self .connected = True
207
+ except OSError as e :
208
+ if e .errno == 118 :
209
+ print ("No network connection. " , end = "" )
210
+ elif e .errno == 113 :
211
+ print ("Connection timeout. " , end = "" )
212
+ else :
213
+ print ("Network error. " , end = "" )
214
+ print ("Cannot connect to server {0} on TCP port {1}." .format (self .addr [0 ], self .addr [1 ]))
215
+
216
+ def emit (self , record ):
217
+ try :
218
+ if self .socktype == socket .SOCK_DGRAM or self .connected :
219
+ self .s .sendto (self .format (record ) + self .terminator , self .addr )
220
+ except OSError :
221
+ print ("Network error, cannot send log to the server." )
222
+
223
+
224
+ class SysLogHandler (SocketHandler ):
225
+ """Mostly RFC 5424 compliant logging handler. Does not implement TLS-based transport."""
226
+ def __init__ (self , host , port = 514 , socktype = socket .SOCK_DGRAM ):
227
+ super ().__init__ (host , port , socktype )
228
+ self .formatter = Formatter (fmt = SYSLOG_FORMAT , datefmt = SYSLOG_DATE_FORMAT , defaults = {"ip" : "-" })
229
+
230
+
231
+ class CircularFileHandler (Handler ):
232
+ def __init__ (self , filename , maxsize = 128_000 ):
233
+ if maxsize < 256 :
234
+ raise ValueError ("maxsize must be at least 256 B" )
235
+ self .filename = filename
236
+ self .maxsize = maxsize
237
+ self .formatter = Formatter ()
238
+
239
+ try :
240
+ # checks if file exists and prevents overwriting it
241
+ self .pos = os .stat (self .filename )[6 ]
242
+ self .file = open (filename , "r+" )
243
+ except OSError :
244
+ self .pos = 0
245
+ self .file = open (filename , "w+" )
246
+
247
+ self .file .seek (self .pos )
248
+
249
+ def emit (self , record ):
250
+ message = self .format (record )
251
+ if len (message ) + self .pos > self .maxsize :
252
+ remaining = self .maxsize - self .pos
253
+ self .file .write (message [:remaining ])
254
+ message = message [remaining :]
255
+ self .pos = 0
256
+ self .file .seek (self .pos )
257
+ self .file .write (message )
258
+ self .file .write ("\n " )
259
+ self .file .flush ()
260
+ self .pos += len (message ) + 1
261
+
262
+
263
+ _stream = sys .stderr
100
264
_level = INFO
101
265
_loggers = {}
102
-
266
+ _formatter = Formatter ()
103
267
104
268
def getLogger (name = "root" ):
105
269
if name in _loggers :
@@ -108,21 +272,34 @@ def getLogger(name="root"):
108
272
_loggers [name ] = l
109
273
return l
110
274
275
+ def critical (msg , * args ):
276
+ getLogger ().critical (msg , * args )
277
+
278
+ def error (msg , * args ):
279
+ getLogger ().error (msg , * args )
280
+
281
+ def warning (msg , * args ):
282
+ getLogger ().warning (msg , * args )
111
283
112
284
def info (msg , * args ):
113
285
getLogger ().info (msg , * args )
114
286
115
-
116
287
def debug (msg , * args ):
117
288
getLogger ().debug (msg , * args )
118
289
119
-
120
- def basicConfig (level = INFO , filename = None , stream = None , format = None ):
121
- global _level , _stream
290
+ def basicConfig (level = INFO , filename = None , stream = None , format = None , datefmt = None , style = None ):
291
+ global _level , _stream , _formatter
122
292
_level = level
293
+ getLogger ().handlers = []
123
294
if stream :
124
295
_stream = stream
125
- if filename is not None :
126
- print ("logging.basicConfig: filename arg is not supported" )
127
- if format is not None :
128
- print ("logging.basicConfig: format arg is not supported" )
296
+ if filename :
297
+ if stream :
298
+ getLogger ().addHandler (StreamHandler (stream ))
299
+ getLogger ().addHandler (CircularFileHandler (filename ))
300
+ if format :
301
+ if not style :
302
+ raise ValueError ("style must be specified when the `format` option is used" )
303
+ _formatter = Formatter (fmt = format , datefmt = datefmt , style = style )
304
+ for handler in getLogger ().handlers :
305
+ handler .setFormatter (_formatter )
0 commit comments