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
from .app import PpApplication
-__version__ = '0.3.2'
+__version__ = '0.3.3'
LOG = logging.getLogger(__name__)
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,
else:
self.cfg_stems = self.appname
- self.init_cfgfiles()
+ self._init_cfgfiles()
enc = getattr(self.args, 'cfg_encoding', None)
if enc:
self._read_config()
+ self._init_log_cfgfiles()
+ self.reinit_logging()
+
# -----------------------------------------------------------
@property
def need_config_file(self):
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,
)
# -------------------------------------------------------------------------
- def init_cfgfiles(self):
+ def _init_cfgfiles(self):
"""Method to generate the self.cfg_files list."""
self.cfg_files = []
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):