]> Frank Brehm's Git Trees - pixelpark/admin-tools.git/commitdiff
Adding possibility for a LOG configuration file
authorFrank Brehm <frank.brehm@pixelpark.com>
Fri, 17 Mar 2017 10:23:30 +0000 (11:23 +0100)
committerFrank Brehm <frank.brehm@pixelpark.com>
Fri, 17 Mar 2017 10:23:30 +0000 (11:23 +0100)
pp_lib/app.py
pp_lib/cfg_app.py

index 5c42c0c4ecda5ad5b41cac53fcfe6dcacdbba1fe..92bd7063e5dbfef6625759fb6e9945383e22f946 100644 (file)
@@ -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
 
index 48f3850275603a464b6cea488f7161f447ce0cb2..ac6b4acd9a3e3c76f0cdb7067fa52205274aa0cb 100644 (file)
@@ -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 <WORKDIR>/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):