Source code for robot_designer_plugin.core.logfile

# #####
# This file is part of the RobotDesigner of the Neurorobotics subproject (SP10)
# in the Human Brain Project (HBP).
# It has been forked from the RobotEditor (https://gitlab.com/h2t/roboteditor)
# developed at the Karlsruhe Institute of Technology in the
# High Performance Humanoid Technologies Laboratory (H2T).
# #####

# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  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 General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####

# #####
#
# Copyright (c) 2016, FZI Forschungszentrum Informatik
#
# Changes:
#
#   2016-01-15: Stefan Ulbrich (FZI), Major refactoring. Integrated into complex plugin framework.
#
# ######

"""
This sub package provides logging mechanism for the plugin development.

Blender is not easy to debug (adding an external debugger is planned) such that providing informative
output to a log file is mandatory.
The log file is stored in the path stored in the plugin directory (:data:`.config.script_path`) in
``resources/log.txt``.

An example of a log file:


.. literalinclude:: ../../../doc/source/developer_manual/log_example.txt

Logging of operators can be automated with the decorators and base classes in the :mod:`.operators` module.
"""

import logging
import sys
import traceback
import os

from .gui import InfoBox

from ..core.config import BACKTRACE_MESSAGE_CALLSTACK, BACKTRACE_MESSAGE, BACKTRACE_FILTER_FUNC, \
    BACKTRACE_FILTER_HIDE_CODE, BACKTRACE_MESSAGE_STACK, BACKTRACE_MESSAGE_STACK_CODE, EXCEPTION_MESSAGE, script_path

from importlib import reload

reload(logging)
logging.basicConfig(format='[%(levelname)5s|%(name)10s|%(filename)12s:%(lineno)03d|%(funcName)s()] %(message)s',
                    filename=os.path.join(script_path, 'resources/log.txt'), filemode='w')

# Also log to stdout
logging.getLogger().addHandler(logging.StreamHandler(sys.stdout))

operator_logger = logging.getLogger('Operators')
'''
Logging object associated with :term:`operators`. All loggers store to ``resources/log.txt``
'''
export_logger = logging.getLogger('Export')
'''
Logging object associated with :term:`operators`. All loggers store to ``resources/log.txt``
'''
core_logger = logging.getLogger('Core')
'''
Logging object associated with the :term:`plugin core`. All loggers store to ``resources/log.txt``
'''
gui_logger = logging.getLogger('GUI')
'''
Logging object associated with the user interface. All loggers store to ``resources/log.txt``
'''
prop_logger = logging.getLogger('Properties')
'''
Logging object associated with :term:`properties`. All loggers store to ``resources/log.txt``
'''

operator_logger.setLevel(logging.INFO)
export_logger.setLevel(logging.INFO)
core_logger.setLevel(logging.INFO)
gui_logger.setLevel(logging.INFO)
prop_logger.setLevel(logging.INFO)


[docs]def LogFunction(func): """ Logging / exception handling decorator for functions (use for draw function for instance). :param func: The function to decorate :return: A :term:`decorator` """ def func_logger(self, context): # Execute the Operator try: return func(self, context) except Exception as e: gui_logger.error("Draw function in {} module threw an exception:\n {} {} {} {} {}".format( func.__module__, EXCEPTION_MESSAGE, type(e).__name__, e, log_callstack(), log_callstack(back_trace=True))) InfoBox.global_messages.append(e) return func_logger
[docs]def log_callstack(back_trace=False): """ Helper function that formats either a (filtered) backtrace or call stack in a string. Blender internals are filtered such that errors in the own code can be detected more easily. :param back_trace: If true, the backtrace is returned. Otherwise, the call stack is returned. :return: the formatted call stack/backtrace in a string. """ if not back_trace: message = BACKTRACE_MESSAGE_CALLSTACK % len([i for i in traceback.extract_stack() if i[2] == 'run']) stack = traceback.extract_stack()[:-1] else: message = BACKTRACE_MESSAGE stack = traceback.extract_tb(sys.exc_info()[2]) last_call = "" for path, line, func, code in stack: if 'addons' in path: file = '...' + path[path.find('addons') + 6:] elif 'scripts' in path: file = '...' + path[path.find('scripts') + 7:] else: file = path if func not in BACKTRACE_FILTER_FUNC: if func in BACKTRACE_FILTER_HIDE_CODE: message += BACKTRACE_MESSAGE_STACK.format(func, file, line) else: message += BACKTRACE_MESSAGE_STACK_CODE.format(func, file, line, code, last_call) last_call = code return message
[docs]def log_callstack_last(back_trace=False): if not back_trace: stack = traceback.extract_stack()[:-1] else: stack = traceback.extract_tb(sys.exc_info()[2]) message = "empty" print("Parsing stack") for path, line, func, code in stack: print(path, func) if func not in BACKTRACE_FILTER_FUNC: if func not in BACKTRACE_FILTER_HIDE_CODE: file = os.path.split(path)[-1] message = "{}:{}".format(file, line) return message