# -*- coding: utf-8 -*-
"""
This package provides the qudi logging facility. It facilitates Pythonic logging by joining Qt log
into native Python logging. Also installs a logging handler that emits a Qt Signal to tap into
every time a log event is registered.
Automatically installs all important logging handlers for qudi to work but also allows for
registering (and removal) of additional handlers.
Installs a rotating log file handler to write log messages to disk.
.. Copyright (c) 2021, the qudi developers. See the AUTHORS.md file at the top-level directory of this
.. distribution and on <https://github.com/Ulm-IQO/qudi-core/>
..
.. This file is part of qudi.
..
.. Qudi is free software: you can redistribute it and/or modify it under the terms of
.. the GNU Lesser General Public License as published by the Free Software Foundation,
.. either version 3 of the License, or (at your option) any later version.
..
.. Qudi 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 Lesser General Public License for more details.
..
.. You should have received a copy of the GNU Lesser General Public License along with qudi.
.. If not, see <https://www.gnu.org/licenses/>.
"""
__all__ = (
'clear_handlers',
'get_handler',
'get_file_handler',
'get_logger',
'get_record_table_model',
'get_signal_handler',
'get_stderr_handler',
'init_record_model_handler',
'init_rotating_file_handler',
'register_handler',
'set_log_level',
'unregister_handler',
)
import os
import logging
import warnings
from logging.handlers import RotatingFileHandler
from PySide2.QtCore import qInstallMessageHandler
from .handlers import LogSignalHandler, LogTableModelHandler, qt_message_handler
# global variables
# Keep track of all handlers that have been registered to the qudi
_handlers = dict()
# default handlers for qudi root logger
_signal_handler = None
_file_handler = None
_stream_handler = None
_table_model_handler = None
# The qudi root logger for all loggers created with this module API
_qudi_root_logger = None
# Register Qt message handler
qInstallMessageHandler(qt_message_handler)
# initialize logging module
logging.basicConfig(format='%(message)s', level=logging.WARNING)
logging.addLevelName(logging.CRITICAL, 'critical')
logging.addLevelName(logging.ERROR, 'error')
logging.addLevelName(logging.WARNING, 'warning')
logging.addLevelName(logging.INFO, 'info')
logging.addLevelName(logging.DEBUG, 'debug')
logging.addLevelName(logging.NOTSET, 'not set')
logging.captureWarnings(True)
# set level of stream handler which logs to stderr
if len(logging.getLogger().handlers) < 1:
_stream_handler = logging.StreamHandler()
logging.getLogger().addHandler(_stream_handler)
else:
_stream_handler = logging.getLogger().handlers[0]
_stream_handler.setLevel(logging.WARNING)
# Create qudi root logger
_qudi_root_logger = logging.getLogger('qudi')
_qudi_root_logger.setLevel(logging.INFO)
# _qudi_root_logger.propagate = False
# Create and register signal handler in root logger
_signal_handler = LogSignalHandler()
logging.getLogger().addHandler(_signal_handler)
def register_handler(name, handler, silent=False):
global _handlers
if name in _handlers:
if silent:
unregister_handler(name)
else:
raise KeyError(
f'Unable to register new logging handler. Handler by name "{name}" '
f'already registered.'
)
logging.getLogger().addHandler(handler)
_handlers[name] = handler
def unregister_handler(name, silent=False):
global _handlers
handler = _handlers.pop(name, None)
if handler is None:
if not silent:
raise KeyError(
f'Unable to unregister logging handler. No handler registered by name "{name}".'
)
else:
logging.getLogger().removeHandler(handler)
def clear_handlers():
global _handlers
root_logger = logging.getLogger()
for name in tuple(_handlers):
root_logger.removeHandler(_handlers.pop(name))
def get_handler(name):
return _handlers.get(name, None)
[docs]
def get_logger(name):
return _qudi_root_logger.getChild(name.split('qudi.', 1)[-1])
def set_log_level(level):
_signal_handler.setLevel(level)
_qudi_root_logger.setLevel(level)
if _table_model_handler is not None:
_table_model_handler.setLevel(level)
if _file_handler is not None:
_file_handler.setLevel(level)
def get_record_table_model():
return None if _table_model_handler is None else _table_model_handler.table_model
def get_signal_handler():
return _signal_handler
def get_stderr_handler():
return _stream_handler
def get_file_handler():
return _file_handler
def init_record_model_handler(max_records=10000):
global _table_model_handler
if _table_model_handler is not None:
logging.getLogger().removeHandler(_table_model_handler)
_table_model_handler = None
_table_model_handler = LogTableModelHandler(
level=_qudi_root_logger.level, max_records=max_records
)
logging.getLogger().addHandler(_table_model_handler)
def init_rotating_file_handler(
path='', filename='qudi.log', max_bytes=1024**3, backup_count=5
):
global _file_handler
# Remove file handler if it has already been registered
if _file_handler is not None:
_file_handler.doRollover()
logging.getLogger().removeHandler(_file_handler)
_file_handler = None
filepath = os.path.join(path, filename)
session_limit = 1000
session_count = 1
while session_count <= session_limit:
try:
# Start new file if old logfiles exist
do_rollover = os.path.exists(filepath) and os.stat(filepath).st_size > 0
_file_handler = RotatingFileHandler(
filepath, maxBytes=max_bytes, backupCount=backup_count
)
_file_handler.setFormatter(
logging.Formatter(
'%(asctime)s %(levelname)s %(name)s %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
)
)
_file_handler.setLevel(_qudi_root_logger.level)
if do_rollover:
_file_handler.doRollover()
except PermissionError:
session_count += 1
if session_count > session_limit:
warnings.warn(
f'Unable to initialize logger rotating file handler. OS denied '
f'access to log file or there are more than {session_limit:d} qudi '
f'sessions running.'
)
return
split_filename = filename.rsplit('.', 1)
if len(split_filename) == 2:
new_filename = (
f'{split_filename[0]}_session{session_count:d}.{split_filename[1]}'
)
else:
new_filename = f'{filename}_session{session_count:d}'
filepath = os.path.join(path, new_filename)
else:
logging.getLogger().addHandler(_file_handler)
break