You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
283 lines
7.5 KiB
283 lines
7.5 KiB
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
Logging.
|
|
|
|
**Project Name:** MakeHuman
|
|
|
|
**Product Home Page:** http://www.makehumancommunity.org/
|
|
|
|
**Github Code Home Page:** https://github.com/makehumancommunity/
|
|
|
|
**Authors:** Glynn Clements
|
|
|
|
**Copyright(c):** MakeHuman Team 2001-2020
|
|
|
|
**Licensing:** AGPL3
|
|
|
|
This file is part of MakeHuman Community (www.makehumancommunity.org).
|
|
|
|
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/>.
|
|
|
|
|
|
Abstract
|
|
--------
|
|
|
|
Logging component. To be used instead of print statements.
|
|
"""
|
|
|
|
import code
|
|
import logging
|
|
from logging import CRITICAL, DEBUG, ERROR, INFO, WARNING
|
|
import logging.config
|
|
import os
|
|
|
|
from library.getpath import getPath, getSysDataPath
|
|
from library.universal import G
|
|
|
|
NOTICE = 25
|
|
MESSAGE = logging.INFO
|
|
|
|
LEVEL_TO_STR = {
|
|
DEBUG: "debug",
|
|
INFO: "info",
|
|
WARNING: "warning",
|
|
ERROR: "error",
|
|
CRITICAL: "critical",
|
|
NOTICE: "notice",
|
|
}
|
|
|
|
|
|
def logLevelToStr(levelCode):
|
|
if levelCode in LEVEL_TO_STR:
|
|
return LEVEL_TO_STR[levelCode]
|
|
else:
|
|
levels = sorted(LEVEL_TO_STR.keys())
|
|
i = 0
|
|
while i < len(levels) and levelCode < levels[i]:
|
|
i += 1
|
|
i = min(i, len(levels) - 1)
|
|
return levels[i].upper()
|
|
|
|
|
|
def _toUnicode(msg, *args):
|
|
"""
|
|
Unicode representation of the formatted message.
|
|
String is decoded with the codeset used by the filesystem of the operating
|
|
system.
|
|
"""
|
|
try:
|
|
msg_ = msg % args
|
|
except TypeError:
|
|
# Also allow dict with keywords in format string, passed as first arg
|
|
if len(args) == 1 and isinstance(args[0], dict):
|
|
msg_ = msg % args[0]
|
|
else:
|
|
raise
|
|
|
|
if isinstance(msg_, bytes):
|
|
return str(msg_, encoding="utf-8")
|
|
else:
|
|
return msg_
|
|
|
|
|
|
def debug(msg, *args, **kwargs):
|
|
try:
|
|
logging.debug(msg, *args, **kwargs)
|
|
except UnicodeError:
|
|
msg_ = _toUnicode(msg, args)
|
|
logging.debug(msg_, kwargs)
|
|
|
|
|
|
def warning(msg, *args, **kwargs):
|
|
try:
|
|
logging.warning(msg, *args, **kwargs)
|
|
except UnicodeError:
|
|
msg_ = _toUnicode(msg, args)
|
|
logging.warning(msg_, kwargs)
|
|
|
|
|
|
def error(msg, *args, **kwargs):
|
|
try:
|
|
logging.error(msg, *args, **kwargs)
|
|
except UnicodeError:
|
|
msg_ = _toUnicode(msg, args)
|
|
logging.error(msg_, kwargs)
|
|
|
|
|
|
def message(msg, *args, **kwargs):
|
|
try:
|
|
logging.info(msg, *args, **kwargs)
|
|
except UnicodeError:
|
|
msg_ = _toUnicode(msg, args)
|
|
logging.info(msg_, kwargs)
|
|
|
|
|
|
# We have to make notice() appear to have been defined in the logging module
|
|
# so that logging.findCaller() finds its caller, not notice() itself
|
|
# This is required for the pathname, filename, module, funcName and lineno
|
|
# members of the LogRecord refer to the caller rather than to notice() itself.
|
|
|
|
_notice_src = r"""
|
|
def notice(format, *args, **kwargs):
|
|
logging.log(NOTICE, format, *args, **kwargs)
|
|
"""
|
|
try:
|
|
exec(code.compile_command(_notice_src, logging.info.__code__.co_filename))
|
|
except Exception:
|
|
print("Error defining notice() function in log.py")
|
|
|
|
def notice(format, *args, **kwargs):
|
|
logging.log(NOTICE, format, *args, **kwargs)
|
|
|
|
|
|
logging.addLevelName(NOTICE, "NOTICE")
|
|
logging.addLevelName(MESSAGE, "MESSAGE")
|
|
|
|
|
|
def _splitpath(path):
|
|
|
|
head, tail = os.path.split(path)
|
|
if tail == "":
|
|
return [head]
|
|
return _splitpath(head) + [tail]
|
|
|
|
|
|
class NoiseFilter(logging.Filter):
|
|
def filter(self, record):
|
|
try:
|
|
if record.msg.endswith(":\n%s"):
|
|
record.msg = record.msg[:-4]
|
|
record.args = record.args[:-1]
|
|
except Exception as e:
|
|
print("Error in NoiseFilter", e)
|
|
import traceback
|
|
|
|
traceback.print_exc()
|
|
return True
|
|
|
|
|
|
class DowngradeFilter(logging.Filter):
|
|
def __init__(self, level):
|
|
super(DowngradeFilter, self).__init__()
|
|
self.level = level
|
|
|
|
def filter(self, record):
|
|
try:
|
|
if record.levelno > self.level:
|
|
record.levelno = self.level
|
|
record.levelname = logging.getLevelName(record.levelno)
|
|
except Exception as e:
|
|
print("Error in DowngradeFilter", e)
|
|
import traceback
|
|
|
|
traceback.print_exc()
|
|
return True
|
|
|
|
|
|
_logLevelColors = {
|
|
DEBUG: "grey",
|
|
NOTICE: "blue",
|
|
INFO: "blue",
|
|
WARNING: "darkorange",
|
|
ERROR: "red",
|
|
CRITICAL: "red",
|
|
}
|
|
|
|
|
|
def getLevelColor(logLevel):
|
|
global _logLevelColors
|
|
if logLevel not in _logLevelColors:
|
|
warning("Unknown log level color %s (%s)" % (logLevel, logLevelToStr(logLevel)))
|
|
return _logLevelColors.get(logLevel, "red")
|
|
|
|
|
|
class SplashLogHandler(logging.Handler):
|
|
def emit(self, record):
|
|
if G.app is not None and G.app.splash is not None:
|
|
G.app.splash.logMessage(self.format(record).split("\n", 1)[0] + "\n")
|
|
|
|
|
|
class StatusLogHandler(logging.Handler):
|
|
def emit(self, record):
|
|
if G.app is not None and G.app.statusBar is not None and record.levelno >= ERROR:
|
|
msg = "An ERROR Occurred. Check Utilities/Logs For More Information! Error Message: {:s}".format(
|
|
record.getMessage()
|
|
)
|
|
G.app.statusBar.temporaryMessage("%s", msg, msec=10000)
|
|
|
|
|
|
class ApplicationLogHandler(logging.Handler):
|
|
def emit(self, record):
|
|
if G.app is not None and G.app.log_window is not None:
|
|
G.app.addLogMessage(self.format(record), record.levelno)
|
|
|
|
|
|
_logger_notice_src = r"""
|
|
def _logger_notice(self, msg, *args, **kwargs):
|
|
self.log(NOTICE, msg, *args, **kwargs)
|
|
"""
|
|
try:
|
|
exec(code.compile_command(_logger_notice_src, logging.info.__code__.co_filename))
|
|
except Exception as e:
|
|
print("Error defining _logger_notice() function in log.py", e)
|
|
|
|
def _logger_notice(self, format, *args, **kwargs):
|
|
self.log(NOTICE, format, *args, **kwargs)
|
|
|
|
|
|
class Logger(logging.Logger):
|
|
message = logging.Logger.info
|
|
notice = _logger_notice
|
|
|
|
|
|
def init():
|
|
def config():
|
|
userDir = getPath("")
|
|
defaults = dict(mhUserDir=userDir.replace("\\", "/"))
|
|
|
|
try:
|
|
filename = os.path.join(userDir, "logging.ini")
|
|
if os.path.isfile(filename):
|
|
logging.config.fileConfig(filename, defaults)
|
|
return
|
|
except Exception:
|
|
pass
|
|
|
|
try:
|
|
logging.config.fileConfig(getSysDataPath("logging.ini"), defaults)
|
|
return
|
|
except Exception:
|
|
pass
|
|
|
|
try:
|
|
logging.basicConfig(level=logging.DEBUG)
|
|
return
|
|
except Exception:
|
|
pass
|
|
|
|
logging.setLoggerClass(Logger)
|
|
|
|
config()
|
|
|
|
logging.captureWarnings(True)
|
|
|
|
try:
|
|
logging.getLogger("OpenGL.formathandler").addFilter(NoiseFilter())
|
|
logging.getLogger("OpenGL.extensions").addFilter(DowngradeFilter(logging.DEBUG))
|
|
except Exception:
|
|
import traceback
|
|
|
|
traceback.print_exc()
|