-
-
Save okin/ef7941ff2623f80ab2818bd1733d2e67 to your computer and use it in GitHub Desktop.
OPSI.Logger with improved support for removing logs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python | |
# -*- coding: utf-8 -*- | |
# This file is part of python-opsi. | |
# Copyright (C) 2006-2016 uib GmbH <info@uib.de> | |
# This program is free software: you can redistribute it and/or modify | |
# it under the terms of the GNU Affero General Public License as | |
# published by the Free Software Foundation, either version 3 of the | |
# License, or (at your option) any later version. | |
# This program is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
# GNU Affero General Public License for more details. | |
# You should have received a copy of the GNU Affero General Public License | |
# along with this program. If not, see <http://www.gnu.org/licenses/>. | |
""" | |
opsi python library - Logger | |
:copyright: uib GmbH <info@uib.de> | |
:author: Jan Schneider <j.schneider@uib.de> | |
:author: Niko Wenselowski <n.wenselowski@uib.de> | |
:license: GNU Affero General Public License version 3 | |
""" | |
import codecs | |
import locale | |
import os | |
import sys | |
import threading | |
import time | |
import traceback | |
import warnings | |
try: | |
import thread | |
except ImportError: | |
# Python 3 | |
import _thread as thread | |
if os.name == 'nt': | |
# Windows imports for file locking | |
import win32con | |
import win32file | |
import pywintypes | |
elif os.name == 'posix': | |
# Posix imports for file locking | |
import fcntl | |
try: | |
import syslog | |
except ImportError: | |
syslog = None | |
__version__ = '4.0.7.6' | |
if sys.version_info > (3, ): | |
# Python 3 | |
unicode = str | |
_STRING_TYPE = str | |
_UNICODE_TYPE = str | |
else: | |
# Python 2 | |
_STRING_TYPE = str | |
_UNICODE_TYPE = unicode | |
# Loglevels | |
LOG_CONFIDENTIAL = 9 | |
LOG_DEBUG2 = 8 | |
LOG_DEBUG = 7 | |
LOG_INFO = 6 | |
LOG_NOTICE = 5 | |
LOG_WARNING = 4 | |
LOG_ERROR = 3 | |
LOG_CRITICAL = 2 | |
LOG_ESSENTIAL = 1 | |
LOG_COMMENT = LOG_ESSENTIAL | |
LOG_NONE = 0 | |
if os.name == 'posix': | |
COLOR_NORMAL = '\033[0;0;0m' | |
COLOR_BLACK = '\033[0;30;40m' | |
COLOR_RED = '\033[0;31;40m' | |
COLOR_GREEN = '\033[0;32;40m' | |
COLOR_YELLOW = '\033[0;33;40m' | |
COLOR_BLUE = '\033[0;34;40m' | |
COLOR_MAGENTA = '\033[0;35;40m' | |
COLOR_CYAN = '\033[0;36;40m' | |
COLOR_WHITE = '\033[0;37;40m' | |
COLOR_LIGHT_BLACK = '\033[1;30;40m' | |
COLOR_LIGHT_RED = '\033[1;31;40m' | |
COLOR_LIGHT_GREEN = '\033[1;32;40m' | |
COLOR_LIGHT_YELLOW = '\033[1;33;40m' | |
COLOR_LIGHT_BLUE = '\033[1;34;40m' | |
COLOR_LIGHT_MAGENTA = '\033[1;35;40m' | |
COLOR_LIGHT_CYAN = '\033[1;36;40m' | |
COLOR_LIGHT_WHITE = '\033[1;37;40m' | |
else: | |
COLOR_NORMAL = '' | |
COLOR_BLACK = '' | |
COLOR_RED = '' | |
COLOR_GREEN = '' | |
COLOR_YELLOW = '' | |
COLOR_BLUE = '' | |
COLOR_MAGENTA = '' | |
COLOR_CYAN = '' | |
COLOR_WHITE = '' | |
COLOR_LIGHT_BLACK = '' | |
COLOR_LIGHT_RED = '' | |
COLOR_LIGHT_GREEN = '' | |
COLOR_LIGHT_YELLOW = '' | |
COLOR_LIGHT_BLUE = '' | |
COLOR_LIGHT_MAGENTA = '' | |
COLOR_LIGHT_CYAN = '' | |
COLOR_LIGHT_WHITE = '' | |
COLORS_AVAILABLE = [ | |
COLOR_NORMAL, COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_YELLOW, | |
COLOR_BLUE, COLOR_MAGENTA, COLOR_CYAN, COLOR_WHITE, COLOR_LIGHT_BLACK, | |
COLOR_LIGHT_RED, COLOR_LIGHT_GREEN, COLOR_LIGHT_YELLOW, COLOR_LIGHT_BLUE, | |
COLOR_LIGHT_MAGENTA, COLOR_LIGHT_CYAN, COLOR_LIGHT_WHITE | |
] | |
DEBUG_COLOR = COLOR_WHITE | |
INFO_COLOR = COLOR_LIGHT_WHITE | |
NOTICE_COLOR = COLOR_GREEN | |
WARNING_COLOR = COLOR_YELLOW | |
ERROR_COLOR = COLOR_RED | |
CRITICAL_COLOR = COLOR_LIGHT_RED | |
CONFIDENTIAL_COLOR = COLOR_LIGHT_YELLOW | |
ESSENTIAL_COLOR = COLOR_LIGHT_CYAN | |
COMMENT_COLOR = ESSENTIAL_COLOR | |
if syslog is not None: | |
_SYSLOG_LEVEL_MAPPING = { | |
LOG_CONFIDENTIAL: syslog.LOG_DEBUG, | |
LOG_DEBUG2: syslog.LOG_DEBUG, | |
LOG_DEBUG: syslog.LOG_DEBUG, | |
LOG_INFO: syslog.LOG_INFO, | |
LOG_NOTICE: syslog.LOG_NOTICE, | |
LOG_WARNING: syslog.LOG_WARNING, | |
LOG_ERROR: syslog.LOG_ERR, | |
LOG_CRITICAL: syslog.LOG_CRIT, | |
LOG_COMMENT: syslog.LOG_CRIT | |
} | |
_LOGLEVEL_NAME_AND_COLOR_MAPPING = { | |
LOG_CONFIDENTIAL: (u'confidential', CONFIDENTIAL_COLOR), | |
LOG_DEBUG2: (u'debug2', DEBUG_COLOR), | |
LOG_DEBUG: (u'debug', DEBUG_COLOR), | |
LOG_INFO: (u'info', INFO_COLOR), | |
LOG_NOTICE: (u'notice', NOTICE_COLOR), | |
LOG_WARNING: (u'warning', WARNING_COLOR), | |
LOG_ERROR: (u'error', ERROR_COLOR), | |
LOG_CRITICAL: (u'critical', CRITICAL_COLOR), | |
LOG_ESSENTIAL: (u'essential', COMMENT_COLOR), | |
} | |
encoding = sys.getfilesystemencoding() | |
_showwarning = warnings.showwarning | |
def forceUnicode(var): | |
if isinstance(var, _UNICODE_TYPE): | |
return var | |
elif isinstance(var, _STRING_TYPE): | |
return unicode(var, 'utf-8', 'replace') | |
elif os.name == 'nt' and isinstance(var, WindowsError): | |
return u"[Error %s] %s" % (var.args[0], var.args[1].decode(encoding)) | |
try: | |
return var.__unicode__() | |
except Exception: | |
pass | |
try: | |
return unicode(var) | |
except Exception: | |
pass | |
try: | |
var = var.__repr__() | |
if isinstance(var, _UNICODE_TYPE): | |
return var | |
return unicode(var, 'utf-8', 'replace') | |
except Exception: | |
pass | |
return unicode(var) | |
class LoggerSubject: | |
def __init__(self): | |
self._observers = [] | |
self._message = u"" | |
self._severity = 0 | |
def getId(self): | |
return u'logger' | |
def getType(self): | |
return u'Logger' | |
def getClass(self): | |
return u'MessageSubject' | |
def setMessage(self, message, severity=0): | |
self._message = forceUnicode(message) | |
self._severity = severity | |
for o in self._observers: | |
o.messageChanged(self, message) | |
def getMessage(self): | |
return self._message | |
def getSeverity(self): | |
return self._severity | |
def attachObserver(self, observer): | |
if observer not in self._observers: | |
self._observers.append(observer) | |
def detachObserver(self, observer): | |
if observer in self._observers: | |
self._observers.remove(observer) | |
def serializable(self): | |
return { | |
"message": self.getMessage(), | |
"severity": self.getSeverity(), | |
"id": self.getId(), | |
"class": self.getClass(), | |
"type": self.getType() | |
} | |
class TwistedLogObserver(object): | |
def __init__(self, logger): | |
self._logger = logger | |
def emit(self, eventDict): | |
if eventDict.get('isError'): | |
if eventDict.get('failure'): | |
self._logger.logTraceback(eventDict['failure'].getTracebackObject()) | |
self._logger.critical(u" ==>>> %s" % eventDict['failure'].getErrorMessage()) | |
for line in eventDict.get('message', ()): | |
if "Can't find property" in line: | |
# Dav property errors | |
self._logger.debug(u"[twisted] %s" % line) | |
else: | |
self._logger.error(u"[twisted] %s" % line) | |
else: | |
for line in eventDict.get('message', ()): | |
self._logger.debug(u"[twisted] %s" % line) | |
class LoggerImplementation: | |
''' Implementation of the singleton interface ''' | |
def __init__(self, logFile=None): | |
self.__syslogLevel = LOG_NONE | |
self.__consoleLevel = LOG_NONE | |
self.__fileLevel = LOG_NONE | |
self.__messageSubjectLevel = LOG_NONE | |
self.__fileColor = False | |
self.__consoleColor = False | |
self.__logFile = logFile | |
self.__syslogFormat = u'%M' | |
self.__consoleFormat = u'%M' | |
self.__consoleStdout = False | |
self.__fileFormat = u'%D [%L] %M (%F|%N)' | |
self.__messageSubjectFormat = u'%M' | |
self.univentionLogger_priv = None | |
self.__univentionClass = None | |
self.__univentionFormat = u'opsi: %M' | |
self.__confidentialStrings = set() | |
self.__componentName = u'' | |
self.__threadConfig = {} | |
self.__objectConfig = {} | |
self.__stdout = VirtFile(self, LOG_NOTICE) | |
self.__stderr = VirtFile(self, LOG_ERROR) | |
self.__lock = threading.Lock() | |
self.__loggerSubject = LoggerSubject() | |
def getStderr(self): | |
return self.__stderr | |
def getStdout(self): | |
return self.__stdout | |
def setConfidentialStrings(self, strings): | |
if not isinstance(strings, (list, tuple)): | |
strings = [strings] | |
self.__confidentialStrings = set() | |
for string in strings: | |
self.addConfidentialString(string) | |
def addConfidentialString(self, string): | |
string = forceUnicode(string) | |
if not string: | |
raise ValueError(u"Cannot use empty string as confidential string") | |
self.__confidentialStrings.add(string) | |
def setLogFormat(self, format, currentThread=False, object=None): | |
self.setConsoleFormat(format, currentThread, object) | |
self.setSyslogFormat(format, currentThread, object) | |
self.setFileFormat(format, currentThread, object) | |
def setConsoleFormat(self, format, currentThread=False, object=None): | |
if currentThread: | |
self._setThreadConfig('consoleFormat', format) | |
elif object: | |
self._setObjectConfig(id(object), 'consoleFormat', format) | |
else: | |
self.__consoleFormat = format | |
def setComponentName(self, componentName, currentThread=False, object=None): | |
if currentThread: | |
self._setThreadConfig('componentName', componentName) | |
elif object: | |
self._setObjectConfig(id(object), 'componentName', componentName) | |
else: | |
self.__componentName = componentName | |
def logToStdout(self, stdout): | |
self.__consoleStdout = stdout | |
def setSyslogFormat(self, format, currentThread=False, object=None): | |
if currentThread: | |
self._setThreadConfig('syslogFormat', format) | |
elif object: | |
self._setObjectConfig(id(object), 'syslogFormat', format) | |
else: | |
self.__syslogFormat = format | |
def setFileFormat(self, format, currentThread=False, object=None): | |
if currentThread: | |
self._setThreadConfig('fileFormat', format) | |
elif object: | |
self._setObjectConfig(id(object), 'fileFormat', format) | |
else: | |
self.__fileFormat = format | |
def setUniventionFormat(self, format, currentThread=False, object=None): | |
if currentThread: | |
self._setThreadConfig('univentionFormat', format) | |
elif object: | |
self._setObjectConfig(id(object), 'univentionFormat', format) | |
else: | |
self.__univentionFormat = format | |
def setMessageSubjectFormat(self, format, currentThread=False, object=None): | |
if currentThread: | |
self._setThreadConfig('messageSubjectFormat', format) | |
elif object: | |
self._setObjectConfig(id(object), 'messageSubjectFormat', format) | |
else: | |
self.__messageSubjectFormat = format | |
def setUniventionLogger(self, logger): | |
self.univentionLogger_priv = logger | |
def setUniventionClass(self, c): | |
self.__univentionClass = c | |
def getMessageSubject(self): | |
return self.__loggerSubject | |
def setColor(self, color): | |
''' Enable or disable ansi color output ''' | |
self.__fileColor = self.__consoleColor = color | |
def setFileColor(self, color): | |
''' Enable or disable ansi color output ''' | |
self.__fileColor = color | |
def setConsoleColor(self, color): | |
''' Enable or disable ansi color output ''' | |
self.__consoleColor = color | |
def setSyslogLevel(self, level=LOG_NONE): | |
''' Maximum level of messages to log by syslog. | |
Set LOG_NONE to disable syslog (default)''' | |
level = self._sanitizeLogLevel(level) | |
self.__syslogLevel = level | |
if syslog is not None: | |
if self.__syslogLevel != LOG_NONE: | |
# Set ident string for syslog | |
ident = 'opsi' | |
try: | |
raise Exception | |
except Exception: | |
# Go back 2 frames | |
frame = sys.exc_traceback.tb_frame.f_back.f_back | |
# Get caller's filename | |
filename = frame.f_code.co_filename | |
ident = filename.split('/')[-1] | |
syslog.openlog(ident, syslog.LOG_CONS, syslog.LOG_DAEMON) | |
def setMessageSubjectLevel(self, level=LOG_NONE): | |
self.__messageSubjectLevel = self._sanitizeLogLevel(level) | |
def setConsoleLevel(self, level=LOG_NONE): | |
''' Maximum level of messages to print to stderr | |
Set LOG_NONE to disable output to stderr (default)''' | |
self.__consoleLevel = self._sanitizeLogLevel(level) | |
@staticmethod | |
def _sanitizeLogLevel(level): | |
if level < LOG_NONE: | |
return LOG_NONE | |
elif level > LOG_CONFIDENTIAL: | |
return LOG_CONFIDENTIAL | |
else: | |
return level | |
def getConsoleLevel(self): | |
return self.__consoleLevel | |
def getFileLevel(self): | |
return self.__fileLevel | |
def getLogFile(self, currentThread=False, object=None): | |
if currentThread: | |
return self._getCurrentThreadLog() | |
elif object: | |
return self._getObjectLog(id(object)) | |
return self.__logFile | |
def setLogFile(self, logFile, currentThread=False, object=None): | |
''' | |
Set the filename of logfile. | |
:param logFile: The path to the logfile. Setting this to `None` \ | |
will disable logging to a file. | |
''' | |
if logFile is not None: | |
logFile = os.path.abspath(logFile) | |
if currentThread: | |
self._setThreadConfig('logFile', logFile) | |
self.debug(u"Now using log-file '%s' for thread %s" \ | |
% (logFile, thread.get_ident())) | |
elif object: | |
self._setObjectConfig(id(object), 'logFile', logFile) | |
self.debug(u"Now using log-file '%s' for object 0x%x" % (logFile, id(object))) | |
else: | |
self.__logFile = logFile | |
self.debug(u"Now using log-file '%s'" % self.__logFile) | |
def linkLogFile(self, linkFile, currentThread=False, object=None): | |
''' Link the current logfile to ``linkfile``. ''' | |
logFile = None | |
if currentThread: | |
logFile = self._getThreadConfig('logFile') | |
elif object: | |
logFile = self._getObjectConfig(id(object), 'logFile') | |
else: | |
logFile = self.__logFile | |
if not logFile: | |
self.error(u"Cannot create symlink {0!r}: log-file unknown", linkFile) | |
return | |
if not os.path.isabs(linkFile): | |
linkFile = os.path.join(os.path.dirname(logFile), linkFile) | |
try: | |
if logFile == linkFile: | |
raise ValueError(u'logFile and linkFile are the same file!') | |
try: | |
os.unlink(linkFile) | |
except OSError as oserr: | |
if oserr.errno != 2: # 2 = File not found | |
self.debug2(u"Failed to remove link {0!r}: {1}", linkFile, oserr) | |
os.symlink(logFile, linkFile) | |
except Exception as error: | |
self.error(u"Failed to create symlink from {0!r} to {1!r}: {2}", logFile, linkFile, error) | |
def setFileLevel(self, level=LOG_NONE): | |
''' Maximum level of messages to appear in logfile | |
Set LOG_NONE to disable output to logfile (default)''' | |
self.__fileLevel = self._sanitizeLogLevel(level) | |
def exit(self, object=None): | |
if object: | |
if id(object) in self.__objectConfig: | |
self.debug(u"Deleting config of object 0x%x" % id(object)) | |
del self.__objectConfig[id(object)] | |
for objectId in self.__objectConfig.keys(): | |
self.debug2(u"Got special config for object 0x%x" % objectId) | |
threadId = unicode(thread.get_ident()) | |
if threadId in self.__threadConfig: | |
self.debug(u"Deleting config of thread %s" % threadId) | |
del self.__threadConfig[threadId] | |
for threadId in self.__threadConfig.keys(): | |
self.debug2(u"Got special config for thread %s" % threadId) | |
def _setThreadConfig(self, key, value): | |
threadId = unicode(thread.get_ident()) | |
try: | |
self.__threadConfig[threadId][key] = value | |
except KeyError: | |
self.__threadConfig[threadId] = {key: value} | |
def _getThreadConfig(self, key=None): | |
threadId = unicode(thread.get_ident()) | |
if threadId not in self.__threadConfig: | |
return None | |
if not key: | |
return self.__threadConfig[threadId] | |
return self.__threadConfig[threadId].get(key) | |
def _setObjectConfig(self, objectId, key, value): | |
try: | |
self.__objectConfig[objectId][key] = value | |
except KeyError: | |
self.__objectConfig[objectId] = {key: value} | |
def _getObjectConfig(self, objectId, key=None): | |
if objectId not in self.__objectConfig: | |
return None | |
if not key: | |
return self.__objectConfig[objectId] | |
return self.__objectConfig[objectId].get(key) | |
def log(self, level, message, raiseException=False, formatArgs=[], formatKwargs={}): | |
''' | |
Log a message with the given level. | |
:param level: The log level of this message. | |
:param message: The message to log. | |
:param raiseException: True raises an exception if any error occurs. \ | |
False suppresses exceptions. | |
:type raiseException: bool | |
''' | |
if (level > self.__messageSubjectLevel and | |
level > self.__consoleLevel and | |
level > self.__fileLevel and | |
level > self.__syslogLevel and | |
not self.univentionLogger_priv): | |
return | |
def formatMessage(unformattedMessage, removeConfidential=False): | |
tempMessage = unicode(unformattedMessage) | |
tempMessage = tempMessage.replace(u'%M', message) | |
if removeConfidential: | |
for string in self.__confidentialStrings: | |
tempMessage = tempMessage.replace(string, u'*** confidential ***') | |
tempMessage = tempMessage.replace(u'%D', datetime) | |
tempMessage = tempMessage.replace(u'%T', threadId) | |
tempMessage = tempMessage.replace(u'%l', unicode(level)) | |
tempMessage = tempMessage.replace(u'%L', levelname) | |
tempMessage = tempMessage.replace(u'%C', componentname) | |
tempMessage = tempMessage.replace(u'%F', filename) | |
tempMessage = tempMessage.replace(u'%N', linenumber) | |
return tempMessage | |
try: | |
if not isinstance(message, unicode): | |
if not isinstance(message, str): | |
message = unicode(message) | |
else: | |
message = unicode(message, 'utf-8', 'replace') | |
try: | |
message = message.format(*formatArgs, **formatKwargs) | |
except KeyError as e: | |
if 'Missing format for key ' not in str(e).lower(): | |
raise e | |
except ValueError as e: | |
if 'invalid conversion specification' not in str(e).lower(): | |
raise e | |
componentname = self.__componentName | |
datetime = unicode(time.strftime(u"%b %d %H:%M:%S", time.localtime()), 'utf-8', 'replace') | |
threadId = unicode(thread.get_ident()) | |
specialConfig = None | |
try: | |
levelname, color = _LOGLEVEL_NAME_AND_COLOR_MAPPING[level] | |
except KeyError: | |
levelname = u'' | |
color = COLOR_NORMAL | |
try: | |
filename = unicode(os.path.basename(sys._getframe(2).f_code.co_filename)) | |
linenumber = unicode(sys._getframe(2).f_lineno) | |
specialConfig = self._getThreadConfig() | |
if not specialConfig and self.__objectConfig: | |
# Ouch, this hurts... | |
f = sys._getframe(2) | |
while f is not None: | |
obj = f.f_locals.get('self') | |
if obj: | |
c = self._getObjectConfig(id(obj)) | |
if c: | |
specialConfig = c | |
break | |
f = f.f_back | |
except Exception: | |
# call stack not deep enough? | |
try: | |
filename | |
except NameError: | |
filename = u'' | |
try: | |
linenumber | |
except NameError: | |
linenumber = u'' | |
if specialConfig: | |
componentname = specialConfig.get('componentName', componentname) | |
if level <= self.__messageSubjectLevel: | |
m = self.__messageSubjectFormat | |
if specialConfig: | |
m = specialConfig.get('messageSubjectFormat', m) | |
m = formatMessage(m, removeConfidential=self.__messageSubjectLevel < LOG_CONFIDENTIAL) | |
self.__loggerSubject.setMessage(m, level) | |
if level <= self.__consoleLevel: | |
# Log to terminal | |
m = self.__consoleFormat | |
if specialConfig: | |
m = specialConfig.get('consoleFormat', m) | |
m = formatMessage(m, removeConfidential=self.__consoleLevel < LOG_CONFIDENTIAL) | |
if self.__consoleStdout: | |
fh = sys.stdout | |
else: | |
fh = sys.stderr | |
try: | |
fhEncoding = fh.encoding | |
except Exception: | |
fhEncoding = None | |
if fhEncoding is None: | |
fhEncoding = locale.getpreferredencoding() | |
if self.__consoleColor: | |
m = u"%s%s%s\n" % (color, m, COLOR_NORMAL) | |
else: | |
m = u"%s\n" % m | |
fh.write(m.encode(fhEncoding, 'backslashreplace')) | |
if level <= self.__fileLevel: | |
# Log to file | |
logFile = self.__logFile | |
if specialConfig: | |
logFile = specialConfig.get('logFile', logFile) | |
if logFile: | |
m = self.__fileFormat | |
if specialConfig: | |
m = specialConfig.get('fileFormat', m) | |
m = formatMessage(m, removeConfidential=self.__fileLevel < LOG_CONFIDENTIAL) | |
try: | |
lf = codecs.open(logFile, 'a+', 'utf-8', 'replace') | |
except Exception as err: | |
lf = None | |
if lf: | |
timeout = 0 | |
locked = False | |
while not locked and timeout < 2000: | |
# While not timed out and not locked | |
try: | |
# Try to lock file | |
if os.name == 'nt': | |
hfile = win32file._get_osfhandle(lf.fileno()) | |
win32file.LockFileEx(hfile, win32con.LOCKFILE_EXCLUSIVE_LOCK, 0, -0x7fff0000, pywintypes.OVERLAPPED()) | |
# win32file.LockFileEx(hfile, flags, 0, -0x10000, __overlapped) | |
elif os.name == 'posix': | |
# Flags for exclusive, non-blocking lock | |
fcntl.flock(lf.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) | |
except IOError as err: | |
# Locking failed | |
# increase timeout counter, sleep 100 millis | |
timeout += 100 | |
time.sleep(0.1) | |
else: | |
# File successfully locked | |
locked = True | |
if locked: | |
if self.__fileColor: | |
m = u"%s%s%s" % (color, m, COLOR_NORMAL) | |
m += u'\n' | |
if os.name == 'nt': | |
m = m.replace(u'\n', u'\r\n') | |
lf.write(m) | |
lf.close() | |
if syslog is not None and level <= self.__syslogLevel: | |
# Log to syslog | |
m = self.__syslogFormat | |
if specialConfig: | |
m = specialConfig.get('syslogFormat', m) | |
m = formatMessage(m, removeConfidential=self.__syslogLevel < LOG_CONFIDENTIAL) | |
try: | |
syslog.syslog(_SYSLOG_LEVEL_MAPPING[level], m) | |
except KeyError: | |
# No known log level - ignoring. | |
pass | |
if self.univentionLogger_priv: | |
# univention log | |
m = self.__univentionFormat | |
if specialConfig: | |
m = specialConfig.get('univentionFormat', m) | |
m = formatMessage(m, removeConfidential=True) | |
if level in (LOG_DEBUG2, LOG_DEBUG, LOG_INFO): | |
univentionLevel = self.univentionLogger_priv.ALL | |
elif level == LOG_NOTICE: | |
univentionLevel = self.univentionLogger_priv.INFO | |
elif level == LOG_WARNING: | |
univentionLevel = self.univentionLogger_priv.WARN | |
elif level in (LOG_ERROR, LOG_CRITICAL, LOG_COMMENT): | |
univentionLevel = self.univentionLogger_priv.ERROR | |
else: | |
univentionLevel = None | |
if univentionLevel: | |
self.univentionLogger_priv.debug(self.__univentionClass, univentionLevel, m) | |
except Exception as err: | |
if raiseException: | |
raise err | |
def logException(self, e, logLevel=LOG_CRITICAL): | |
self.logTraceback(sys.exc_info()[2], logLevel) | |
message = forceUnicode(e) | |
self.log(logLevel, u' ==>>> %s' % message) | |
def logFailure(self, failure, logLevel=LOG_CRITICAL): | |
self.logTraceback(failure.getTracebackObject(), logLevel) | |
message = forceUnicode(failure.getErrorMessage()) | |
self.log(logLevel, u' ==>>> %s' % message) | |
def logTraceback(self, tb, logLevel=LOG_CRITICAL): | |
''' | |
Log an traceback. | |
This will log the call trace from the given traceback. | |
''' | |
self.log(logLevel, u'Traceback:') | |
try: | |
for tbInfo in traceback.format_tb(tb): | |
self.log(logLevel, tbInfo) | |
except AttributeError as attrError: | |
self.log(LOG_CRITICAL, u" Failed to log traceback for {0!r}: {1}".format(tb, attrError)) | |
def logWarnings(self): | |
""" | |
Use OPSI.Logger to log warning messages. | |
This redirects messages emitted to the ``warnings`` modules to | |
the opsi logger. | |
""" | |
def _logWarning(message, category, filename, lineno, file=None, line=None): | |
formattedMessage = warnings.formatwarning(message, category, filename, lineno, line) | |
self.warning(formattedMessage) | |
warnings.showwarning = _logWarning | |
def startTwistedLogging(self): | |
from twisted.python import log | |
observer = TwistedLogObserver(self) | |
log.startLoggingWithObserver(observer.emit, setStdout=0) | |
def confidential(self, message, *args, **kwargs): | |
''' Log a confidential message. ''' | |
self.log(LOG_CONFIDENTIAL, message, formatArgs=args, formatKwargs=kwargs) | |
def debug3(self, message, *args, **kwargs): | |
''' Log a debug message. ''' | |
self.debug2(message, *args, **kwargs) | |
def debug2(self, message, *args, **kwargs): | |
''' Log a debug message. ''' | |
self.log(LOG_DEBUG2, message, formatArgs=args, formatKwargs=kwargs) | |
def debug(self, message, *args, **kwargs): | |
''' Log a debug message. ''' | |
self.log(LOG_DEBUG, message, formatArgs=args, formatKwargs=kwargs) | |
def info(self, message, *args, **kwargs): | |
''' Log a info message. ''' | |
self.log(LOG_INFO, message, formatArgs=args, formatKwargs=kwargs) | |
def msg(self, message, *args, **kwargs): | |
''' Log a info message. ''' | |
self.info(message, *args, **kwargs) | |
def notice(self, message, *args, **kwargs): | |
''' Log a notice message. ''' | |
self.log(LOG_NOTICE, message, formatArgs=args, formatKwargs=kwargs) | |
def warning(self, message, *args, **kwargs): | |
''' Log a warning message. ''' | |
self.log(LOG_WARNING, message, formatArgs=args, formatKwargs=kwargs) | |
def error(self, message, *args, **kwargs): | |
''' Log a error message. ''' | |
self.log(LOG_ERROR, message, formatArgs=args, formatKwargs=kwargs) | |
def err(self, message): | |
''' Log a error message. ''' | |
self.error(message) | |
def critical(self, message, *args, **kwargs): | |
''' Log a critical message. ''' | |
self.log(LOG_CRITICAL, message, formatArgs=args, formatKwargs=kwargs) | |
def essential(self, message, *args, **kwargs): | |
''' Log a essential message. ''' | |
self.log(LOG_ESSENTIAL, message, formatArgs=args, formatKwargs=kwargs) | |
def comment(self, message, *args, **kwargs): | |
''' Log a comment message. ''' | |
self.essential(message, *args, **kwargs) | |
class Logger(LoggerImplementation): | |
''' | |
This class implements a SINGLETON used for logging to console, \ | |
file or syslog. | |
''' | |
# Storage for the instance reference | |
__instance = None | |
def __init__(self, logFile=None): | |
""" Create singleton instance """ | |
# Check whether we already have an instance | |
if Logger.__instance is None: | |
# Create and remember instance | |
Logger.__instance = LoggerImplementation() | |
# Store instance reference as the only member in the handle | |
self.__dict__['_Logger__instance'] = Logger.__instance | |
def __getattr__(self, attr): | |
""" Delegate access to implementation """ | |
return getattr(self.__instance, attr) | |
def __setattr__(self, attr, value): | |
""" Delegate access to implementation """ | |
return setattr(self.__instance, attr, value) | |
class VirtFile(object): | |
def __init__(self, logger, level): | |
self.logger = logger | |
self.level = level | |
self.encoding = 'utf-8' | |
def write(self, s): | |
self.logger.log(self.level, s) | |
def flush(self): | |
return |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment