logger.py 15.5 KB
Newer Older
W
wenjun 已提交
1 2 3 4
#!/usr/bin/env python3
# coding=utf-8

#
D
deveco_test 已提交
5
# Copyright (c) 2020-2022 Huawei Device Co., Ltd.
W
wenjun 已提交
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import logging
import sys
M
mamingshuai 已提交
21
import time
22
import threading
W
wenjun 已提交
23 24 25 26 27
from logging.handlers import RotatingFileHandler

from _core.constants import LogType
from _core.plugin import Plugin
from _core.plugin import get_plugin
M
mamingshuai 已提交
28 29
from _core.exception import ParamError

W
wenjun 已提交
30 31

__all__ = ["Log", "platform_logger", "device_logger", "shutdown",
M
mamingshuai 已提交
32 33 34
           "add_task_file_handler", "remove_task_file_handler",
           "change_logger_level", "add_encrypt_file_handler",
           "remove_encrypt_file_handler"]
W
wenjun 已提交
35 36 37

_HANDLERS = []
_LOGGERS = []
D
deveco_test 已提交
38
MAX_LOG_LENGTH = 20 * 1024 * 1024
M
mamingshuai 已提交
39
MAX_ENCRYPT_LOG_LENGTH = 5 * 1024 * 1024
D
deveco_test 已提交
40
MAX_LOG_NUMS = 1000
W
wenjun 已提交
41 42 43 44 45 46 47 48 49 50


class Log:
    task_file_handler = None

    def __init__(self):
        self.level = logging.INFO
        self.handlers = []
        self.loggers = {}
        self.task_file_handler = None
M
mamingshuai 已提交
51
        self.encrypt_file_handler = None
W
wenjun 已提交
52 53 54 55 56 57 58 59 60

    def __initial__(self, log_handler_flag, log_file=None, level=None,
                    log_format=None):
        if _LOGGERS:
            return

        self.handlers = []
        if log_file and "console" in log_handler_flag:
            file_handler = RotatingFileHandler(
D
deveco_test 已提交
61
                log_file, mode="a", maxBytes=MAX_LOG_LENGTH, backupCount=MAX_LOG_NUMS,
W
wenjun 已提交
62 63 64 65 66 67 68 69 70 71 72 73 74 75
                encoding="UTF-8")
            file_handler.setFormatter(logging.Formatter(log_format))
            self.handlers.append(file_handler)
        if "console" in log_handler_flag:
            stream_handler = logging.StreamHandler(sys.stdout)
            stream_handler.setFormatter(logging.Formatter(log_format))
            self.handlers.append(stream_handler)

        if level:
            self.level = level
        self.loggers = {}
        self.task_file_handler = None
        _HANDLERS.extend(self.handlers)

M
mamingshuai 已提交
76 77 78
    def set_level(self, level):
        self.level = level

W
wenjun 已提交
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
    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.platform_log.setLevel(self.level)
            for handler in self.handlers:
                log.platform_log.addHandler(handler)
            if self.task_file_handler:
                log.add_task_log(self.task_file_handler)
            return log

    def add_task_file_handler(self, log_file):
        from xdevice import Variables
        file_handler = RotatingFileHandler(
D
deveco_test 已提交
97
                log_file, mode="a", maxBytes=MAX_LOG_LENGTH, backupCount=MAX_LOG_NUMS,
W
wenjun 已提交
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
                encoding="UTF-8")
        file_handler.setFormatter(logging.Formatter(
            Variables.report_vars.log_format))
        self.task_file_handler = file_handler
        for _, log in self.loggers.items():
            log.add_task_log(self.task_file_handler)

    def remove_task_file_handler(self):
        if self.task_file_handler is None:
            return
        for _, log in self.loggers.items():
            log.remove_task_log(self.task_file_handler)
        self.task_file_handler.close()
        self.task_file_handler = None

M
mamingshuai 已提交
113 114 115 116 117 118
    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,
D
deveco_test 已提交
119
                               backup_count=MAX_LOG_NUMS, encoding="utf-8")
M
mamingshuai 已提交
120 121 122 123 124 125 126 127 128 129 130 131 132 133
        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

W
wenjun 已提交
134 135 136 137 138 139 140

class FrameworkLog:

    def __init__(self, name):
        self.name = name
        self.platform_log = logging.Logger(name)
        self.task_log = None
M
mamingshuai 已提交
141 142 143 144 145 146 147 148 149
        self.encrypt_log = None

    def set_level(self, level):
        # apply to dynamic change logger level, and
        # only change the level of platform log
        cache = getattr(self.platform_log, "_cache", None)
        if cache and isinstance(cache, dict):
            cache.clear()
        self.platform_log.setLevel(level)
W
wenjun 已提交
150 151 152 153 154

    def add_task_log(self, handler):
        if self.task_log:
            return
        self.task_log = logging.Logger(self.name)
M
mamingshuai 已提交
155 156 157
        log_level = getattr(sys, "log_level", logging.INFO) if hasattr(
            sys, "log_level") else logging.DEBUG
        self.task_log.setLevel(log_level)
W
wenjun 已提交
158 159 160 161 162 163 164 165
        self.task_log.addHandler(handler)

    def remove_task_log(self, handler):
        if not self.task_log:
            return
        self.task_log.removeHandler(handler)
        self.task_log = None

M
mamingshuai 已提交
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
    def add_encrypt_log(self, handler):
        if self.encrypt_log:
            return
        self.encrypt_log = logging.Logger(self.name)
        log_level = getattr(sys, "log_level", logging.INFO) if hasattr(
            sys, "log_level") else logging.DEBUG
        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

W
wenjun 已提交
181
    def info(self, msg, *args, **kwargs):
M
mamingshuai 已提交
182 183 184
        additional_output = self._get_additional_output(**kwargs)
        updated_msg = self._update_msg(additional_output, msg)
        self.platform_log.info(updated_msg, *args)
W
wenjun 已提交
185
        if self.task_log:
M
mamingshuai 已提交
186 187 188
            self.task_log.info(updated_msg, *args)
        if self.encrypt_log:
            self.encrypt_log.info(updated_msg, *args)
W
wenjun 已提交
189 190

    def debug(self, msg, *args, **kwargs):
M
mamingshuai 已提交
191 192 193 194 195 196 197 198 199 200
        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)
            if self.task_log:
                self.task_log.debug(updated_msg, *args)
        else:
            if self.encrypt_log:
                self.encrypt_log.debug(updated_msg, *args)
W
wenjun 已提交
201 202

    def error(self, msg, *args, **kwargs):
M
mamingshuai 已提交
203 204 205 206 207
        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)
W
wenjun 已提交
208
        if self.task_log:
M
mamingshuai 已提交
209 210 211
            self.task_log.error(updated_msg, *args)
        if self.encrypt_log:
            self.encrypt_log.error(updated_msg, *args)
W
wenjun 已提交
212 213

    def warning(self, msg, *args, **kwargs):
M
mamingshuai 已提交
214 215
        additional_output = self._get_additional_output(**kwargs)
        updated_msg = self._update_msg(additional_output, msg)
W
wenjun 已提交
216

M
mamingshuai 已提交
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
        self.platform_log.warning(updated_msg, *args)
        if self.task_log:
            self.task_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)
W
wenjun 已提交
232
        if self.task_log:
M
mamingshuai 已提交
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
            self.task_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 = "[%s]" % msg if msg else msg
        if msg and additional_output:
            msg = "%s [%s]" % (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
W
wenjun 已提交
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310


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():
    # logging will be shutdown automatically, when the program exits.
    # This function is used by testing.
    for log in _LOGGERS:
        for handler in log.handlers:
            log.removeHandler(handler)
    for handler in _HANDLERS:
        handler.close()
    _HANDLERS.clear()
    _LOGGERS.clear()


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_file_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.remove_task_file_handler()


M
mamingshuai 已提交
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
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()


W
wenjun 已提交
327 328
def _init_global_logger(name=None):
    handler = logging.StreamHandler(sys.stdout)
329 330
    log_format = \
        "[%(asctime)s] [%(thread)d] [%(name)s] [%(levelname)s] [%(message)s]"
W
wenjun 已提交
331
    handler.setFormatter(logging.Formatter(log_format))
M
mamingshuai 已提交
332 333 334
    log = FrameworkLog(name)
    log.platform_log.setLevel(logging.INFO)
    log.platform_log.addHandler(handler)
W
wenjun 已提交
335
    return log
M
mamingshuai 已提交
336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383


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)

            # change level of loggers which will to be instantiated.
            # Actually, it changes the level attribute in ToolLog,
            # which will be used when instantiating the FrameLog object.
            plugins = get_plugin(Plugin.LOG, LogType.tool)
            for log_plugin in plugins:
                log_plugin.set_level(logger_level)
            # change level of loggers which have instantiated
            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):
        RotatingFileHandler.__init__(self, 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"

        # baseFilename is the attribute in FileHandler
        base_file_name = getattr(self, "baseFilename", None)
D
deveco_test 已提交
384 385
        with open(base_file_name, self.mode) as handler:
            return handler
M
mamingshuai 已提交
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403

    def emit(self, record):
        try:
            if not self._encrypt_valid():
                return

            # shouldRoller and doRoller is the method in RotatingFileHandler
            should_rollover = getattr(self, "shouldRollover", None)
            if callable(should_rollover) and should_rollover(record):
                self.doRollover()

            # stream is the attribute in StreamHandler
            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()
D
deveco_test 已提交
404
        except RecursionError as _:  # pylint:disable=undefined-variable
M
mamingshuai 已提交
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
            raise

    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

    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
429 430 431
        info = "[%s] [%s] [%s] [%s] %s%s" \
               % (create_time, threading.currentThread().ident, name,
                  level_name, msg, "\n")
M
mamingshuai 已提交
432 433 434 435 436 437

        try:
            return do_rsa_encrypt(info)
        except ParamError as error:
            error_no_str = \
                "ErrorNo={}".format(getattr(error, "error_no", "00113"))
438 439 440
            info = "[%s] [%s] [%s] [%s] [%s] [%s]\n" % (
                create_time, threading.currentThread().ident,
                name, "ERROR", error, error_no_str)
M
mamingshuai 已提交
441 442
            self.encrypt_error = bytes(info, "utf-8")
            return self.encrypt_error