2 # -*- indent-tabs-mode: nil -*- vi: set expandtab:
4 # ================================================================================
5 # Copyright (c) 2019-2020 AT&T Intellectual Property. All rights reserved.
6 # ================================================================================
7 # Licensed under the Apache License, Version 2.0 (the "License");
8 # you may not use this file except in compliance with the License.
9 # You may obtain a copy of the License at
11 # http://www.apache.org/licenses/LICENSE-2.0
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
18 # ============LICENSE_END=========================================================
20 """ Common Logging library in Python.
24 Original Written by: Terry Schmalzried
25 Date written: October 1, 2015
26 Last updated: December 1, 2016
33 import logging.handlers
42 """ Common Logging object.
59 DateFmt = '%Y-%m-%dT%H:%M:%S'
62 def __init__(self, configFile, logKey, **kwargs):
63 """Construct a Common Logger for one Log File.
66 configFile -- configuration filename.
67 logKey -- the keyword in configFile that identifies the log filename.
69 Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
70 style -- the log file format (style) to use when writing log messages,
71 one of CommonLogger.ErrorFile, CommonLogger.DebugFile,
72 CommonLogger.AuditFile and CommonLogger.MetricsFile, or
73 one of the strings "error", "debug", "audit" or "metrics".
74 May also be set in the config file using a field named
75 <logKey>Style (where <logKey> is the value of the logKey
76 parameter). The keyword value overrides the value in the
78 requestID (dame) -- optional default value for this log record field.
79 serviceInstanceID (am) -- optional default value for this log record field.
80 threadID (am) -- optional default value for this log record field.
81 serverName (am) -- optional default value for this log record field.
82 serviceName (am) -- optional default value for this log record field.
83 instanceUUID (am) -- optional default value for this log record field.
84 severity (am) -- optional default value for this log record field.
85 serverIPAddress (am) -- optional default value for this log record field.
86 server (am) -- optional default value for this log record field.
87 IPAddress (am) -- optional default value for this log record field.
88 className (am) -- optional default value for this log record field.
89 timer (am) -- (ElapsedTime) optional default value for this log record field.
90 partnerName (ame) -- optional default value for this log record field.
91 targetEntity (me) -- optional default value for this log record field.
92 targetServiceName (me) -- optional default value for this log record field.
93 statusCode (am) -- optional default value for this log record field.
94 responseCode (am) -- optional default value for this log record field.
95 responseDescription (am) -- optional default value for this log record field.
96 processKey (am) -- optional default value for this log record field.
97 targetVirtualEntity (m) -- optional default value for this log record field.
98 customField1 (am) -- optional default value for this log record field.
99 customField2 (am) -- optional default value for this log record field.
100 customField3 (am) -- optional default value for this log record field.
101 customField4 (am) -- optional default value for this log record field.
102 errorCategory (e) -- optional default value for this log record field.
103 errorCode (e) -- optional default value for this log record field.
104 errorDescription (e) -- optional default value for this log record field.
106 Note: the pipe '|' character is not allowed in any log record field.
109 self._monitorFlag = False
111 # Get configuration parameters
112 self._logKey = str(logKey)
113 self._configFile = str(configFile)
114 self._rotateMethod = 'time'
115 self._timeRotateIntervalType = 'midnight'
116 self._timeRotateInterval = 1
117 self._sizeMaxBytes = 0
118 self._sizeRotateMode = 'a'
119 self._socketHost = None
121 self._typeLogger = 'filelogger'
122 self._backupCount = 6
123 self._logLevelThreshold = self._intLogLevel('')
128 self._fields["style"] = CommonLogger.UnknownFile
130 self._configFileModified = os.path.getmtime(self._configFile)
131 for line in open(self._configFile):
132 line = line.split('#',1)[0] # remove comments
134 key, value = [x.strip() for x in line.split('=',1)]
135 if key == 'rotateMethod' and value.lower() in ['time', 'size', 'none']:
136 self._rotateMethod = value.lower()
137 elif key == 'timeRotateIntervalType' and value in ['S', 'M', 'H', 'D', 'W0', 'W1', 'W2', 'W3', 'W4', 'W5', 'W6', 'midnight']:
138 self._timeRotateIntervalType = value
139 elif key == 'timeRotateInterval' and int( value ) > 0:
140 self._timeRotateInterval = int( value )
141 elif key == 'sizeMaxBytes' and int( value ) >= 0:
142 self._sizeMaxBytes = int( value )
143 elif key == 'sizeRotateMode' and value in ['a']:
144 self._sizeRotateMode = value
145 elif key == 'backupCount' and int( value ) >= 0:
146 self._backupCount = int( value )
147 elif key == self._logKey + 'SocketHost':
148 self._socketHost = value
149 elif key == self._logKey + 'SocketPort' and int( value ) == 0:
150 self._socketPort = int( value )
151 elif key == self._logKey + 'LogType' and value.lower() in ['filelogger', 'stdoutlogger', 'stderrlogger', 'socketlogger', 'nulllogger']:
152 self._typeLogger = value.lower()
153 elif key == self._logKey + 'LogLevel':
154 self._logLevelThreshold = self._intLogLevel(value.upper())
155 elif key == self._logKey + 'Style':
156 self._fields["style"] = value
157 elif key == self._logKey:
158 self._logFile = value
159 except Exception as x:
160 print("exception reading '%s' configuration file: %s" %(self._configFile, str(x)))
163 print("exception reading '%s' configuration file" %(self._configFile))
166 if self._logFile is None:
167 print('configuration file %s is missing definition %s for log file' %(self._configFile, self._logKey))
171 # initialize default log fields
172 # timestamp will automatically be generated
173 for key in ['style', 'requestID', 'serviceInstanceID', 'threadID', 'serverName', 'serviceName', 'instanceUUID', \
174 'severity', 'serverIPAddress', 'server', 'IPAddress', 'className', 'timer', \
175 'partnerName', 'targetEntity', 'targetServiceName', 'statusCode', 'responseCode', \
176 'responseDescription', 'processKey', 'targetVirtualEntity', 'customField1', \
177 'customField2', 'customField3', 'customField4', 'errorCategory', 'errorCode', \
178 'errorDescription' ]:
179 if key in kwargs and kwargs[key] != None:
180 self._fields[key] = kwargs[key]
182 self._resetStyleField()
185 self._logLock = threading.Lock()
187 self._logger = logging.getLogger(self._logKey)
188 self._logger.propagate = False
191 self._defaultServerInfo()
193 # spawn a thread to monitor configFile for logLevel and logFile changes
194 self._monitorFlag = True
195 self._monitorThread = threading.Thread(target=self._monitorConfigFile, args=())
196 self._monitorThread.daemon = True
197 self._monitorThread.start()
200 def _createLogger(self):
201 if self._typeLogger == 'filelogger':
202 self._mkdir_p(self._logFile)
203 if self._rotateMethod == 'time':
204 self._logHandler = logging.handlers.TimedRotatingFileHandler(self._logFile, \
205 when=self._timeRotateIntervalType, interval=self._timeRotateInterval, \
206 backupCount=self._backupCount, encoding=None, delay=False, utc=True)
207 elif self._rotateMethod == 'size':
208 self._logHandler = logging.handlers.RotatingFileHandler(self._logFile, \
209 mode=self._sizeRotateMode, maxBytes=self._sizeMaxBytes, \
210 backupCount=self._backupCount, encoding=None, delay=False)
213 self._logHandler = logging.handlers.WatchedFileHandler(self._logFile, \
214 mode=self._sizeRotateMode, \
215 encoding=None, delay=False)
216 elif self._typeLogger == 'stderrlogger':
217 self._logHandler = logging.handlers.StreamHandler(sys.stderr)
218 elif self._typeLogger == 'stdoutlogger':
219 self._logHandler = logging.handlers.StreamHandler(sys.stdout)
220 elif self._typeLogger == 'socketlogger':
221 self._logHandler = logging.handlers.SocketHandler(self._socketHost, self._socketPort)
222 elif self._typeLogger == 'nulllogger':
223 self._logHandler = logging.handlers.NullHandler()
225 if self._fields["style"] == CommonLogger.AuditFile or self._fields["style"] == CommonLogger.MetricsFile:
226 self._logFormatter = logging.Formatter(fmt='%(begtime)s,%(begmsecs)03d+00:00|%(endtime)s,%(endmsecs)03d+00:00|%(message)s', datefmt=CommonLogger.DateFmt)
228 self._logFormatter = logging.Formatter(fmt='%(asctime)s,%(msecs)03d+00:00|%(message)s', datefmt='%Y-%m-%dT%H:%M:%S')
229 self._logFormatter.converter = time.gmtime
230 self._logHandler.setFormatter(self._logFormatter)
231 self._logger.addHandler(self._logHandler)
233 def _resetStyleField(self):
234 styleFields = ["error", "debug", "audit", "metrics"]
235 if self._fields['style'] in styleFields:
236 self._fields['style'] = styleFields.index(self._fields['style'])
239 if not self._monitorFlag:
242 self._monitorFlag = False
244 if self._monitorThread is not None and self._monitorThread.is_alive():
245 self._monitorThread.join()
247 self._monitorThread = None
250 def _defaultServerInfo(self):
252 # If not set or purposely set = None, then set default
253 if self._fields.get('server') is None:
255 self._fields['server'] = socket.getfqdn()
256 except Exception as err:
258 self._fields['server'] = socket.gethostname()
259 except Exception as err:
260 self._fields['server'] = ""
262 # If not set or purposely set = None, then set default
263 if self._fields.get('serverIPAddress') is None:
265 self._fields['serverIPAddress'] = socket.gethostbyname(self._fields['server'])
266 except Exception as err:
267 self._fields['serverIPAddress'] = ""
270 def _monitorConfigFile(self):
271 while self._monitorFlag:
273 fileTime = os.path.getmtime(self._configFile)
274 if fileTime > self._configFileModified:
275 self._configFileModified = fileTime
276 ReopenLogFile = False
277 logFile = self._logFile
278 with open(self._configFile) as fp:
280 line = line.split('#',1)[0] # remove comments
282 key, value = [x.strip() for x in line.split('=',1)]
283 if key == 'rotateMethod' and value.lower() in ['time', 'size', 'none'] and self._rotateMethod != value:
284 self._rotateMethod = value.lower()
286 elif key == 'timeRotateIntervalType' and value in ['S', 'M', 'H', 'D', 'W0', 'W1', 'W2', 'W3', 'W4', 'W5', 'W6', 'midnight']:
287 self._timeRotateIntervalType = value
289 elif key == 'timeRotateInterval' and int( value ) > 0:
290 self._timeRotateInterval = int( value )
292 elif key == 'sizeMaxBytes' and int( value ) >= 0:
293 self._sizeMaxBytes = int( value )
295 elif key == 'sizeRotateMode' and value in ['a']:
296 self._sizeRotateMode = value
298 elif key == 'backupCount' and int( value ) >= 0:
299 self._backupCount = int( value )
301 elif key == self._logKey + 'SocketHost' and self._socketHost != value:
302 self._socketHost = value
304 elif key == self._logKey + 'SocketPort' and self._socketPort > 0 and self._socketPort != int( value ):
305 self._socketPort = int( value )
307 elif key == self._logKey + 'LogLevel' and self._logLevelThreshold != self._intLogLevel( value.upper() ):
308 self._logLevelThreshold = self._intLogLevel(value.upper())
309 elif key == self._logKey + 'LogType' and self._typeLogger != value and value.lower() in ['filelogger', 'stdoutlogger', 'stderrlogger', 'socketlogger', 'nulllogger']:
310 self._typeLogger = value.lower()
312 elif key == self._logKey + 'Style':
313 self._fields["style"] = value
314 self._resetStyleField()
315 elif key == self._logKey and self._logFile != value:
320 self._logger.removeHandler(self._logHandler)
321 self._logFile = logFile
323 except Exception as err:
329 def setFields(self, **kwargs):
330 """Set default values for log fields.
332 Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
333 style -- the log file format (style) to use when writing log messages
334 requestID (dame) -- optional default value for this log record field.
335 serviceInstanceID (am) -- optional default value for this log record field.
336 threadID (am) -- optional default value for this log record field.
337 serverName (am) -- optional default value for this log record field.
338 serviceName (am) -- optional default value for this log record field.
339 instanceUUID (am) -- optional default value for this log record field.
340 severity (am) -- optional default value for this log record field.
341 serverIPAddress (am) -- optional default value for this log record field.
342 server (am) -- optional default value for this log record field.
343 IPAddress (am) -- optional default value for this log record field.
344 className (am) -- optional default value for this log record field.
345 timer (am) -- (ElapsedTime) optional default value for this log record field.
346 partnerName (ame) -- optional default value for this log record field.
347 targetEntity (me) -- optional default value for this log record field.
348 targetServiceName (me) -- optional default value for this log record field.
349 statusCode (am) -- optional default value for this log record field.
350 responseCode (am) -- optional default value for this log record field.
351 responseDescription (am) -- optional default value for this log record field.
352 processKey (am) -- optional default value for this log record field.
353 targetVirtualEntity (m) -- optional default value for this log record field.
354 customField1 (am) -- optional default value for this log record field.
355 customField2 (am) -- optional default value for this log record field.
356 customField3 (am) -- optional default value for this log record field.
357 customField4 (am) -- optional default value for this log record field.
358 errorCategory (e) -- optional default value for this log record field.
359 errorCode (e) -- optional default value for this log record field.
360 errorDescription (e) -- optional default value for this log record field.
362 Note: the pipe '|' character is not allowed in any log record field.
365 for key in ['style', 'requestID', 'serviceInstanceID', 'threadID', 'serverName', 'serviceName', 'instanceUUID', \
366 'severity', 'serverIPAddress', 'server', 'IPAddress', 'className', 'timer', \
367 'partnerName', 'targetEntity', 'targetServiceName', 'statusCode', 'responseCode', \
368 'responseDescription', 'processKey', 'targetVirtualEntity', 'customField1', \
369 'customField2', 'customField3', 'customField4', 'errorCategory', 'errorCode', \
370 'errorDescription' ]:
372 if kwargs[key] != None:
373 self._fields[key] = kwargs[key]
374 elif key in self._fields:
375 del self._fields[key]
377 self._defaultServerInfo()
380 def debug(self, message, **kwargs):
381 """Write a DEBUG level message to the log file.
384 message -- value for the last log record field.
386 Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
387 style -- the log file format (style) to use when writing log messages
388 requestID (dame) -- optional default value for this log record field.
389 serviceInstanceID (am) -- optional default value for this log record field.
390 threadID (am) -- optional default value for this log record field.
391 serverName (am) -- optional default value for this log record field.
392 serviceName (am) -- optional default value for this log record field.
393 instanceUUID (am) -- optional default value for this log record field.
394 severity (am) -- optional default value for this log record field.
395 serverIPAddress (am) -- optional default value for this log record field.
396 server (am) -- optional default value for this log record field.
397 IPAddress (am) -- optional default value for this log record field.
398 className (am) -- optional default value for this log record field.
399 timer (am) -- (ElapsedTime) optional default value for this log record field.
400 partnerName (ame) -- optional default value for this log record field.
401 targetEntity (me) -- optional default value for this log record field.
402 targetServiceName (me) -- optional default value for this log record field.
403 statusCode (am) -- optional default value for this log record field.
404 responseCode (am) -- optional default value for this log record field.
405 responseDescription (am) -- optional default value for this log record field.
406 processKey (am) -- optional default value for this log record field.
407 targetVirtualEntity (m) -- optional default value for this log record field.
408 customField1 (am) -- optional default value for this log record field.
409 customField2 (am) -- optional default value for this log record field.
410 customField3 (am) -- optional default value for this log record field.
411 customField4 (am) -- optional default value for this log record field.
412 errorCategory (e) -- optional default value for this log record field.
413 errorCode (e) -- optional default value for this log record field.
414 errorDescription (e) -- optional default value for this log record field.
415 begTime (am) -- optional starting time for this audit/metrics log record.
417 Note: the pipe '|' character is not allowed in any log record field.
420 self._log('DEBUG', message, errorCategory = 'DEBUG', **kwargs)
422 def info(self, message, **kwargs):
423 """Write an INFO level message to the log file.
426 message -- value for the last log record field.
428 Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
429 style -- the log file format (style) to use when writing log messages
430 requestID (dame) -- optional default value for this log record field.
431 serviceInstanceID (am) -- optional default value for this log record field.
432 threadID (am) -- optional default value for this log record field.
433 serverName (am) -- optional default value for this log record field.
434 serviceName (am) -- optional default value for this log record field.
435 instanceUUID (am) -- optional default value for this log record field.
436 severity (am) -- optional default value for this log record field.
437 serverIPAddress (am) -- optional default value for this log record field.
438 server (am) -- optional default value for this log record field.
439 IPAddress (am) -- optional default value for this log record field.
440 className (am) -- optional default value for this log record field.
441 timer (am) -- (ElapsedTime) optional default value for this log record field.
442 partnerName (ame) -- optional default value for this log record field.
443 targetEntity (me) -- optional default value for this log record field.
444 targetServiceName (me) -- optional default value for this log record field.
445 statusCode (am) -- optional default value for this log record field.
446 responseCode (am) -- optional default value for this log record field.
447 responseDescription (am) -- optional default value for this log record field.
448 processKey (am) -- optional default value for this log record field.
449 targetVirtualEntity (m) -- optional default value for this log record field.
450 customField1 (am) -- optional default value for this log record field.
451 customField2 (am) -- optional default value for this log record field.
452 customField3 (am) -- optional default value for this log record field.
453 customField4 (am) -- optional default value for this log record field.
454 errorCategory (e) -- optional default value for this log record field.
455 errorCode (e) -- optional default value for this log record field.
456 errorDescription (e) -- optional default value for this log record field.
457 begTime (am) -- optional starting time for this audit/metrics log record.
459 Note: the pipe '|' character is not allowed in any log record field.
462 self._log('INFO', message, errorCategory = 'INFO', **kwargs)
464 def warn(self, message, **kwargs):
465 """Write a WARN level message to the log file.
468 message -- value for the last log record field.
470 Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
471 style -- the log file format (style) to use when writing log messages
472 requestID (dame) -- optional default value for this log record field.
473 serviceInstanceID (am) -- optional default value for this log record field.
474 threadID (am) -- optional default value for this log record field.
475 serverName (am) -- optional default value for this log record field.
476 serviceName (am) -- optional default value for this log record field.
477 instanceUUID (am) -- optional default value for this log record field.
478 severity (am) -- optional default value for this log record field.
479 serverIPAddress (am) -- optional default value for this log record field.
480 server (am) -- optional default value for this log record field.
481 IPAddress (am) -- optional default value for this log record field.
482 className (am) -- optional default value for this log record field.
483 timer (am) -- (ElapsedTime) optional default value for this log record field.
484 partnerName (ame) -- optional default value for this log record field.
485 targetEntity (me) -- optional default value for this log record field.
486 targetServiceName (me) -- optional default value for this log record field.
487 statusCode (am) -- optional default value for this log record field.
488 responseCode (am) -- optional default value for this log record field.
489 responseDescription (am) -- optional default value for this log record field.
490 processKey (am) -- optional default value for this log record field.
491 targetVirtualEntity (m) -- optional default value for this log record field.
492 customField1 (am) -- optional default value for this log record field.
493 customField2 (am) -- optional default value for this log record field.
494 customField3 (am) -- optional default value for this log record field.
495 customField4 (am) -- optional default value for this log record field.
496 errorCategory (e) -- optional default value for this log record field.
497 errorCode (e) -- optional default value for this log record field.
498 errorDescription (e) -- optional default value for this log record field.
499 begTime (am) -- optional starting time for this audit/metrics log record.
501 Note: the pipe '|' character is not allowed in any log record field.
504 self._log('WARN', message, errorCategory = 'WARN', **kwargs)
506 def error(self, message, **kwargs):
507 """Write an ERROR level message to the log file.
510 message -- value for the last log record field.
512 Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
513 style -- the log file format (style) to use when writing log messages
514 requestID (dame) -- optional default value for this log record field.
515 serviceInstanceID (am) -- optional default value for this log record field.
516 threadID (am) -- optional default value for this log record field.
517 serverName (am) -- optional default value for this log record field.
518 serviceName (am) -- optional default value for this log record field.
519 instanceUUID (am) -- optional default value for this log record field.
520 severity (am) -- optional default value for this log record field.
521 serverIPAddress (am) -- optional default value for this log record field.
522 server (am) -- optional default value for this log record field.
523 IPAddress (am) -- optional default value for this log record field.
524 className (am) -- optional default value for this log record field.
525 timer (am) -- (ElapsedTime) optional default value for this log record field.
526 partnerName (ame) -- optional default value for this log record field.
527 targetEntity (me) -- optional default value for this log record field.
528 targetServiceName (me) -- optional default value for this log record field.
529 statusCode (am) -- optional default value for this log record field.
530 responseCode (am) -- optional default value for this log record field.
531 responseDescription (am) -- optional default value for this log record field.
532 processKey (am) -- optional default value for this log record field.
533 targetVirtualEntity (m) -- optional default value for this log record field.
534 customField1 (am) -- optional default value for this log record field.
535 customField2 (am) -- optional default value for this log record field.
536 customField3 (am) -- optional default value for this log record field.
537 customField4 (am) -- optional default value for this log record field.
538 errorCategory (e) -- optional default value for this log record field.
539 errorCode (e) -- optional default value for this log record field.
540 errorDescription (e) -- optional default value for this log record field.
541 begTime (am) -- optional starting time for this audit/metrics log record.
543 Note: the pipe '|' character is not allowed in any log record field.
546 self._log('ERROR', message, errorCategory = 'ERROR', **kwargs)
548 def fatal(self, message, **kwargs):
549 """Write a FATAL level message to the log file.
552 message -- value for the last log record field.
554 Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
555 style -- the log file format (style) to use when writing log messages
556 requestID (dame) -- optional default value for this log record field.
557 serviceInstanceID (am) -- optional default value for this log record field.
558 threadID (am) -- optional default value for this log record field.
559 serverName (am) -- optional default value for this log record field.
560 serviceName (am) -- optional default value for this log record field.
561 instanceUUID (am) -- optional default value for this log record field.
562 severity (am) -- optional default value for this log record field.
563 serverIPAddress (am) -- optional default value for this log record field.
564 server (am) -- optional default value for this log record field.
565 IPAddress (am) -- optional default value for this log record field.
566 className (am) -- optional default value for this log record field.
567 timer (am) -- (ElapsedTime) optional default value for this log record field.
568 partnerName (ame) -- optional default value for this log record field.
569 targetEntity (me) -- optional default value for this log record field.
570 targetServiceName (me) -- optional default value for this log record field.
571 statusCode (am) -- optional default value for this log record field.
572 responseCode (am) -- optional default value for this log record field.
573 responseDescription (am) -- optional default value for this log record field.
574 processKey (am) -- optional default value for this log record field.
575 targetVirtualEntity (m) -- optional default value for this log record field.
576 customField1 (am) -- optional default value for this log record field.
577 customField2 (am) -- optional default value for this log record field.
578 customField3 (am) -- optional default value for this log record field.
579 customField4 (am) -- optional default value for this log record field.
580 errorCategory (e) -- optional default value for this log record field.
581 errorCode (e) -- optional default value for this log record field.
582 errorDescription (e) -- optional default value for this log record field.
583 begTime (am) -- optional starting time for this audit/metrics log record.
585 Note: the pipe '|' character is not allowed in any log record field.
588 self._log('FATAL', message, errorCategory = 'FATAL', **kwargs)
590 def _log(self, logLevel, message, **kwargs):
591 """Write a message to the log file.
594 logLevel -- value ('DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL', ...) for the log record.
595 message -- value for the last log record field.
597 Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
598 style -- the log file format (style) to use when writing log messages
599 requestID (dame) -- optional default value for this log record field.
600 serviceInstanceID (am) -- optional default value for this log record field.
601 threadID (am) -- optional default value for this log record field.
602 serverName (am) -- optional default value for this log record field.
603 serviceName (am) -- optional default value for this log record field.
604 instanceUUID (am) -- optional default value for this log record field.
605 severity (am) -- optional default value for this log record field.
606 serverIPAddress (am) -- optional default value for this log record field.
607 server (am) -- optional default value for this log record field.
608 IPAddress (am) -- optional default value for this log record field.
609 className (am) -- optional default value for this log record field.
610 timer (am) -- (ElapsedTime) optional default value for this log record field.
611 partnerName (ame) -- optional default value for this log record field.
612 targetEntity (me) -- optional default value for this log record field.
613 targetServiceName (me) -- optional default value for this log record field.
614 statusCode (am) -- optional default value for this log record field.
615 responseCode (am) -- optional default value for this log record field.
616 responseDescription (am) -- optional default value for this log record field.
617 processKey (am) -- optional default value for this log record field.
618 targetVirtualEntity (m) -- optional default value for this log record field.
619 customField1 (am) -- optional default value for this log record field.
620 customField2 (am) -- optional default value for this log record field.
621 customField3 (am) -- optional default value for this log record field.
622 customField4 (am) -- optional default value for this log record field.
623 errorCategory (e) -- optional default value for this log record field.
624 errorCode (e) -- optional default value for this log record field.
625 errorDescription (e) -- optional default value for this log record field.
626 begTime (am) -- optional starting time for this audit/metrics log record.
628 Note: the pipe '|' character is not allowed in any log record field.
631 # timestamp will automatically be inserted
632 style = int(self._getVal('style', '', **kwargs))
633 requestID = self._getVal('requestID', '', **kwargs)
634 serviceInstanceID = self._getVal('serviceInstanceID', '', **kwargs)
635 threadID = self._getVal('threadID', threading.currentThread().getName(), **kwargs)
636 serverName = self._getVal('serverName', '', **kwargs)
637 serviceName = self._getVal('serviceName', '', **kwargs)
638 instanceUUID = self._getVal('instanceUUID', '', **kwargs)
639 upperLogLevel = self._noSep(logLevel.upper())
640 severity = self._getVal('severity', '', **kwargs)
641 serverIPAddress = self._getVal('serverIPAddress', '', **kwargs)
642 server = self._getVal('server', '', **kwargs)
643 IPAddress = self._getVal('IPAddress', '', **kwargs)
644 className = self._getVal('className', '', **kwargs)
645 timer = self._getVal('timer', '', **kwargs)
646 partnerName = self._getVal('partnerName', '', **kwargs)
647 targetEntity = self._getVal('targetEntity', '', **kwargs)
648 targetServiceName = self._getVal('targetServiceName', '', **kwargs)
649 statusCode = self._getVal('statusCode', '', **kwargs)
650 responseCode = self._getVal('responseCode', '', **kwargs)
651 responseDescription = self._noSep(self._getVal('responseDescription', '', **kwargs))
652 processKey = self._getVal('processKey', '', **kwargs)
653 targetVirtualEntity = self._getVal('targetVirtualEntity', '', **kwargs)
654 customField1 = self._getVal('customField1', '', **kwargs)
655 customField2 = self._getVal('customField2', '', **kwargs)
656 customField3 = self._getVal('customField3', '', **kwargs)
657 customField4 = self._getVal('customField4', '', **kwargs)
658 errorCategory = self._getVal('errorCategory', '', **kwargs)
659 errorCode = self._getVal('errorCode', '', **kwargs)
660 errorDescription = self._noSep(self._getVal('errorDescription', '', **kwargs))
661 nbegTime = self._getArg('begTime', {}, **kwargs)
663 detailMessage = self._noSep(message)
664 if bool(re.match(r" *$", detailMessage)):
665 return # don't log empty messages
667 useLevel = self._intLogLevel(upperLogLevel)
668 if CommonLogger.verbose: print("logger STYLE=%s" % style)
669 if useLevel < self._logLevelThreshold:
670 if CommonLogger.verbose: print("skipping because of level")
674 if style == CommonLogger.ErrorFile:
675 if CommonLogger.verbose: print("using CommonLogger.ErrorFile")
676 self._logger.log(50, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' \
677 %(requestID, threadID, serviceName, partnerName, targetEntity, targetServiceName,
678 errorCategory, errorCode, errorDescription, detailMessage))
679 elif style == CommonLogger.DebugFile:
680 if CommonLogger.verbose: print("using CommonLogger.DebugFile")
681 self._logger.log(50, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' \
682 %(requestID, threadID, serverName, serviceName, instanceUUID, upperLogLevel,
683 severity, serverIPAddress, server, IPAddress, className, timer, detailMessage))
684 elif style == CommonLogger.AuditFile:
685 if CommonLogger.verbose: print("using CommonLogger.AuditFile")
686 endAuditTime, endAuditMsec = self._getTime()
687 if type(nbegTime) is dict and 'begTime' in nbegTime and 'begMsec' in nbegTime:
688 d = { 'begtime': self._noSep(nbegTime['begTime']), 'begmsecs': float(self._noSep(nbegTime['begMsec'])), 'endtime': endAuditTime, 'endmsecs': endAuditMsec }
689 elif self._begTime is not None:
690 d = { 'begtime': self._begTime, 'begmsecs': self._begMsec, 'endtime': endAuditTime, 'endmsecs': endAuditMsec }
692 d = { 'begtime': endAuditTime, 'begmsecs': endAuditMsec, 'endtime': endAuditTime, 'endmsecs': endAuditMsec }
695 self._logger.log(50, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' \
696 %(requestID, serviceInstanceID, threadID, serverName, serviceName, partnerName,
697 statusCode, responseCode, responseDescription, instanceUUID, upperLogLevel,
698 severity, serverIPAddress, timer, server, IPAddress, className, unused,
699 processKey, customField1, customField2, customField3, customField4, detailMessage), extra=d)
700 elif style == CommonLogger.MetricsFile:
701 if CommonLogger.verbose: print("using CommonLogger.MetricsFile")
702 endMetricsTime, endMetricsMsec = self._getTime()
703 if type(nbegTime) is dict and 'begTime' in nbegTime and 'begMsec' in nbegTime:
704 d = { 'begtime': self._noSep(nbegTime['begTime']), 'begmsecs': float(self._noSep(nbegTime['begMsec'])), 'endtime': endMetricsTime, 'endmsecs': endMetricsMsec }
705 elif self._begTime is not None:
706 d = { 'begtime': self._begTime, 'begmsecs': self._begMsec, 'endtime': endMetricsTime, 'endmsecs': endMetricsMsec }
708 d = { 'begtime': endMetricsTime, 'begmsecs': endMetricsMsec, 'endtime': endMetricsTime, 'endmsecs': endMetricsMsec }
711 self._logger.log(50, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' \
712 %(requestID, serviceInstanceID, threadID, serverName, serviceName, partnerName,
713 targetEntity, targetServiceName, statusCode, responseCode, responseDescription,
714 instanceUUID, upperLogLevel, severity, serverIPAddress, timer, server, IPAddress,
715 className, unused, processKey, targetVirtualEntity, customField1, customField2,
716 customField3, customField4, detailMessage), extra=d)
718 print("!!!!!!!!!!!!!!!! style not set: %s" % self._fields["style"])
722 lt = time.localtime(ct)
723 return (time.strftime(CommonLogger.DateFmt, lt), (ct - int(ct)) * 1000)
725 def setStartRecordEvent(self):
727 Set the start time to be saved for both audit and metrics records
729 self._begTime, self._begMsec = self._getTime()
731 def getStartRecordEvent(self):
733 Retrieve the start time to be used for either audit and metrics records
735 begTime, begMsec = self._getTime()
736 return {'begTime':begTime, 'begMsec':begMsec}
738 def _getVal(self, key, default, **kwargs):
739 val = self._fields.get(key)
740 if key in kwargs: val = kwargs[key]
741 if val is None: val = default
742 return self._noSep(val)
744 def _getArg(self, key, default, **kwargs):
746 if key in kwargs: val = kwargs[key]
747 if val is None: val = default
750 def _noSep(self, message):
751 if message is None: return ''
752 return re.sub(r'[\|\n]', ' ', str(message))
754 def _intLogLevel(self, logLevel):
755 if logLevel == 'FATAL': useLevel = 50
756 elif logLevel == 'ERROR': useLevel = 40
757 elif logLevel == 'WARN': useLevel = 30
758 elif logLevel == 'INFO': useLevel = 20
759 elif logLevel == 'DEBUG': useLevel = 10
763 def _mkdir_p(self, filename):
764 """Create missing directories from a full filename path like mkdir -p"""
769 folder=os.path.dirname(filename)
774 if not os.path.exists(folder):
777 except OSError as err:
778 print("error number %d creating %s directory to hold %s logfile: %s" %(err.errno, err.filename, filename, err.strerror))
780 except Exception as err:
781 print("error creating %s directory to hold %s logfile: %s" %(folder, filename, str(err)))
784 if __name__ == "__main__":
786 def __checkOneTime(line):
787 format = r'[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2},[0-9]{3}[+]00:00[|]'
788 m = re.match(format, line)
790 print("ERROR: time string did not match proper time format, %s" %line)
791 print("\t: format=%s" % format)
795 def __checkTwoTimes(line, different):
796 format = r'[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:([0-9]{2}),([0-9]{3})[+]00:00[|][0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:([0-9]{2}),([0-9]{3})[+]00:00[|]'
797 m = re.match(format, line)
799 print("ERROR: time strings did not match proper time format, %s" %line)
800 print("\t: format=%s" % format)
802 second1 = int(m.group(1))
803 msec1 = int(m.group(2))
804 second2 = int(m.group(3))
805 msec2 = int(m.group(4))
806 if second1 > second2: second2 += 60
807 t1 = second1 * 1000 + msec1
808 t2 = second2 * 1000 + msec2
810 # print("t1=%d (%d,%d) t2=%d (%d,%d), diff = %d" % (t1, second1, msec1, t2, second2, msec2, diff))
813 print("ERROR: times did not differ enough: %s" % line)
817 print("ERROR: times were too far apart: %s" % line)
821 def __checkBegTime(line):
822 format = "begTime should be ([-0-9T:]+)"
823 # print("checkBegTime(%s)" % line)
824 strt = 'begTime should be '
826 rest = line[i+len(strt):].rstrip()
827 if not line.startswith(rest + ","):
828 print("ERROR: line %s should start with %s" % (line,rest))
832 def __checkLog(logfile, numLines, numFields):
835 with open(logfile, "r") as fp:
837 # print("saw line %s" % line)
841 print("ERROR: wrong number of fields. Expected %d, got %d: %s" % (numFields, c, line))
843 if re.search("should not appear", line):
844 print("ERROR: a line appeared that should not have appeared, %s" % line)
846 elif re.search("single time", line):
847 errorCount += __checkOneTime(line)
848 elif re.search("time should be the same", line):
849 errorCount += __checkTwoTimes(line, different=False)
850 elif re.search("time should be ", line):
851 errorCount += __checkTwoTimes(line, different=True)
852 elif re.search("begTime should be ", line):
853 errorCount += __checkBegTime(line)
855 print("ERROR: an unknown message appeared, %s" % line)
858 if lineCount != numLines:
859 print("ERROR: expected %d lines, but got %d lines" % (numLines, lineCount))
864 parser = argparse.ArgumentParser(description="test the CommonLogger functions")
865 parser.add_argument("-k", "--keeplogs", help="Keep the log files after finishing the tests", action="store_true")
866 parser.add_argument("-v", "--verbose", help="Print debugging messages", action="store_true")
867 args = parser.parse_args()
869 spid = str(os.getpid())
872 logcfg = "/tmp/cl.log" + spid + ".cfg"
873 errorLog = "/tmp/cl.error" + spid + ".log"
874 metricsLog = "/tmp/cl.metrics" + spid + ".log"
875 auditLog = "/tmp/cl.audit" + spid + ".log"
876 debugLog = "/tmp/cl.debug" + spid + ".log"
877 if args.verbose: CommonLogger.verbose = True
881 for f in [ logcfg, errorLog, metricsLog, auditLog, debugLog ]:
886 if not args.keeplogs:
887 atexit.register(cleanupTmps)
889 with open(logcfg, "w") as o:
890 o.write("error = " + errorLog + "\n" +
891 "errorLogLevel = WARN\n" +
892 "metrics = " + metricsLog + "\n" +
893 "metricsLogLevel = INFO\n" +
894 "audit = " + auditLog + "\n" +
895 "auditLogLevel = INFO\n" +
896 "debug = " + debugLog + "\n" +
897 "debugLogLevel = DEBUG\n")
900 instanceUUID = uuid.uuid1()
901 serviceName = "testharness"
902 errorLogger = CommonLogger(logcfg, "error", style=CommonLogger.ErrorFile, instanceUUID=instanceUUID, serviceName=serviceName)
903 debugLogger = CommonLogger(logcfg, "debug", style=CommonLogger.DebugFile, instanceUUID=instanceUUID, serviceName=serviceName)
904 auditLogger = CommonLogger(logcfg, "audit", style=CommonLogger.AuditFile, instanceUUID=instanceUUID, serviceName=serviceName)
905 metricsLogger = CommonLogger(logcfg, "metrics", style=CommonLogger.MetricsFile, instanceUUID=instanceUUID, serviceName=serviceName)
909 errorLogger.debug("error calling debug (should not appear)")
910 errorLogger.info("error calling info (should not appear)")
911 errorLogger.warn("error calling warn (single time)")
912 errorLogger.error("error calling error (single time)")
913 errorLogger.setStartRecordEvent()
915 errorLogger.fatal("error calling fatal, after setStartRecordEvent and sleep (start should be ignored, single time)")
917 errorCount += __checkLog(errorLog, 3, 10)
919 auditLogger.debug("audit calling debug (should not appear)")
920 auditLogger.info("audit calling info (time should be the same)")
921 auditLogger.warn("audit calling warn (time should be the same)")
922 auditLogger.error("audit calling error (time should be the same)")
923 bt = auditLogger.getStartRecordEvent()
924 # print("bt=%s" % bt)
926 auditLogger.setStartRecordEvent()
928 auditLogger.fatal("audit calling fatal, after setStartRecordEvent and sleep, time should be different)")
930 auditLogger.fatal("audit calling fatal, begTime should be %s" % bt['begTime'], begTime=bt)
932 errorCount += __checkLog(auditLog, 5, 25)
934 debugLogger.debug("debug calling debug (single time)")
935 debugLogger.info("debug calling info (single time)")
936 debugLogger.warn("debug calling warn (single time)")
937 debugLogger.setStartRecordEvent()
939 debugLogger.error("debug calling error, after SetStartRecordEvent and sleep (start should be ignored, single time)")
940 debugLogger.fatal("debug calling fatal (single time)")
941 errorCount += __checkLog(debugLog, 5, 13)
944 metricsLogger.debug("metrics calling debug (should not appear)")
945 metricsLogger.info("metrics calling info (time should be the same)")
946 metricsLogger.warn("metrics calling warn (time should be the same)")
947 bt = metricsLogger.getStartRecordEvent()
949 metricsLogger.setStartRecordEvent()
951 metricsLogger.error("metrics calling error, after SetStartRecordEvent and sleep, time should be different")
952 metricsLogger.fatal("metrics calling fatal (time should be the same)")
954 metricsLogger.fatal("metrics calling fatal, begTime should be %s" % bt['begTime'], begTime=bt)
956 errorCount += __checkLog(metricsLog, 5, 28)
958 print("%d tests run, %d errors found" % (testsRun, errorCount))