Skip to content

logger

Core logging module for pyRevit.

Attributes

LOG_REC_FORMAT = '%(levelname)s [%(name)s] %(message)s' module-attribute

LOG_REC_FORMAT_HEADER = coreutils.prepare_html_str('<strong>%(levelname)s</strong> [%(name)s] %(message)s') module-attribute

LOG_REC_FORMAT_HEADER_NO_NAME = coreutils.prepare_html_str('<strong>%(levelname)s</strong>\n%(message)s') module-attribute

LOG_REC_FORMAT_EMOJI = '{emoji} %(levelname)s [%(name)s] %(message)s' module-attribute

LOG_REC_FORMAT_FILE = '%(asctime)s %(levelname)s [%(name)s] %(message)s' module-attribute

LOG_REC_FORMAT_FILE_C = '%(asctime)s %(levelname)s [<{}> %(name)s] %(message)s' module-attribute

LOG_REC_FORMAT_HTML = coreutils.prepare_html_str('<div class="logdefault {style}">{message}</div>') module-attribute

LOG_REC_CLASS_ERROR = 'logerror' module-attribute

LOG_REC_FORMAT_ERROR = LOG_REC_FORMAT_HTML.format(style=LOG_REC_CLASS_ERROR, message=LOG_REC_FORMAT_HEADER) module-attribute

LOG_REC_CLASS_WARNING = 'logwarning' module-attribute

LOG_REC_FORMAT_WARNING = LOG_REC_FORMAT_HTML.format(style=LOG_REC_CLASS_WARNING, message=LOG_REC_FORMAT_HEADER) module-attribute

LOG_REC_CLASS_CRITICAL = 'logcritical' module-attribute

LOG_REC_FORMAT_CRITICAL = LOG_REC_FORMAT_HTML.format(style=LOG_REC_CLASS_CRITICAL, message=LOG_REC_FORMAT_HEADER) module-attribute

LOG_REC_CLASS_SUCCESS = 'logsuccess' module-attribute

LOG_REC_FORMAT_SUCCESS = LOG_REC_FORMAT_HTML.format(style=LOG_REC_CLASS_SUCCESS, message=LOG_REC_FORMAT_HEADER_NO_NAME) module-attribute

LOG_REC_CLASS_DEPRECATE = 'logdeprecate' module-attribute

LOG_REC_FORMAT_DEPRECATE = LOG_REC_FORMAT_HTML.format(style=LOG_REC_CLASS_DEPRECATE, message=LOG_REC_FORMAT_HEADER_NO_NAME) module-attribute

DEFAULT_LOGGING_LEVEL = logging.WARNING module-attribute

DEPRECATE_LOG_LEVEL = 25 module-attribute

SUCCESS_LOG_LEVEL = 80 module-attribute

FILE_LOG_FILENAME = '{}runtime.log'.format(PYREVIT_FILE_PREFIX_STAMPED) module-attribute

FILE_LOG_FILEPATH = op.join(PYREVIT_VERSION_APP_DIR, FILE_LOG_FILENAME) module-attribute

FILE_LOGGING_DEFAULT_STATE = False module-attribute

stdout_hndlr = logging.StreamHandler(sys.stdout) module-attribute

default_formatter = logging.Formatter(LOG_REC_FORMAT) module-attribute

formatters = {SUCCESS_LOG_LEVEL: logging.Formatter(LOG_REC_FORMAT_SUCCESS), logging.ERROR: logging.Formatter(LOG_REC_FORMAT_ERROR), logging.WARNING: logging.Formatter(LOG_REC_FORMAT_WARNING), logging.CRITICAL: logging.Formatter(LOG_REC_FORMAT_CRITICAL), DEPRECATE_LOG_LEVEL: logging.Formatter(LOG_REC_FORMAT_DEPRECATE)} module-attribute

file_hndlr = logging.FileHandler(FILE_LOG_FILEPATH, mode='a', delay=True) module-attribute

file_formatter = logging.Formatter(LOG_REC_FORMAT_FILE) module-attribute

loggers = {} module-attribute

Classes

DispatchingFormatter(log_formatters, log_default_formatter)

Bases: object

Dispatching formatter to format by log level.

Parameters:

Name Type Description Default
log_formatters (dict[int

logging.Formatter]): dict of level:formatter key pairs

required
log_default_formatter Formatter

default formatter

required
Source code in pyrevitlib/pyrevit/coreutils/logger.py
def __init__(self, log_formatters, log_default_formatter):
    self._formatters = log_formatters
    self._default_formatter = log_default_formatter

Functions

format(record)

Format given record by log level.

Source code in pyrevitlib/pyrevit/coreutils/logger.py
def format(self, record):
    """Format given record by log level."""
    formatter = self._formatters.get(record.levelno,
                                     self._default_formatter)
    return formatter.format(record)

LoggerWrapper(*args)

Bases: Logger

Custom logging object.

Source code in pyrevitlib/pyrevit/coreutils/logger.py
def __init__(self, *args):
    logging.Logger.__init__(self, *args)
    self._has_errors = False
    self._filelogstate = False
    self._curlevel = DEFAULT_LOGGING_LEVEL

Functions

findCaller(stack_info=False, stacklevel=1)

Override findCaller for IronPython 3 compatibility.

IronPython 3.4 has issues with stack frame inspection that causes IndexError when the logging module tries to find caller information. This override returns safe default values for IronPython 3.

Source code in pyrevitlib/pyrevit/coreutils/logger.py
def findCaller(self, stack_info=False, stacklevel=1):
    """Override findCaller for IronPython 3 compatibility.

    IronPython 3.4 has issues with stack frame inspection that causes
    IndexError when the logging module tries to find caller information.
    This override returns safe default values for IronPython 3.
    """
    if IRONPY3:
        return ("(unknown)", 0, "(unknown)", None)
    else:
        return logging.Logger.findCaller(self)
callHandlers(record)

Override logging.Logger.callHandlers.

Source code in pyrevitlib/pyrevit/coreutils/logger.py
def callHandlers(self, record):
    """Override logging.Logger.callHandlers."""
    for hdlr in self.handlers:
        try:
            # stream-handler only records based on current level
            if isinstance(hdlr, logging.StreamHandler) \
                    and record.levelno >= self._curlevel:
                if IRONPY3:
                    # Direct write for IronPython 3 to avoid handler internals
                    try:
                        msg = hdlr.format(record)
                        hdlr.stream.write(msg + '\n')
                        hdlr.stream.flush()
                    except Exception:
                        pass
                else:
                    hdlr.handle(record)
            # file-handler must record everything
            elif isinstance(hdlr, logging.FileHandler) \
                    and self._filelogstate:
                if IRONPY3:
                    # Direct write for IronPython 3 to avoid handler internals
                    try:
                        msg = hdlr.format(record)
                        hdlr.stream.write(msg + '\n')
                        hdlr.stream.flush()
                    except Exception:
                        pass
                else:
                    hdlr.handle(record)
        except Exception:
            # Silently ignore handler errors for IronPython 3
            if not IRONPY3:
                raise
isEnabledFor(level)

Override logging.Logger.isEnabledFor.

Source code in pyrevitlib/pyrevit/coreutils/logger.py
def isEnabledFor(self, level):
    """Override logging.Logger.isEnabledFor."""
    # update current logging level and file logging state
    self._filelogstate = \
        envvars.get_pyrevit_env_var(envvars.FILELOGGING_ENVVAR)
    self._curlevel = \
        envvars.get_pyrevit_env_var(envvars.LOGGING_LEVEL_ENVVAR)

    # the loader assembly sets EXEC_PARAMS.debug_mode to true if
    # user Ctrl-clicks on the button at script runtime.
    if EXEC_PARAMS.debug_mode:
        self._curlevel = logging.DEBUG

    # if file logging is disabled, return the current logging level
    # but if it's enabled, return the file logging level so the record
    # is generated and logged by file-handler. The stream-handler still
    # outputs the record based on the current logging level
    if self._filelogstate:
        return level >= logging.DEBUG

    return level >= self._curlevel
is_enabled_for(level)

Check if logger is enabled for level in pyRevit environment.

Source code in pyrevitlib/pyrevit/coreutils/logger.py
def is_enabled_for(self, level):
    """Check if logger is enabled for level in pyRevit environment."""
    self._curlevel = \
        envvars.get_pyrevit_env_var(envvars.LOGGING_LEVEL_ENVVAR)

    # the loader assembly sets EXEC_PARAMS.debug_mode to true if
    # user Ctrl-clicks on the button at script runtime.
    if EXEC_PARAMS.debug_mode:
        self._curlevel = logging.DEBUG

    return level >= self._curlevel
has_errors()

Check if logger has reported any errors.

Source code in pyrevitlib/pyrevit/coreutils/logger.py
def has_errors(self):
    """Check if logger has reported any errors."""
    return self._has_errors
set_level(level)

Set logging level to level.

Source code in pyrevitlib/pyrevit/coreutils/logger.py
def set_level(self, level):
    """Set logging level to level."""
    self._reset_logger_env_vars(level)
set_quiet_mode()

Activate quiet mode. All log levels are disabled.

Source code in pyrevitlib/pyrevit/coreutils/logger.py
def set_quiet_mode(self):
    """Activate quiet mode. All log levels are disabled."""
    self._reset_logger_env_vars(logging.CRITICAL)
set_verbose_mode()

Activate verbose mode. Log levels >= INFO are enabled.

Source code in pyrevitlib/pyrevit/coreutils/logger.py
def set_verbose_mode(self):
    """Activate verbose mode. Log levels >= INFO are enabled."""
    self._reset_logger_env_vars(logging.INFO)
set_debug_mode()

Activate debug mode. Log levels >= DEBUG are enabled.

Source code in pyrevitlib/pyrevit/coreutils/logger.py
def set_debug_mode(self):
    """Activate debug mode. Log levels >= DEBUG are enabled."""
    self._reset_logger_env_vars(logging.DEBUG)
reset_level()

Reset logging level back to default.

Source code in pyrevitlib/pyrevit/coreutils/logger.py
def reset_level(self):
    """Reset logging level back to default."""
    self._reset_logger_env_vars(DEFAULT_LOGGING_LEVEL)
get_level()

Return current logging level.

Source code in pyrevitlib/pyrevit/coreutils/logger.py
def get_level(self):
    """Return current logging level."""
    return envvars.get_pyrevit_env_var(envvars.LOGGING_LEVEL_ENVVAR)
log_parse_except(parsed_file, parse_ex)

Logs a file parsing exception.

Parameters:

Name Type Description Default
parsed_file str

File path that failed the parsing

required
parse_ex Exception

Parsing exception

required
Source code in pyrevitlib/pyrevit/coreutils/logger.py
def log_parse_except(self, parsed_file, parse_ex):
    """Logs a file parsing exception.

    Args:
        parsed_file (str): File path that failed the parsing
        parse_ex (Exception): Parsing exception
    """
    err_msg = '<strong>Error while parsing file:</strong>\n{file}\n' \
              '<strong>Error type:</strong> {type}\n' \
              '<strong>Error Message:</strong> {errmsg}\n' \
              '<strong>Line/Column:</strong> {lineno}/{colno}\n' \
              '<strong>Line Text:</strong> {linetext}' \
              .format(file=parsed_file,
                      type=parse_ex.__class__.__name__,
                      errmsg=parse_ex.msg if hasattr(parse_ex, 'msg') else "",
                      lineno=parse_ex.lineno if hasattr(parse_ex, 'lineno') else 0,
                      colno=parse_ex.offset if hasattr(parse_ex, 'offset') else 0,
                      linetext=parse_ex.text if hasattr(parse_ex, 'text') else "",
                      )
    self.error(coreutils.prepare_html_str(err_msg))
success(message, *args, **kws)

Log a success message.

Parameters:

Name Type Description Default
message str

success message

required
*args Any

extra agruments passed to the log function

()
**kws Any

extra agruments passed to the log function

{}
Source code in pyrevitlib/pyrevit/coreutils/logger.py
def success(self, message, *args, **kws):
    """Log a success message.

    Args:
        message (str): success message
        *args (Any): extra agruments passed to the log function
        **kws (Any): extra agruments passed to the log function
    """
    if self.isEnabledFor(SUCCESS_LOG_LEVEL):
        # Yes, logger takes its '*args' as 'args'.
        self._log(SUCCESS_LOG_LEVEL, message, args, **kws)
deprecate(message, *args, **kws)

Log a deprecation message.

Parameters:

Name Type Description Default
message str

deprecation message

required
*args Any

extra agruments passed to the log function

()
**kws Any

extra agruments passed to the log function

{}
Source code in pyrevitlib/pyrevit/coreutils/logger.py
def deprecate(self, message, *args, **kws):
    """Log a deprecation message.

    Args:
        message (str): deprecation message
        *args (Any): extra agruments passed to the log function
        **kws (Any): extra agruments passed to the log function
    """
    if self.isEnabledFor(DEPRECATE_LOG_LEVEL):
        # Yes, logger takes its '*args' as 'args'.
        self._log(DEPRECATE_LOG_LEVEL, message, args, **kws)
dev_log(source, message='')

Appends a message to a log file.

Parameters:

Name Type Description Default
source str

source of the message

required
message str

message to log

''
Source code in pyrevitlib/pyrevit/coreutils/logger.py
def dev_log(self, source, message=''):
    """Appends a message to a log file.

    Args:
        source (str): source of the message
        message (str): message to log
    """
    devlog_fname = \
        '{}.log'.format(EXEC_PARAMS.command_uniqueid or self.name)
    with open(op.join(USER_DESKTOP, devlog_fname), 'a') as devlog_file:
        devlog_file.writelines('{tstamp} [{exid}] {src}: {msg}\n'.format(
            tstamp=EXEC_PARAMS.exec_timestamp,
            exid=EXEC_PARAMS.exec_id,
            src=source,
            msg=message,
            ))

Functions

get_stdout_hndlr()

Return stdout logging handler object.

Returns:

Type Description
StreamHandler

configured instance of python's native stream handler

Source code in pyrevitlib/pyrevit/coreutils/logger.py
def get_stdout_hndlr():
    """Return stdout logging handler object.

    Returns:
        (logging.StreamHandler):
            configured instance of python's native stream handler
    """
    global stdout_hndlr     #pylint: disable=W0603

    return stdout_hndlr

get_file_hndlr()

Return file logging handler object.

Returns:

Type Description
FileHandler

configured instance of python's native stream handler

Source code in pyrevitlib/pyrevit/coreutils/logger.py
def get_file_hndlr():
    """Return file logging handler object.

    Returns:
        (logging.FileHandler):
            configured instance of python's native stream handler
    """
    global file_hndlr       #pylint: disable=W0603

    if EXEC_PARAMS.command_mode:
        cmd_file_hndlr = logging.FileHandler(FILE_LOG_FILEPATH,
                                             mode='a', delay=True)
        logformat = LOG_REC_FORMAT_FILE_C.format(EXEC_PARAMS.command_name)
        formatter = logging.Formatter(logformat)
        cmd_file_hndlr.setFormatter(formatter)
        return cmd_file_hndlr
    else:
        return file_hndlr

get_logger(logger_name)

Register and return a logger with given name.

Caches all registered loggers and returns the same logger object on second call with the same logger name.

Parameters:

Name Type Description Default
logger_name str

logger name

required

Returns:

Type Description
LoggerWrapper

logger object wrapper python's native logger

Examples:

get_logger('my command')

Source code in pyrevitlib/pyrevit/coreutils/logger.py
def get_logger(logger_name):
    """Register and return a logger with given name.

    Caches all registered loggers and returns the same logger object on
    second call with the same logger name.

    Args:
        logger_name (str): logger name

    Returns:
        (LoggerWrapper): logger object wrapper python's native logger

    Examples:
        ```python
        get_logger('my command')
        ```
        <LoggerWrapper ...>
    """
    if loggers.get(logger_name):
        return loggers.get(logger_name)
    else:
        logger = logging.getLogger(logger_name)    # type: LoggerWrapper
        logger.addHandler(get_stdout_hndlr())
        logger.propagate = False
        logger.addHandler(get_file_hndlr())
        loggers.update({logger_name: logger})
        return logger

set_file_logging(status)

Set file logging status (enable/disable).

Parameters:

Name Type Description Default
status bool

True to enable, False to disable

required
Source code in pyrevitlib/pyrevit/coreutils/logger.py
def set_file_logging(status):
    """Set file logging status (enable/disable).

    Args:
        status (bool): True to enable, False to disable
    """
    envvars.set_pyrevit_env_var(envvars.FILELOGGING_ENVVAR, status)

loggers_have_errors()

Check if any errors have been reported by any of registered loggers.

Source code in pyrevitlib/pyrevit/coreutils/logger.py
def loggers_have_errors():
    """Check if any errors have been reported by any of registered loggers."""
    for logger in loggers.values():
        if logger.has_errors():
            return True
    return False