From: Frank Brehm Date: Fri, 17 Mar 2017 10:23:30 +0000 (+0100) Subject: Adding possibility for a LOG configuration file X-Git-Tag: 0.1.2~256 X-Git-Url: https://git.uhu-banane.de/?a=commitdiff_plain;h=e385a4d67af8680291c0fd1d634d2b316d58c46e;p=pixelpark%2Fadmin-tools.git Adding possibility for a LOG configuration file --- diff --git a/pp_lib/app.py b/pp_lib/app.py index 5c42c0c..92bd706 100644 --- a/pp_lib/app.py +++ b/pp_lib/app.py @@ -33,7 +33,7 @@ from .colored import ColoredFormatter, colorstr from .obj import PpBaseObjectError, PpBaseObject -__version__ = '0.2.1' +__version__ = '0.2.2' LOG = logging.getLogger(__name__) @@ -240,9 +240,9 @@ class PpApplication(PpBaseObject): retval = int(retval) trace = bool(trace) - root_log = logging.getLogger() + root_logger = logging.getLogger() has_handlers = False - if root_log.handlers: + if root_logger.handlers: has_handlers = True if msg: @@ -301,10 +301,10 @@ class PpApplication(PpBaseObject): @return: None """ - root_log = logging.getLogger() - root_log.setLevel(logging.INFO) + root_logger = logging.getLogger() + root_logger.setLevel(logging.INFO) if self.verbose: - root_log.setLevel(logging.DEBUG) + root_logger.setLevel(logging.DEBUG) # create formatter format_str = '' @@ -331,7 +331,7 @@ class PpApplication(PpBaseObject): lh_console.setLevel(logging.INFO) lh_console.setFormatter(formatter) - root_log.addHandler(lh_console) + root_logger.addHandler(lh_console) return diff --git a/pp_lib/cfg_app.py b/pp_lib/cfg_app.py index 48f3850..ac6b4ac 100644 --- a/pp_lib/cfg_app.py +++ b/pp_lib/cfg_app.py @@ -12,10 +12,14 @@ from __future__ import absolute_import import sys import os import logging +import logging.config import re import traceback import textwrap import datetime +import json + +from json import JSONDecodeError # Third party modules import six @@ -38,7 +42,7 @@ from .rec_dict import RecursiveDictionary from .app import PpApplication -__version__ = '0.3.2' +__version__ = '0.3.3' LOG = logging.getLogger(__name__) @@ -71,6 +75,7 @@ class PpConfigApplication(PpApplication): self._cfg_dir = None self.cfg_stems = [] self.cfg_files = [] + self.log_cfg_files = [] super(PpConfigApplication, self).__init__( appname=appname, verbose=verbose, version=version, base_dir=base_dir, @@ -105,7 +110,7 @@ class PpConfigApplication(PpApplication): else: self.cfg_stems = self.appname - self.init_cfgfiles() + self._init_cfgfiles() enc = getattr(self.args, 'cfg_encoding', None) if enc: @@ -118,6 +123,9 @@ class PpConfigApplication(PpApplication): self._read_config() + self._init_log_cfgfiles() + self.reinit_logging() + # ----------------------------------------------------------- @property def need_config_file(self): @@ -172,6 +180,14 @@ class PpConfigApplication(PpApplication): help="Configuration files to use additional to the standard configuration files.", ) + self.arg_parser.add_argument( + "--log-cfgfile", + metavar="FILE", dest="log_cfgfile", + help=("Configuration file for logging in JSON format. " + "See https://docs.python.org/3/library/logging.config.html#logging-config-dictschema " + "how the structures has to be defined.") + ) + self.arg_parser.add_argument( "--cfg-encoding", metavar="ENCODING", dest="cfg_encoding", default=self.cfg_encoding, @@ -180,7 +196,7 @@ class PpConfigApplication(PpApplication): ) # ------------------------------------------------------------------------- - def init_cfgfiles(self): + def _init_cfgfiles(self): """Method to generate the self.cfg_files list.""" self.cfg_files = [] @@ -229,6 +245,118 @@ class PpConfigApplication(PpApplication): for usercfg_fn in cmdline_cfg: self.cfg_files.append(usercfg_fn) + # ------------------------------------------------------------------------- + def _init_log_cfgfiles(self): + """Method to generate the self.log_cfg_files list.""" + + self.log_cfg_files = [] + + cfg_basename = 'logging.json' + + # add /etc/app/logging.json or $VIRTUAL_ENV/etc/app/logging.json + etc_dir = os.sep + 'etc' + if 'VIRTUAL_ENV' in os.environ: + etc_dir = os.path.join(os.environ['VIRTUAL_ENV'], 'etc') + syscfg_fn = None + if self.cfg_dir: + syscfg_fn = os.path.join(etc_dir, self.cfg_dir, cfg_basename) + else: + syscfg_fn = os.path.join(etc_dir, cfg_basename) + self.log_cfg_files.append(syscfg_fn) + + # add /etc/app.ini + mod_dir = os.path.dirname(__file__) + work_dir = os.path.abspath(os.path.join(mod_dir, '..')) + work_etc_dir = os.path.join(work_dir, 'etc') + if self.verbose > 1: + LOG.debug("Searching for {!r} ...".format(work_etc_dir)) + self.log_cfg_files.append(os.path.join(work_etc_dir, cfg_basename)) + + # add $HOME/.config/app.ini + usercfg_fn = None + user_cfg_dir = os.path.expanduser('~/.config') + if user_cfg_dir: + if self.cfg_dir: + user_cfg_dir = os.path.join(user_cfg_dir, self.cfg_dir) + if self.verbose > 1: + LOG.debug("user_cfg_dir: {!r}".format(user_cfg_dir)) + usercfg_fn = os.path.join(user_cfg_dir, cfg_basename) + self.log_cfg_files.append(usercfg_fn) + + # add a configfile given on command line with --log-cfgfile + cmdline_cfg = getattr(self.args, 'log_cfgfile', None) + if cmdline_cfg: + self.log_cfg_files.append(cmdline_cfg) + + if self.verbose > 1: + LOG.debug("Log config files:\n{}".format(pp(self.log_cfg_files))) + + # ------------------------------------------------------------------------- + def _init_logging_from_jsonfile(self): + + open_opts = {} + if six.PY3: + open_opts['encoding'] = 'utf-8' + open_opts['errors'] = 'surrogateescape' + + found = False + for cfg_file in reversed(self.log_cfg_files): + + if self.verbose > 1: + LOG.debug("Searching for {!r} ...".format(cfg_file)) + + if not os.path.exists(cfg_file): + continue + if not os.path.isfile(cfg_file): + continue + if not os.access(cfg_file, os.R_OK): + msg = "No read access to {!r}.".format(cfg_file) + self.handle_error(msg, "File error") + continue + + log_cfg = None + if self.verbose > 1: + LOG.debug("Reading and evaluating {!r} ...".format(cfg_file)) + with open(cfg_file, 'r', **open_opts) as fh: + try: + log_cfg = json.load(fh) + except JSONDecodeError as e: + msg = "Wrong file {!r} - ".format(cfg_file) + str(e) + self.handle_error(msg, e.__class__.__name__) + continue + try: + logging.config.dictConfig(log_cfg) + except Exception as e: + msg = "Wrong file {!r} - ".format(cfg_file) + str(e) + self.handle_error(msg, e.__class__.__name__) + continue + found = True + break + + return found + + # ------------------------------------------------------------------------- + def reinit_logging(self): + """ + Re-Initialize the logger object. + It creates a colored loghandler with all output to STDERR. + Maybe overridden in descendant classes. + + @return: None + """ + + root_logger = logging.getLogger() + + for log_handler in root_logger.handlers: + root_logger.removeHandler(log_handler) + + if self._init_logging_from_jsonfile(): + return + + self.init_logging() + + return + # ------------------------------------------------------------------------- def _read_config(self):