1 # -------------------------------------------------------------------------
2 # Copyright (c) 2015-2017 AT&T Intellectual Property
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
16 # -------------------------------------------------------------------------
19 """ONAP Common Logging library in Python."""
22 # -*- indent-tabs-mode: nil -*- vi: set expandtab:
25 from __future__ import print_function
26 import os, sys, getopt, logging, logging.handlers, time, re, uuid, socket, threading
29 """ONAP Common Logging object.
46 DateFmt = '%Y-%m-%dT%H:%M:%S'
49 def __init__(self, configFile, logKey, **kwargs):
50 """Construct a Common Logger for one Log File.
53 configFile -- configuration filename.
54 logKey -- the keyword in configFile that identifies the log filename.
56 Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
57 style -- the log file format (style) to use when writing log messages,
58 one of CommonLogger.ErrorFile, CommonLogger.DebugFile,
59 CommonLogger.AuditFile and CommonLogger.MetricsFile, or
60 one of the strings "error", "debug", "audit" or "metrics".
61 May also be set in the config file using a field named
62 <logKey>Style (where <logKey> is the value of the logKey
63 parameter). The keyword value overrides the value in the
65 requestID (dame) -- optional default value for this log record field.
66 serviceInstanceID (am) -- optional default value for this log record field.
67 threadID (am) -- optional default value for this log record field.
68 serverName (am) -- optional default value for this log record field.
69 serviceName (am) -- optional default value for this log record field.
70 instanceUUID (am) -- optional default value for this log record field.
71 severity (am) -- optional default value for this log record field.
72 serverIPAddress (am) -- optional default value for this log record field.
73 server (am) -- optional default value for this log record field.
74 IPAddress (am) -- optional default value for this log record field.
75 className (am) -- optional default value for this log record field.
76 timer (am) -- (ElapsedTime) optional default value for this log record field.
77 partnerName (ame) -- optional default value for this log record field.
78 targetEntity (me) -- optional default value for this log record field.
79 targetServiceName (me) -- optional default value for this log record field.
80 statusCode (am) -- optional default value for this log record field.
81 responseCode (am) -- optional default value for this log record field.
82 responseDescription (am) -- optional default value for this log record field.
83 processKey (am) -- optional default value for this log record field.
84 targetVirtualEntity (m) -- optional default value for this log record field.
85 customField1 (am) -- optional default value for this log record field.
86 customField2 (am) -- optional default value for this log record field.
87 customField3 (am) -- optional default value for this log record field.
88 customField4 (am) -- optional default value for this log record field.
89 errorCategory (e) -- optional default value for this log record field.
90 errorCode (e) -- optional default value for this log record field.
91 errorDescription (e) -- optional default value for this log record field.
93 Note: the pipe '|' character is not allowed in any log record field.
96 self._monitorFlag = False
98 # Get configuration parameters
99 self._logKey = str(logKey)
100 self._configFile = str(configFile)
101 self._rotateMethod = 'time'
102 self._timeRotateIntervalType = 'midnight'
103 self._timeRotateInterval = 1
104 self._sizeMaxBytes = 0
105 self._sizeRotateMode = 'a'
106 self._socketHost = None
108 self._typeLogger = 'filelogger'
109 self._backupCount = 6
110 self._logLevelThreshold = self._intLogLevel('')
115 self._fields["style"] = CommonLogger.UnknownFile
117 self._configFileModified = os.path.getmtime(self._configFile)
118 for line in open(self._configFile):
119 line = line.split('#',1)[0] # remove comments
121 key, value = [x.strip() for x in line.split('=',1)]
122 if key == 'rotateMethod' and value.lower() in ['time', 'size', 'none']:
123 self._rotateMethod = value.lower()
124 elif key == 'timeRotateIntervalType' and value in ['S', 'M', 'H', 'D', 'W0', 'W1', 'W2', 'W3', 'W4', 'W5', 'W6', 'midnight']:
125 self._timeRotateIntervalType = value
126 elif key == 'timeRotateInterval' and int( value ) > 0:
127 self._timeRotateInterval = int( value )
128 elif key == 'sizeMaxBytes' and int( value ) >= 0:
129 self._sizeMaxBytes = int( value )
130 elif key == 'sizeRotateMode' and value in ['a']:
131 self._sizeRotateMode = value
132 elif key == 'backupCount' and int( value ) >= 0:
133 self._backupCount = int( value )
134 elif key == self._logKey + 'SocketHost':
135 self._socketHost = value
136 elif key == self._logKey + 'SocketPort' and int( value ) == 0:
137 self._socketPort = int( value )
138 elif key == self._logKey + 'LogType' and value.lower() in ['filelogger', 'stdoutlogger', 'stderrlogger', 'socketlogger', 'nulllogger']:
139 self._typeLogger = value.lower()
140 elif key == self._logKey + 'LogLevel':
141 self._logLevelThreshold = self._intLogLevel(value.upper())
142 elif key == self._logKey + 'Style':
143 self._fields["style"] = value
144 elif key == self._logKey:
145 self._logFile = value
146 except Exception as x:
147 print("exception reading '%s' configuration file: %s" %(self._configFile, str(x)), file=sys.stderr)
150 print("exception reading '%s' configuration file" %(self._configFile), file=sys.stderr)
153 if self._logFile is None:
154 print('configuration file %s is missing definition %s for log file' %(self._configFile, self._logKey), file=sys.stderr)
158 # initialize default log fields
159 # timestamp will automatically be generated
160 for key in ['style', 'requestID', 'serviceInstanceID', 'threadID', 'serverName', 'serviceName', 'instanceUUID', \
161 'severity', 'serverIPAddress', 'server', 'IPAddress', 'className', 'timer', \
162 'partnerName', 'targetEntity', 'targetServiceName', 'statusCode', 'responseCode', \
163 'responseDescription', 'processKey', 'targetVirtualEntity', 'customField1', \
164 'customField2', 'customField3', 'customField4', 'errorCategory', 'errorCode', \
165 'errorDescription' ]:
166 if key in kwargs and kwargs[key] != None:
167 self._fields[key] = kwargs[key]
169 self._resetStyleField()
172 self._logLock = threading.Lock()
174 self._logger = logging.getLogger(self._logKey)
175 self._logger.propagate = False
178 self._defaultServerInfo()
180 # spawn a thread to monitor configFile for logLevel and logFile changes
181 self._monitorFlag = True
182 self._monitorThread = threading.Thread(target=self._monitorConfigFile, args=())
183 self._monitorThread.daemon = True
184 self._monitorThread.start()
187 def _createLogger(self):
188 if self._typeLogger == 'filelogger':
189 self._mkdir_p(self._logFile)
190 if self._rotateMethod == 'time':
191 self._logHandler = logging.handlers.TimedRotatingFileHandler(self._logFile, \
192 when=self._timeRotateIntervalType, interval=self._timeRotateInterval, \
193 backupCount=self._backupCount, encoding=None, delay=False, utc=True)
194 elif self._rotateMethod == 'size':
195 self._logHandler = logging.handlers.RotatingFileHandler(self._logFile, \
196 mode=self._sizeRotateMode, maxBytes=self._sizeMaxBytes, \
197 backupCount=self._backupCount, encoding=None, delay=False)
200 self._logHandler = logging.handlers.WatchedFileHandler(self._logFile, \
201 mode=self._sizeRotateMode, \
202 encoding=None, delay=False)
203 elif self._typeLogger == 'stderrlogger':
204 self._logHandler = logging.handlers.StreamHandler(sys.stderr)
205 elif self._typeLogger == 'stdoutlogger':
206 self._logHandler = logging.handlers.StreamHandler(sys.stdout)
207 elif self._typeLogger == 'socketlogger':
208 self._logHandler = logging.handlers.SocketHandler(self._socketHost, self._socketPort)
209 elif self._typeLogger == 'nulllogger':
210 self._logHandler = logging.handlers.NullHandler()
212 if self._fields["style"] == CommonLogger.AuditFile or self._fields["style"] == CommonLogger.MetricsFile:
213 self._logFormatter = logging.Formatter(fmt='%(begtime)s,%(begmsecs)03d+00:00|%(endtime)s,%(endmsecs)03d+00:00|%(message)s', datefmt=CommonLogger.DateFmt)
215 self._logFormatter = logging.Formatter(fmt='%(asctime)s,%(msecs)03d+00:00|%(message)s', datefmt='%Y-%m-%dT%H:%M:%S')
216 self._logFormatter.converter = time.gmtime
217 self._logHandler.setFormatter(self._logFormatter)
218 self._logger.addHandler(self._logHandler)
220 def _resetStyleField(self):
221 styleFields = ["error", "debug", "audit", "metrics"]
222 if self._fields['style'] in styleFields:
223 self._fields['style'] = styleFields.index(self._fields['style'])
226 if self._monitorFlag == False:
229 self._monitorFlag = False
231 if self._monitorThread is not None and self._monitorThread.is_alive():
232 self._monitorThread.join()
234 self._monitorThread = None
237 def _defaultServerInfo(self):
239 # If not set or purposely set = None, then set default
240 if self._fields.get('server') is None:
242 self._fields['server'] = socket.getfqdn()
243 except Exception as err:
245 self._fields['server'] = socket.gethostname()
246 except Exception as err:
247 self._fields['server'] = ""
249 # If not set or purposely set = None, then set default
250 if self._fields.get('serverIPAddress') is None:
252 self._fields['serverIPAddress'] = socket.gethostbyname(self._fields['server'])
253 except Exception as err:
254 self._fields['serverIPAddress'] = ""
257 def _monitorConfigFile(self):
258 while self._monitorFlag:
260 fileTime = os.path.getmtime(self._configFile)
261 if fileTime > self._configFileModified:
262 self._configFileModified = fileTime
263 ReopenLogFile = False
264 logFile = self._logFile
265 with open(self._configFile) as fp:
267 line = line.split('#',1)[0] # remove comments
269 key, value = [x.strip() for x in line.split('=',1)]
270 if key == 'rotateMethod' and value.lower() in ['time', 'size', 'none'] and self._rotateMethod != value:
271 self._rotateMethod = value.lower()
273 elif key == 'timeRotateIntervalType' and value in ['S', 'M', 'H', 'D', 'W0', 'W1', 'W2', 'W3', 'W4', 'W5', 'W6', 'midnight']:
274 self._timeRotateIntervalType = value
276 elif key == 'timeRotateInterval' and int( value ) > 0:
277 self._timeRotateInterval = int( value )
279 elif key == 'sizeMaxBytes' and int( value ) >= 0:
280 self._sizeMaxBytes = int( value )
282 elif key == 'sizeRotateMode' and value in ['a']:
283 self._sizeRotateMode = value
285 elif key == 'backupCount' and int( value ) >= 0:
286 self._backupCount = int( value )
288 elif key == self._logKey + 'SocketHost' and self._socketHost != value:
289 self._socketHost = value
291 elif key == self._logKey + 'SocketPort' and self._socketPort > 0 and self._socketPort != int( value ):
292 self._socketPort = int( value )
294 elif key == self._logKey + 'LogLevel' and self._logLevelThreshold != self._intLogLevel( value.upper() ):
295 self._logLevelThreshold = self._intLogLevel(value.upper())
296 elif key == self._logKey + 'LogType' and self._typeLogger != value and value.lower() in ['filelogger', 'stdoutlogger', 'stderrlogger', 'socketlogger', 'nulllogger']:
297 self._typeLogger = value.lower()
299 elif key == self._logKey + 'Style':
300 self._fields["style"] = value
301 self._resetStyleField()
302 elif key == self._logKey and self._logFile != value:
307 self._logger.removeHandler(self._logHandler)
308 self._logFile = logFile
310 except Exception as err:
316 def setFields(self, **kwargs):
317 """Set default values for log fields.
319 Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
320 style -- the log file format (style) to use when writing log messages
321 requestID (dame) -- optional default value for this log record field.
322 serviceInstanceID (am) -- optional default value for this log record field.
323 threadID (am) -- optional default value for this log record field.
324 serverName (am) -- optional default value for this log record field.
325 serviceName (am) -- optional default value for this log record field.
326 instanceUUID (am) -- optional default value for this log record field.
327 severity (am) -- optional default value for this log record field.
328 serverIPAddress (am) -- optional default value for this log record field.
329 server (am) -- optional default value for this log record field.
330 IPAddress (am) -- optional default value for this log record field.
331 className (am) -- optional default value for this log record field.
332 timer (am) -- (ElapsedTime) optional default value for this log record field.
333 partnerName (ame) -- optional default value for this log record field.
334 targetEntity (me) -- optional default value for this log record field.
335 targetServiceName (me) -- optional default value for this log record field.
336 statusCode (am) -- optional default value for this log record field.
337 responseCode (am) -- optional default value for this log record field.
338 responseDescription (am) -- optional default value for this log record field.
339 processKey (am) -- optional default value for this log record field.
340 targetVirtualEntity (m) -- optional default value for this log record field.
341 customField1 (am) -- optional default value for this log record field.
342 customField2 (am) -- optional default value for this log record field.
343 customField3 (am) -- optional default value for this log record field.
344 customField4 (am) -- optional default value for this log record field.
345 errorCategory (e) -- optional default value for this log record field.
346 errorCode (e) -- optional default value for this log record field.
347 errorDescription (e) -- optional default value for this log record field.
349 Note: the pipe '|' character is not allowed in any log record field.
352 for key in ['style', 'requestID', 'serviceInstanceID', 'threadID', 'serverName', 'serviceName', 'instanceUUID', \
353 'severity', 'serverIPAddress', 'server', 'IPAddress', 'className', 'timer', \
354 'partnerName', 'targetEntity', 'targetServiceName', 'statusCode', 'responseCode', \
355 'responseDescription', 'processKey', 'targetVirtualEntity', 'customField1', \
356 'customField2', 'customField3', 'customField4', 'errorCategory', 'errorCode', \
357 'errorDescription' ]:
359 if kwargs[key] != None:
360 self._fields[key] = kwargs[key]
361 elif key in self._fields:
362 del self._fields[key]
364 self._defaultServerInfo()
367 def debug(self, message, **kwargs):
368 """Write a DEBUG level message to the log file.
371 message -- value for the last log record field.
373 Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
374 style -- the log file format (style) to use when writing log messages
375 requestID (dame) -- optional default value for this log record field.
376 serviceInstanceID (am) -- optional default value for this log record field.
377 threadID (am) -- optional default value for this log record field.
378 serverName (am) -- optional default value for this log record field.
379 serviceName (am) -- optional default value for this log record field.
380 instanceUUID (am) -- optional default value for this log record field.
381 severity (am) -- optional default value for this log record field.
382 serverIPAddress (am) -- optional default value for this log record field.
383 server (am) -- optional default value for this log record field.
384 IPAddress (am) -- optional default value for this log record field.
385 className (am) -- optional default value for this log record field.
386 timer (am) -- (ElapsedTime) optional default value for this log record field.
387 partnerName (ame) -- optional default value for this log record field.
388 targetEntity (me) -- optional default value for this log record field.
389 targetServiceName (me) -- optional default value for this log record field.
390 statusCode (am) -- optional default value for this log record field.
391 responseCode (am) -- optional default value for this log record field.
392 responseDescription (am) -- optional default value for this log record field.
393 processKey (am) -- optional default value for this log record field.
394 targetVirtualEntity (m) -- optional default value for this log record field.
395 customField1 (am) -- optional default value for this log record field.
396 customField2 (am) -- optional default value for this log record field.
397 customField3 (am) -- optional default value for this log record field.
398 customField4 (am) -- optional default value for this log record field.
399 errorCategory (e) -- optional default value for this log record field.
400 errorCode (e) -- optional default value for this log record field.
401 errorDescription (e) -- optional default value for this log record field.
403 Note: the pipe '|' character is not allowed in any log record field.
406 self._log('DEBUG', message, errorCategory = 'DEBUG', **kwargs)
408 def info(self, message, **kwargs):
409 """Write an INFO level message to the log file.
412 message -- value for the last log record field.
414 Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
415 style -- the log file format (style) to use when writing log messages
416 requestID (dame) -- optional default value for this log record field.
417 serviceInstanceID (am) -- optional default value for this log record field.
418 threadID (am) -- optional default value for this log record field.
419 serverName (am) -- optional default value for this log record field.
420 serviceName (am) -- optional default value for this log record field.
421 instanceUUID (am) -- optional default value for this log record field.
422 severity (am) -- optional default value for this log record field.
423 serverIPAddress (am) -- optional default value for this log record field.
424 server (am) -- optional default value for this log record field.
425 IPAddress (am) -- optional default value for this log record field.
426 className (am) -- optional default value for this log record field.
427 timer (am) -- (ElapsedTime) optional default value for this log record field.
428 partnerName (ame) -- optional default value for this log record field.
429 targetEntity (me) -- optional default value for this log record field.
430 targetServiceName (me) -- optional default value for this log record field.
431 statusCode (am) -- optional default value for this log record field.
432 responseCode (am) -- optional default value for this log record field.
433 responseDescription (am) -- optional default value for this log record field.
434 processKey (am) -- optional default value for this log record field.
435 targetVirtualEntity (m) -- optional default value for this log record field.
436 customField1 (am) -- optional default value for this log record field.
437 customField2 (am) -- optional default value for this log record field.
438 customField3 (am) -- optional default value for this log record field.
439 customField4 (am) -- optional default value for this log record field.
440 errorCategory (e) -- optional default value for this log record field.
441 errorCode (e) -- optional default value for this log record field.
442 errorDescription (e) -- optional default value for this log record field.
444 Note: the pipe '|' character is not allowed in any log record field.
447 self._log('INFO', message, errorCategory = 'INFO', **kwargs)
449 def warn(self, message, **kwargs):
450 """Write a WARN level message to the log file.
453 message -- value for the last log record field.
455 Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
456 style -- the log file format (style) to use when writing log messages
457 requestID (dame) -- optional default value for this log record field.
458 serviceInstanceID (am) -- optional default value for this log record field.
459 threadID (am) -- optional default value for this log record field.
460 serverName (am) -- optional default value for this log record field.
461 serviceName (am) -- optional default value for this log record field.
462 instanceUUID (am) -- optional default value for this log record field.
463 severity (am) -- optional default value for this log record field.
464 serverIPAddress (am) -- optional default value for this log record field.
465 server (am) -- optional default value for this log record field.
466 IPAddress (am) -- optional default value for this log record field.
467 className (am) -- optional default value for this log record field.
468 timer (am) -- (ElapsedTime) optional default value for this log record field.
469 partnerName (ame) -- optional default value for this log record field.
470 targetEntity (me) -- optional default value for this log record field.
471 targetServiceName (me) -- optional default value for this log record field.
472 statusCode (am) -- optional default value for this log record field.
473 responseCode (am) -- optional default value for this log record field.
474 responseDescription (am) -- optional default value for this log record field.
475 processKey (am) -- optional default value for this log record field.
476 targetVirtualEntity (m) -- optional default value for this log record field.
477 customField1 (am) -- optional default value for this log record field.
478 customField2 (am) -- optional default value for this log record field.
479 customField3 (am) -- optional default value for this log record field.
480 customField4 (am) -- optional default value for this log record field.
481 errorCategory (e) -- optional default value for this log record field.
482 errorCode (e) -- optional default value for this log record field.
483 errorDescription (e) -- optional default value for this log record field.
485 Note: the pipe '|' character is not allowed in any log record field.
488 self._log('WARN', message, errorCategory = 'WARN', **kwargs)
490 def error(self, message, **kwargs):
491 """Write an ERROR level message to the log file.
494 message -- value for the last log record field.
496 Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
497 style -- the log file format (style) to use when writing log messages
498 requestID (dame) -- optional default value for this log record field.
499 serviceInstanceID (am) -- optional default value for this log record field.
500 threadID (am) -- optional default value for this log record field.
501 serverName (am) -- optional default value for this log record field.
502 serviceName (am) -- optional default value for this log record field.
503 instanceUUID (am) -- optional default value for this log record field.
504 severity (am) -- optional default value for this log record field.
505 serverIPAddress (am) -- optional default value for this log record field.
506 server (am) -- optional default value for this log record field.
507 IPAddress (am) -- optional default value for this log record field.
508 className (am) -- optional default value for this log record field.
509 timer (am) -- (ElapsedTime) optional default value for this log record field.
510 partnerName (ame) -- optional default value for this log record field.
511 targetEntity (me) -- optional default value for this log record field.
512 targetServiceName (me) -- optional default value for this log record field.
513 statusCode (am) -- optional default value for this log record field.
514 responseCode (am) -- optional default value for this log record field.
515 responseDescription (am) -- optional default value for this log record field.
516 processKey (am) -- optional default value for this log record field.
517 targetVirtualEntity (m) -- optional default value for this log record field.
518 customField1 (am) -- optional default value for this log record field.
519 customField2 (am) -- optional default value for this log record field.
520 customField3 (am) -- optional default value for this log record field.
521 customField4 (am) -- optional default value for this log record field.
522 errorCategory (e) -- optional default value for this log record field.
523 errorCode (e) -- optional default value for this log record field.
524 errorDescription (e) -- optional default value for this log record field.
526 Note: the pipe '|' character is not allowed in any log record field.
529 self._log('ERROR', message, errorCategory = 'ERROR', **kwargs)
531 def fatal(self, message, **kwargs):
532 """Write a FATAL level message to the log file.
535 message -- value for the last log record field.
537 Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
538 style -- the log file format (style) to use when writing log messages
539 requestID (dame) -- optional default value for this log record field.
540 serviceInstanceID (am) -- optional default value for this log record field.
541 threadID (am) -- optional default value for this log record field.
542 serverName (am) -- optional default value for this log record field.
543 serviceName (am) -- optional default value for this log record field.
544 instanceUUID (am) -- optional default value for this log record field.
545 severity (am) -- optional default value for this log record field.
546 serverIPAddress (am) -- optional default value for this log record field.
547 server (am) -- optional default value for this log record field.
548 IPAddress (am) -- optional default value for this log record field.
549 className (am) -- optional default value for this log record field.
550 timer (am) -- (ElapsedTime) optional default value for this log record field.
551 partnerName (ame) -- optional default value for this log record field.
552 targetEntity (me) -- optional default value for this log record field.
553 targetServiceName (me) -- optional default value for this log record field.
554 statusCode (am) -- optional default value for this log record field.
555 responseCode (am) -- optional default value for this log record field.
556 responseDescription (am) -- optional default value for this log record field.
557 processKey (am) -- optional default value for this log record field.
558 targetVirtualEntity (m) -- optional default value for this log record field.
559 customField1 (am) -- optional default value for this log record field.
560 customField2 (am) -- optional default value for this log record field.
561 customField3 (am) -- optional default value for this log record field.
562 customField4 (am) -- optional default value for this log record field.
563 errorCategory (e) -- optional default value for this log record field.
564 errorCode (e) -- optional default value for this log record field.
565 errorDescription (e) -- optional default value for this log record field.
567 Note: the pipe '|' character is not allowed in any log record field.
570 self._log('FATAL', message, errorCategory = 'FATAL', **kwargs)
572 def _log(self, logLevel, message, **kwargs):
573 """Write a message to the log file.
576 logLevel -- value ('DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL', ...) for the log record.
577 message -- value for the last log record field.
579 Keyword arguments: Annotations are d:debug, a=audit, m=metrics, e=error
580 style -- the log file format (style) to use when writing log messages
581 requestID (dame) -- optional default value for this log record field.
582 serviceInstanceID (am) -- optional default value for this log record field.
583 threadID (am) -- optional default value for this log record field.
584 serverName (am) -- optional default value for this log record field.
585 serviceName (am) -- optional default value for this log record field.
586 instanceUUID (am) -- optional default value for this log record field.
587 severity (am) -- optional default value for this log record field.
588 serverIPAddress (am) -- optional default value for this log record field.
589 server (am) -- optional default value for this log record field.
590 IPAddress (am) -- optional default value for this log record field.
591 className (am) -- optional default value for this log record field.
592 timer (am) -- (ElapsedTime) optional default value for this log record field.
593 partnerName (ame) -- optional default value for this log record field.
594 targetEntity (me) -- optional default value for this log record field.
595 targetServiceName (me) -- optional default value for this log record field.
596 statusCode (am) -- optional default value for this log record field.
597 responseCode (am) -- optional default value for this log record field.
598 responseDescription (am) -- optional default value for this log record field.
599 processKey (am) -- optional default value for this log record field.
600 targetVirtualEntity (m) -- optional default value for this log record field.
601 customField1 (am) -- optional default value for this log record field.
602 customField2 (am) -- optional default value for this log record field.
603 customField3 (am) -- optional default value for this log record field.
604 customField4 (am) -- optional default value for this log record field.
605 errorCategory (e) -- optional default value for this log record field.
606 errorCode (e) -- optional default value for this log record field.
607 errorDescription (e) -- optional default value for this log record field.
609 Note: the pipe '|' character is not allowed in any log record field.
612 # timestamp will automatically be inserted
613 style = int(self._getVal('style', '', **kwargs))
614 requestID = self._getVal('requestID', '', **kwargs)
615 serviceInstanceID = self._getVal('serviceInstanceID', '', **kwargs)
616 threadID = self._getVal('threadID', threading.currentThread().getName(), **kwargs)
617 serverName = self._getVal('serverName', '', **kwargs)
618 serviceName = self._getVal('serviceName', '', **kwargs)
619 instanceUUID = self._getVal('instanceUUID', '', **kwargs)
620 upperLogLevel = self._noSep(logLevel.upper())
621 severity = self._getVal('severity', '', **kwargs)
622 serverIPAddress = self._getVal('serverIPAddress', '', **kwargs)
623 server = self._getVal('server', '', **kwargs)
624 IPAddress = self._getVal('IPAddress', '', **kwargs)
625 className = self._getVal('className', '', **kwargs)
626 timer = self._getVal('timer', '', **kwargs)
627 partnerName = self._getVal('partnerName', '', **kwargs)
628 targetEntity = self._getVal('targetEntity', '', **kwargs)
629 targetServiceName = self._getVal('targetServiceName', '', **kwargs)
630 statusCode = self._getVal('statusCode', '', **kwargs)
631 responseCode = self._getVal('responseCode', '', **kwargs)
632 responseDescription = self._noSep(self._getVal('responseDescription', '', **kwargs))
633 processKey = self._getVal('processKey', '', **kwargs)
634 targetVirtualEntity = self._getVal('targetVirtualEntity', '', **kwargs)
635 customField1 = self._getVal('customField1', '', **kwargs)
636 customField2 = self._getVal('customField2', '', **kwargs)
637 customField3 = self._getVal('customField3', '', **kwargs)
638 customField4 = self._getVal('customField4', '', **kwargs)
639 errorCategory = self._getVal('errorCategory', '', **kwargs)
640 errorCode = self._getVal('errorCode', '', **kwargs)
641 errorDescription = self._noSep(self._getVal('errorDescription', '', **kwargs))
643 detailMessage = self._noSep(message)
644 if bool(re.match(r" *$", detailMessage)):
645 return # don't log empty messages
647 useLevel = self._intLogLevel(upperLogLevel)
648 if CommonLogger.verbose: print("logger STYLE=%s" % style)
649 if useLevel < self._logLevelThreshold:
650 if CommonLogger.verbose: print("skipping because of level")
654 if style == CommonLogger.ErrorFile:
655 if CommonLogger.verbose: print("using CommonLogger.ErrorFile")
656 self._logger.log(50, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' \
657 %(requestID, threadID, serviceName, partnerName, targetEntity, targetServiceName,
658 errorCategory, errorCode, errorDescription, detailMessage))
659 elif style == CommonLogger.DebugFile:
660 if CommonLogger.verbose: print("using CommonLogger.DebugFile")
661 self._logger.log(50, '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s' \
662 %(requestID, threadID, serverName, serviceName, instanceUUID, upperLogLevel,
663 severity, serverIPAddress, server, IPAddress, className, timer, detailMessage))
664 elif style == CommonLogger.AuditFile:
665 if CommonLogger.verbose: print("using CommonLogger.AuditFile")
666 endAuditTime, endAuditMsec = self._getTime()
667 if self._begTime is not None:
668 d = { 'begtime': self._begTime, 'begmsecs': self._begMsec, 'endtime': endAuditTime, 'endmsecs': endAuditMsec }
670 d = { 'begtime': endAuditTime, 'begmsecs': endAuditMsec, 'endtime': endAuditTime, 'endmsecs': endAuditMsec }
673 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' \
674 %(requestID, serviceInstanceID, threadID, serverName, serviceName, partnerName,
675 statusCode, responseCode, responseDescription, instanceUUID, upperLogLevel,
676 severity, serverIPAddress, timer, server, IPAddress, className, unused,
677 processKey, customField1, customField2, customField3, customField4, detailMessage), extra=d)
678 elif style == CommonLogger.MetricsFile:
679 if CommonLogger.verbose: print("using CommonLogger.MetricsFile")
680 endMetricsTime, endMetricsMsec = self._getTime()
681 if self._begTime is not None:
682 d = { 'begtime': self._begTime, 'begmsecs': self._begMsec, 'endtime': endMetricsTime, 'endmsecs': endMetricsMsec }
684 d = { 'begtime': endMetricsTime, 'begmsecs': endMetricsMsec, 'endtime': endMetricsTime, 'endmsecs': endMetricsMsec }
687 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' \
688 %(requestID, serviceInstanceID, threadID, serverName, serviceName, partnerName,
689 targetEntity, targetServiceName, statusCode, responseCode, responseDescription,
690 instanceUUID, upperLogLevel, severity, serverIPAddress, timer, server, IPAddress,
691 className, unused, processKey, targetVirtualEntity, customField1, customField2,
692 customField3, customField4, detailMessage), extra=d)
694 print("!!!!!!!!!!!!!!!! style not set: %s" % self._fields["style"])
698 lt = time.localtime(ct)
699 return (time.strftime(CommonLogger.DateFmt, lt), (ct - int(ct)) * 1000)
701 def setStartRecordEvent(self):
703 Set the start time to be saved for both audit and metrics records
705 self._begTime, self._begMsec = self._getTime()
707 def _getVal(self, key, default, **kwargs):
708 val = self._fields.get(key)
709 if key in kwargs: val = kwargs[key]
710 if val is None: val = default
711 return self._noSep(val)
713 def _noSep(self, message):
714 if message is None: return ''
715 return re.sub(r'[\|\n]', ' ', str(message))
717 def _intLogLevel(self, logLevel):
718 if logLevel == 'FATAL': useLevel = 50
719 elif logLevel == 'ERROR': useLevel = 40
720 elif logLevel == 'WARN': useLevel = 30
721 elif logLevel == 'INFO': useLevel = 20
722 elif logLevel == 'DEBUG': useLevel = 10
726 def _mkdir_p(self, filename):
727 """Create missing directories from a full filename path like mkdir -p"""
732 folder=os.path.dirname(filename)
737 if not os.path.exists(folder):
740 except OSError as err:
741 print("error number %d creating %s directory to hold %s logfile: %s" %(err.errno, err.filename, filename, err.strerror), file=sys.stderr)
743 except Exception as err:
744 print("error creating %s directory to hold %s logfile: %s" %(folder, filename, str(err)), file=sys.stderr)
747 def __checkTime1(line):
748 format = r'[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9],[0-9][0-9][0-9][+]00:00[|]'
749 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[|]'
750 m = re.match(format, line)
752 print("ERROR: time string did not match proper time format, %s" %line)
753 print("\t: format=%s" % format)
757 def __checkTime2(line, different):
758 format = '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:([0-9][0-9]),([0-9][0-9][0-9])[+]00:00[|][0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:([0-9][0-9]),([0-9][0-9][0-9])[+]00:00[|]'
759 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[|]'
760 m = re.match(format, line)
762 print("ERROR: time strings did not match proper time format, %s" %line)
763 print("\t: format=%s" % format)
765 second1 = int(m.group(1))
766 msec1 = int(m.group(2))
767 second2 = int(m.group(3))
768 msec2 = int(m.group(4))
769 if second1 > second2: second2 += 60
770 t1 = second1 * 1000 + msec1
771 t2 = second2 * 1000 + msec2
773 # print("t1=%d (%d,%d) t2=%d (%d,%d), diff = %d" % (t1, second1, msec1, t2, second2, msec2, diff))
776 print("ERROR: times did not differ enough: %s" % line)
780 print("ERROR: times were too far apart: %s" % line)
784 def __checkLog(logfile, numLines, numFields):
787 with open(logfile, "r") as fp:
789 # print("saw line %s" % line)
793 print("ERROR: wrong number of fields. Expected %d, got %d: %s" % (numFields, c, line))
795 if re.search("should not appear", line):
796 print("ERROR: a line appeared that should not have appeared, %s" % line)
798 elif re.search("single time", line):
799 errorCount += __checkTime1(line)
800 elif re.search("time should be the same", line):
801 errorCount += __checkTime2(line, different=False)
802 elif re.search("time should be different", line):
803 errorCount += __checkTime2(line, different=True)
805 print("ERROR: an unknown message appeared, %s" % line)
808 if lineCount != numLines:
809 print("ERROR: expected %d lines, but got %d lines" % (numLines, lineCount))
813 if __name__ == "__main__":
815 parser = argparse.ArgumentParser(description="test the CommonLogger functions")
816 parser.add_argument("-k", "--keeplogs", help="Keep the log files after finishing the tests", action="store_true")
817 parser.add_argument("-v", "--verbose", help="Print debugging messages", action="store_true")
818 args = parser.parse_args()
820 spid = str(os.getpid())
823 logcfg = "/tmp/cl.log" + spid + ".cfg"
824 errorLog = "/tmp/cl.error" + spid + ".log"
825 metricsLog = "/tmp/cl.metrics" + spid + ".log"
826 auditLog = "/tmp/cl.audit" + spid + ".log"
827 debugLog = "/tmp/cl.debug" + spid + ".log"
828 if args.verbose: CommonLogger.verbose = True
832 for f in [ logcfg, errorLog, metricsLog, auditLog, debugLog ]:
837 if not args.keeplogs:
838 atexit.register(cleanupTmps)
840 with open(logcfg, "w") as o:
841 o.write("error = " + errorLog + "\n" +
842 "errorLogLevel = WARN\n" +
843 "metrics = " + metricsLog + "\n" +
844 "metricsLogLevel = INFO\n" +
845 "audit = " + auditLog + "\n" +
846 "auditLogLevel = INFO\n" +
847 "debug = " + debugLog + "\n" +
848 "debugLogLevel = DEBUG\n")
851 instanceUUID = uuid.uuid1()
852 serviceName = "testharness"
853 errorLogger = CommonLogger(logcfg, "error", style=CommonLogger.ErrorFile, instanceUUID=instanceUUID, serviceName=serviceName)
854 debugLogger = CommonLogger(logcfg, "debug", style=CommonLogger.DebugFile, instanceUUID=instanceUUID, serviceName=serviceName)
855 auditLogger = CommonLogger(logcfg, "audit", style=CommonLogger.AuditFile, instanceUUID=instanceUUID, serviceName=serviceName)
856 metricsLogger = CommonLogger(logcfg, "metrics", style=CommonLogger.MetricsFile, instanceUUID=instanceUUID, serviceName=serviceName)
860 errorLogger.debug("error calling debug (should not appear)")
861 errorLogger.info("error calling info (should not appear)")
862 errorLogger.warn("error calling warn (single time)")
863 errorLogger.error("error calling error (single time)")
864 errorLogger.setStartRecordEvent()
866 errorLogger.fatal("error calling fatal, after setStartRecordEvent and sleep (start should be ignored, single time)")
868 errorCount += __checkLog(errorLog, 3, 10)
870 auditLogger.debug("audit calling debug (should not appear)")
871 auditLogger.info("audit calling info (time should be the same)")
872 auditLogger.warn("audit calling warn (time should be the same)")
873 auditLogger.error("audit calling error (time should be the same)")
874 auditLogger.setStartRecordEvent()
876 auditLogger.fatal("audit calling fatal, after setStartRecordEvent and sleep, time should be different)")
878 errorCount += __checkLog(auditLog, 4, 25)
880 debugLogger.debug("debug calling debug (single time)")
881 debugLogger.info("debug calling info (single time)")
882 debugLogger.warn("debug calling warn (single time)")
883 debugLogger.setStartRecordEvent()
885 debugLogger.error("debug calling error, after SetStartRecordEvent and sleep (start should be ignored, single time)")
886 debugLogger.fatal("debug calling fatal (single time)")
887 errorCount += __checkLog(debugLog, 5, 13)
890 metricsLogger.debug("metrics calling debug (should not appear)")
891 metricsLogger.info("metrics calling info (time should be the same)")
892 metricsLogger.warn("metrics calling warn (time should be the same)")
893 metricsLogger.setStartRecordEvent()
895 metricsLogger.error("metrics calling error, after SetStartRecordEvent and sleep, time should be different")
896 metricsLogger.fatal("metrics calling fatal (time should be the same)")
898 errorCount += __checkLog(metricsLog, 4, 28)
900 print("%d tests run, %d errors found" % (testsRun, errorCount))