import logging
import os
import sys
import time
import threading
import queue
from logging.handlers import RotatingFileHandler
from _core.constants import LogMode
from _core.constants import LogType
from _core.plugin import Plugin
from _core.plugin import get_plugin
from _core.exception import ParamError
__all__ = ["Log", "platform_logger", "device_logger", "shutdown",
"add_task_file_handler", "remove_task_file_handler",
"change_logger_level",
"add_encrypt_file_handler", "remove_encrypt_file_handler",
"redirect_driver_log_begin", "redirect_driver_log_end", "get_driver_log_path"]
_HANDLERS = []
_LOGGERS = []
MAX_LOG_LENGTH = 20 * 1024 * 1024
MAX_ENCRYPT_LOG_LENGTH = 5 * 1024 * 1024
MAX_LOG_NUMS = 1000
MAX_LOG_CACHE_SIZE = 10
def _new_file_handler(log_file, log_level=None, mode="a"):
from xdevice import Variables
handler = RotatingFileHandler(log_file, mode=mode, maxBytes=MAX_LOG_LENGTH,
backupCount=MAX_LOG_NUMS, encoding="utf-8")
handler.setFormatter(logging.Formatter(Variables.report_vars.log_format))
handler.setLevel(log_level or _query_log_level())
return handler
def _query_log_level():
log_level = getattr(sys, "log_level", logging.INFO) if hasattr(
sys, "log_level") else logging.DEBUG
return log_level
class DriverLogFilter(logging.Filter):
def __init__(self, thread_name):
super().__init__()
self.thread_name = thread_name
def filter(self, record):
return str(record.threadName).startswith(self.thread_name)
class SchedulerLogFilter(logging.Filter):
def __init__(self):
super().__init__()
self.driver_thread_names = []
def filter(self, record):
thread_name = str(record.threadName)
for name in self.driver_thread_names:
if thread_name.startswith(name):
return False
return True
def add_driver_thread_name(self, thread_name):
if thread_name not in self.driver_thread_names:
self.driver_thread_names.append(thread_name)
def del_driver_thread_name(self, thread_name):
if thread_name in self.driver_thread_names:
self.driver_thread_names.remove(thread_name)
class Log:
def __init__(self):
self.level = logging.INFO
self.handlers = []
self.loggers = {}
self.task_log_filter = None
self.task_log_handler = None
self.encrypt_file_handler = None
self.driver_log_handler = {}
self.platform_log_handler = None
self._lock = threading.Lock()
def __initial__(self, log_handler_flag, log_file=None, level=None,
log_format=None):
if _LOGGERS:
return
if log_file and "console" in log_handler_flag:
file_handler = RotatingFileHandler(
log_file, mode="a", maxBytes=MAX_LOG_LENGTH, backupCount=MAX_LOG_NUMS,
encoding="UTF-8")
file_handler.setFormatter(logging.Formatter(log_format))
self.handlers.append(file_handler)
self.platform_log_handler = file_handler
if "console" in log_handler_flag \
and getattr(sys, LogMode.name, LogMode.default) != LogMode.no_console:
stream_handler = logging.StreamHandler(sys.stdout)
stream_handler.setFormatter(logging.Formatter(log_format))
self.handlers.append(stream_handler)
if level:
self.level = level
_HANDLERS.extend(self.handlers)
def set_level(self, level):
self.level = level
def __logger__(self, name=None):
if not name:
return _init_global_logger(name)
elif name in self.loggers:
return self.loggers.get(name)
else:
log = self.loggers.setdefault(name, FrameworkLog(name))
_LOGGERS.append(log)
log.add_platform_level(self.level)
for handler in self.handlers:
log.add_platform_handler(handler)
return log
def __manage_loggers_handler(self, mode, handler):
"""manager loggers handler
mode: str, manage mode, add or remove
handler: logging.Handler, log handler
"""
for _, log in self.loggers.items():
if mode == "add":
log.add_platform_handler(handler)
else:
log.del_platform_handler(handler)
def add_log_handler(self, thread_name, handler):
"""添加基于线程名的日志处理器
thread_name: str, thread name
handler: logging.Handler, log handler
"""
handler.addFilter(DriverLogFilter(thread_name))
self.handlers.append(handler)
self.__manage_loggers_handler("add", handler)
def del_log_handler(self, handler):
"""移除基于线程名的日志处理器
thread_name: str, thread name
handler: logging.Handler, log handler
"""
if handler in self.handlers:
self.handlers.remove(handler)
self.__manage_loggers_handler("del", handler)
def add_driver_log_handler(self, thread_name, log_file):
os.makedirs(os.path.dirname(log_file), exist_ok=True)
try:
self._lock.acquire()
handler = _new_file_handler(log_file)
self.add_log_handler(thread_name, handler)
if self.task_log_filter is not None \
and isinstance(self.task_log_filter, SchedulerLogFilter):
self.task_log_filter.add_driver_thread_name(thread_name)
self.driver_log_handler.update({thread_name: handler})
finally:
self._lock.release()
def get_driver_log_path_by_name(self, thread_name):
try:
self._lock.acquire()
if thread_name in self.driver_log_handler:
handler = self.driver_log_handler[thread_name]
return handler.baseFilename
return None
finally:
self._lock.release()
def del_driver_log_handler(self, thread_name):
if thread_name not in self.driver_log_handler.keys():
return
try:
self._lock.acquire()
if self.task_log_filter is not None \
and isinstance(self.task_log_filter, SchedulerLogFilter):
self.task_log_filter.del_driver_thread_name(thread_name)
handler = self.driver_log_handler.pop(thread_name)
handler.close()
self.del_log_handler(handler)
finally:
self._lock.release()
def add_task_log_handler(self, log_file):
self.task_log_filter = SchedulerLogFilter()
self.task_log_handler = _new_file_handler(log_file)
self.task_log_handler.addFilter(self.task_log_filter)
if self.platform_log_handler:
if self.platform_log_handler in self.handlers:
self.handlers.remove(self.platform_log_handler)
self.__manage_loggers_handler("del", self.platform_log_handler)
self.__manage_loggers_handler("add", self.task_log_handler)
def del_task_log_handler(self):
if self.task_log_handler is None:
return
if self.platform_log_handler:
if self.platform_log_handler not in self.handlers:
self.handlers.append(self.platform_log_handler)
self.__manage_loggers_handler("add", self.platform_log_handler)
self.__manage_loggers_handler("del", self.task_log_handler)
self.task_log_handler.close()
self.task_log_handler = None
def add_encrypt_file_handler(self, log_file):
from xdevice import Variables
file_handler = \
EncryptFileHandler(log_file, mode="ab",
max_bytes=MAX_ENCRYPT_LOG_LENGTH,
backup_count=MAX_LOG_NUMS)
file_handler.setFormatter(logging.Formatter(
Variables.report_vars.log_format))
self.encrypt_file_handler = file_handler
for _, log in self.loggers.items():
log.add_encrypt_log(self.encrypt_file_handler)
def remove_encrypt_file_handler(self):
if self.encrypt_file_handler is None:
return
for _, log in self.loggers.items():
log.remove_encrypt_log(self.encrypt_file_handler)
self.encrypt_file_handler.close()
self.encrypt_file_handler = None
class FrameworkLog:
def __init__(self, name):
self.name = name
self.platform_log = logging.Logger(name)
self.encrypt_log = None
def set_level(self, level):
cache = getattr(self.platform_log, "_cache", None)
if cache and isinstance(cache, dict):
cache.clear()
self.platform_log.setLevel(level)
def add_platform_handler(self, handler):
self.platform_log.addHandler(handler)
def del_platform_handler(self, handler):
self.platform_log.removeHandler(handler)
def add_platform_level(self, level):
self.platform_log.setLevel(level)
def add_encrypt_log(self, handler):
if self.encrypt_log:
return
self.encrypt_log = logging.Logger(self.name)
log_level = _query_log_level()
self.encrypt_log.setLevel(log_level)
self.encrypt_log.addHandler(handler)
def remove_encrypt_log(self, handler):
if not self.encrypt_log:
return
self.encrypt_log.removeHandler(handler)
self.encrypt_log = None
def info(self, msg, *args, **kwargs):
additional_output = self._get_additional_output(**kwargs)
updated_msg = self._update_msg(additional_output, msg)
self.platform_log.info(updated_msg, *args)
if self.encrypt_log:
self.encrypt_log.info(updated_msg, *args)
def debug(self, msg, *args, **kwargs):
additional_output = self._get_additional_output(**kwargs)
updated_msg = self._update_msg(additional_output, msg)
from _core.report.encrypt import check_pub_key_exist
if not check_pub_key_exist():
self.platform_log.debug(updated_msg, *args)
else:
if self.encrypt_log:
self.encrypt_log.debug(updated_msg, *args)
def error(self, msg, *args, **kwargs):
error_no = kwargs.get("error_no", "00000")
additional_output = self._get_additional_output(error_no, **kwargs)
updated_msg = self._update_msg(additional_output, msg)
self.platform_log.error(updated_msg, *args)
if self.encrypt_log:
self.encrypt_log.error(updated_msg, *args)
def warning(self, msg, *args, **kwargs):
additional_output = self._get_additional_output(**kwargs)
updated_msg = self._update_msg(additional_output, msg)
self.platform_log.warning(updated_msg, *args)
if self.encrypt_log:
self.encrypt_log.warning(updated_msg, *args)
def exception(self, msg, *args, **kwargs):
error_no = kwargs.get("error_no", "00000")
exc_info = kwargs.get("exc_info", True)
if exc_info is not True and exc_info is not False:
exc_info = True
additional_output = self._get_additional_output(error_no, **kwargs)
updated_msg = self._update_msg(additional_output, msg)
self.platform_log.exception(updated_msg, exc_info=exc_info, *args)
if self.encrypt_log:
self.encrypt_log.exception(updated_msg, exc_info=exc_info, *args)
@classmethod
def _update_msg(cls, additional_output, msg):
msg = "[{}]".format(msg) if msg else msg
if msg and additional_output:
msg = "{} [{}]".format(msg, additional_output)
return msg
def _get_additional_output(self, error_number=None, **kwargs):
dict_str = self._get_dict_str(**kwargs)
if error_number:
additional_output = "ErrorNo=%s" % error_number
else:
return dict_str
if dict_str:
additional_output = "%s, %s" % (additional_output, dict_str)
return additional_output
@classmethod
def _get_dict_str(cls, **kwargs):
dict_str = ""
for key, value in kwargs.items():
if key in ["error_no", "exc_info"]:
continue
dict_str = "%s%s=%s, " % (dict_str, key, value)
if dict_str:
dict_str = dict_str[:-2]
return dict_str
def platform_logger(name=None):
plugins = get_plugin(Plugin.LOG, LogType.tool)
for log_plugin in plugins:
if log_plugin.get_plugin_config().enabled:
return log_plugin.__logger__(name)
return _init_global_logger(name)
def device_logger(name=None):
plugins = get_plugin(Plugin.LOG, LogType.device)
for log_plugin in plugins:
if log_plugin.get_plugin_config().enabled:
return log_plugin.__logger__(name)
return _init_global_logger(name)
def shutdown():
for log in _LOGGERS:
for handler in log.handlers:
log.removeHandler(handler)
for handler in _HANDLERS:
handler.close()
_HANDLERS.clear()
_LOGGERS.clear()
def redirect_driver_log_begin(thread_name, log_file):
plugins = get_plugin(Plugin.LOG, LogType.tool)
for log_plugin in plugins:
if log_plugin.get_plugin_config().enabled:
log_plugin.add_driver_log_handler(thread_name, log_file)
def redirect_driver_log_end(thread_name):
plugins = get_plugin(Plugin.LOG, LogType.tool)
for log_plugin in plugins:
if log_plugin.get_plugin_config().enabled:
log_plugin.del_driver_log_handler(thread_name)
def get_driver_log_path(thread_name):
plugins = get_plugin(Plugin.LOG, LogType.tool)
for log_plugin in plugins:
if log_plugin.get_plugin_config().enabled:
path = log_plugin.get_driver_log_path_by_name(thread_name)
if path:
return path
else:
return None
def add_task_file_handler(log_file=None):
if log_file is None:
return
plugins = get_plugin(Plugin.LOG, LogType.tool)
for log_plugin in plugins:
if log_plugin.get_plugin_config().enabled:
log_plugin.add_task_log_handler(log_file)
def remove_task_file_handler():
plugins = get_plugin(Plugin.LOG, LogType.tool)
for log_plugin in plugins:
if log_plugin.get_plugin_config().enabled:
log_plugin.del_task_log_handler()
def add_encrypt_file_handler(log_file=None):
if log_file is None:
return
plugins = get_plugin(Plugin.LOG, LogType.tool)
for log_plugin in plugins:
if log_plugin.get_plugin_config().enabled:
log_plugin.add_encrypt_file_handler(log_file)
def remove_encrypt_file_handler():
plugins = get_plugin(Plugin.LOG, LogType.tool)
for log_plugin in plugins:
if log_plugin.get_plugin_config().enabled:
log_plugin.remove_encrypt_file_handler()
def _init_global_logger(name=None):
handler = logging.StreamHandler(sys.stdout)
log_format = \
"[%(asctime)s] [%(thread)d] [%(name)s] [%(levelname)s] [%(message)s]"
handler.setFormatter(logging.Formatter(log_format))
log = FrameworkLog(name)
log.platform_log.setLevel(logging.INFO)
log.platform_log.addHandler(handler)
return log
def change_logger_level(leve_dict):
level_map = {"debug": logging.DEBUG, "info": logging.INFO}
if "console" in leve_dict.keys():
level = leve_dict["console"]
if not level:
return
if str(level).lower() in level_map.keys():
logger_level = level_map.get(str(level).lower(), logging.INFO)
plugins = get_plugin(Plugin.LOG, LogType.tool)
for log_plugin in plugins:
log_plugin.set_level(logger_level)
for logger in _LOGGERS:
if getattr(logger, "setLevel", None):
logger.setLevel(logger_level)
elif getattr(logger, "set_level", None):
logger.set_level(logger_level)
if "file" in leve_dict.keys():
level = leve_dict["file"]
if not level:
return
if str(level).lower() in level_map.keys():
logger_level = level_map.get(str(level).lower(), logging.INFO)
setattr(sys, "log_level", logger_level)
class EncryptFileHandler(RotatingFileHandler):
def __init__(self, filename, mode='ab', max_bytes=0, backup_count=0,
encoding=None, delay=False):
super().__init__(filename, mode, max_bytes, backup_count, encoding, delay)
self.mode = mode
self.encrypt_error = None
def _open(self):
if not self.mode == "ab":
self.mode = "ab"
base_file_name = getattr(self, "baseFilename", None)
return open(base_file_name, self.mode)
def emit(self, record):
try:
if not self._encrypt_valid():
return
should_rollover = getattr(self, "shouldRollover", None)
if callable(should_rollover) and should_rollover(record):
self.doRollover()
if not getattr(self, "stream", None):
setattr(self, "stream", self._open())
msg = self.format(record)
stream = getattr(self, "stream", self._open())
stream.write(msg)
self.flush()
except RecursionError as error:
raise error
def _encrypt_valid(self):
from _core.report.encrypt import check_pub_key_exist
if check_pub_key_exist() and not self.encrypt_error:
return True
return False
def format(self, record):
"""
Customize the implementation method. If the log format of the
framework changes, update the return value format of the method
in a timely manner.
:param record: logging.LogRecord
:return: bytes
"""
from _core.report.encrypt import do_rsa_encrypt
create_time = "{},{}".format(
time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(record.created)),
"{:0>3d}".format(int("%d" % record.msecs)))
name = record.name
level_name = record.levelname
msg = record.msg
if msg and "%s" in msg:
msg = msg % record.args
info = "[%s] [%s] [%s] [%s] %s%s" \
% (create_time, threading.current_thread().ident, name,
level_name, msg, "\n")
try:
return do_rsa_encrypt(info)
except ParamError as error:
error_no_str = \
"ErrorNo={}".format(getattr(error, "error_no", "00113"))
info = "[%s] [%s] [%s] [%s] [%s] [%s]\n" % (
create_time, threading.current_thread().ident,
name, "ERROR", error, error_no_str)
self.encrypt_error = bytes(info, "utf-8")
return self.encrypt_error
class LogQueue:
log = None
max_size = 0
queue_debug = None
queue_info = None
def __init__(self, log, max_size=MAX_LOG_CACHE_SIZE):
self.log = log
self.max_size = max_size
self.queue_info = queue.Queue(maxsize=self.max_size)
self.queue_debug = queue.Queue(maxsize=self.max_size)
self.queue_error = queue.Queue(maxsize=self.max_size)
def _put(self, log_queue, log_data, clear):
is_print = False
result_data = ""
if log_queue.full() or clear:
if log_queue.qsize() > 0:
is_print = True
result_data = "{}\n".format(log_queue.get())
else:
result_data = ""
if log_data != "":
log_queue.put(log_data)
while not log_queue.empty():
is_print = True
result_data = "{} [{}] {}\n".format(result_data, threading.current_thread().ident, log_queue.get())
else:
if log_data != "":
log_queue.put(log_data)
return is_print, result_data
def info(self, log_data, clear=False):
is_print, result_data = self._put(self.queue_info, log_data, clear)
if is_print:
self.log.info(result_data)
def debug(self, log_data, clear=False):
is_print, result_data = self._put(self.queue_debug, log_data, clear)
if is_print:
self.log.debug(result_data)
def error(self, log_data, clear=False):
is_print, result_data = self._put(self.queue_error, log_data, clear)
if is_print:
self.log.error(result_data)
def clear(self):
self.info(log_data="", clear=True)
self.debug(log_data="", clear=True)
self.error(log_data="", clear=True)