1
0
mirror of https://github.com/EDCD/EDMarketConnector.git synced 2025-06-07 19:03:23 +03:00

Logging: Make correct loggers for 'found' plugins

* Log messages propagate up Parent.Child chains, so we don't need a
 channel on the plugin logger.
* But it still needs the filter to define qualname and class for
 formatting.
This commit is contained in:
Athanasius 2020-07-30 15:33:55 +01:00
parent 3dab0e9d91
commit 898ff9fbb2
2 changed files with 42 additions and 5 deletions

View File

@ -35,6 +35,8 @@ from typing import Tuple
# #
# 14. Call from *package* # 14. Call from *package*
_default_loglevel = logging.DEBUG
class Logger: class Logger:
""" """
@ -47,7 +49,7 @@ class Logger:
Users of this class should then call getLogger() to get the Users of this class should then call getLogger() to get the
logging.Logger instance. logging.Logger instance.
""" """
def __init__(self, logger_name: str, loglevel: int = logging.DEBUG): def __init__(self, logger_name: str, loglevel: int = _default_loglevel):
""" """
Set up a `logging.Logger` with our preferred configuration. Set up a `logging.Logger` with our preferred configuration.
This includes using an EDMCContextFilter to add 'class' and 'qualname' This includes using an EDMCContextFilter to add 'class' and 'qualname'
@ -78,6 +80,33 @@ class Logger:
return self.logger return self.logger
def get_plugin_logger(name: str, loglevel: int = _default_loglevel) -> logging.Logger:
"""
'Found' plugins need their own logger to call out where the logging is
coming from, but we don't need to set up *everything* for them.
The name will be '{config.appname}.{plugin.name}', e.g.
'EDMarketConnector.miggytest'. This means that any logging sent through
there *also* goes to the channels defined in the 'EDMarketConnector'
logger, so we can let that take care of the formatting.
If we add our own channel then the output gets duplicated (assuming same
logLevel set).
However we do need to attach our filter to this still. That's not at
the channel level.
:param name: Name of this Logger.
:param loglevel: Optional logLevel for this Logger.
:return: logging.Logger instance, all set up.
"""
plugin_logger = logging.getLogger(name)
plugin_logger.setLevel(loglevel)
plugin_logger.addFilter(EDMCContextFilter())
return plugin_logger
class EDMCContextFilter(logging.Filter): class EDMCContextFilter(logging.Filter):
""" """
logging.Filter sub-class to place extra attributes of the calling site logging.Filter sub-class to place extra attributes of the calling site

16
plug.py
View File

@ -8,12 +8,14 @@ import importlib
import sys import sys
import operator import operator
import threading # noqa: F401 - We don't use it, but plugins might import threading # noqa: F401 - We don't use it, but plugins might
from typing import Optional
import logging import logging
import tkinter as tk import tkinter as tk
import myNotebook as nb # noqa: N813 import myNotebook as nb # noqa: N813
from config import config, appname from config import config, appname
import EDMCLogging
logger = logging.getLogger(appname) logger = logging.getLogger(appname)
@ -79,7 +81,7 @@ last_error = {
class Plugin(object): class Plugin(object):
def __init__(self, name, loadfile): def __init__(self, name: str, loadfile: str, plugin_logger: Optional[logging.Logger]):
""" """
Load a single plugin Load a single plugin
:param name: module name :param name: module name
@ -90,6 +92,7 @@ class Plugin(object):
self.name = name # Display name. self.name = name # Display name.
self.folder = name # basename of plugin folder. None for internal plugins. self.folder = name # basename of plugin folder. None for internal plugins.
self.module = None # None for disabled plugins. self.module = None # None for disabled plugins.
self.logger = plugin_logger
if loadfile: if loadfile:
logger.info(f'loading plugin "{name.replace(".", "_")}" from "{loadfile}"') logger.info(f'loading plugin "{name.replace(".", "_")}" from "{loadfile}"')
@ -173,7 +176,7 @@ def load_plugins(master):
for name in sorted(os.listdir(config.internal_plugin_dir)): for name in sorted(os.listdir(config.internal_plugin_dir)):
if name.endswith('.py') and not name[0] in ['.', '_']: if name.endswith('.py') and not name[0] in ['.', '_']:
try: try:
plugin = Plugin(name[:-3], os.path.join(config.internal_plugin_dir, name)) plugin = Plugin(name[:-3], os.path.join(config.internal_plugin_dir, name), logger)
plugin.folder = None # Suppress listing in Plugins prefs tab plugin.folder = None # Suppress listing in Plugins prefs tab
internal.append(plugin) internal.append(plugin)
except Exception as e: except Exception as e:
@ -191,12 +194,17 @@ def load_plugins(master):
pass pass
elif name.endswith('.disabled'): elif name.endswith('.disabled'):
name, discard = name.rsplit('.', 1) name, discard = name.rsplit('.', 1)
found.append(Plugin(name, None)) found.append(Plugin(name, None, logger))
else: else:
try: try:
# Add plugin's folder to load path in case plugin has internal package dependencies # Add plugin's folder to load path in case plugin has internal package dependencies
sys.path.append(os.path.join(config.plugin_dir, name)) sys.path.append(os.path.join(config.plugin_dir, name))
found.append(Plugin(name, os.path.join(config.plugin_dir, name, 'load.py')))
# Create a logger for this 'found' plugin. Must be before the
# load.py is loaded.
plugin_logger = EDMCLogging.get_plugin_logger(f'{appname}.{name}')
found.append(Plugin(name, os.path.join(config.plugin_dir, name, 'load.py'), plugin_logger))
except Exception as e: except Exception as e:
logger.exception(f'Failure loading found Plugin "{name}"') logger.exception(f'Failure loading found Plugin "{name}"')
pass pass