Skip to content

Commit

Permalink
Merge pull request powerline#1495 from ZyX-I/extended-logging
Browse files Browse the repository at this point in the history
Add more logging options
  • Loading branch information
ZyX-I committed Nov 22, 2015
2 parents 649757a + 3f59edc commit eaa772f
Show file tree
Hide file tree
Showing 12 changed files with 813 additions and 123 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ matrix:
- python: "3.2"
- python: "3.3"
- python: "3.4"
- python: "3.5"
- python: "pypy"
- python: "pypy3"
- python: "2.6"
Expand Down
13 changes: 13 additions & 0 deletions docs/source/configuration/local.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Vim overrides

Vim configuration can be overridden using the following options:

.. _local-configuration-overrides-vim-config:

``g:powerline_config_overrides``
Dictionary, recursively merged with contents of
:file:`powerline/config.json`.
Expand All @@ -37,6 +39,17 @@ Vim configuration can be overridden using the following options:
was configured in :ref:`log_* options <config-common-log>`. Level is always
:ref:`log_level <config-common-log_level>`, same for format.

.. warning::
This variable is deprecated. Use :ref:`log_file option
<config-common-log>` in conjunction with
:py:class:`powerline.vim.VimVarHandler` class and :ref:`Vim config
overrides variable <local-configuration-overrides-vim-config>`. Using
this is also the only variant to make saving into the environment
variable the *only* place where log is saved or save into different
variable.

.. autoclass:: powerline.vim.VimVarHandler

.. _local-configuration-overrides-script:

Powerline script overrides
Expand Down
28 changes: 26 additions & 2 deletions docs/source/configuration/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,38 @@ Common configuration is a subdictionary that is a value of ``common`` key in
.. _config-common-log:

``log_file``
Defines path which will hold powerline logs. If not present, logging will be
done to stderr.
Defines how logs will be handled. There are three variants here:

#. Absent. In this case logging will be done to stderr: equivalent to
``[["logging.StreamHandler", []]]`` or ``[null]``.
#. Plain string. In this case logging will be done to the given file:
``"/file/name"`` is equivalent to ``[["logging.FileHandler",
[["/file/name"]]]]`` or ``["/file/name"]``. Leading ``~/`` is expanded in
the file name, so using ``"~/.log/foo"`` is permitted. If directory
pointed by the option is absent, it will be created, but not its parent.
#. List of handler definitions. Handler definition may either be ``null``,
a string or a list with two or three elements:

#. Logging class name and module. If module name is absent, it is
equivalent to ``logging.handlers``.
#. Class constructor arguments in a form ``[[args[, kwargs]]]``: accepted
variants are ``[]`` (no arguments), ``[args]`` (e.g.
``[["/file/name"]]``: only positional arguments) or ``[args, kwargs]``
(e.g. ``[[], {"host": "localhost", "port": 6666}]``: positional and
keyword arguments, but no positional arguments in the example).
#. Optional logging level. Overrides :ref:`log_level key
<config-common-log_level>` and has the same format.
#. Optional format string. Partially overrides :ref:`log_format key
<config-common-log_format>` and has the same format. “Partially” here
means that it may only specify more critical level.

.. _config-common-log_level:

``log_level``
String, determines logging level. Defaults to ``WARNING``.

.. _config-common-log_format:

``log_format``
String, determines format of the log messages. Defaults to
``'%(asctime)s:%(level)s:%(message)s'``.
Expand Down
178 changes: 139 additions & 39 deletions powerline/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from powerline.colorscheme import Colorscheme
from powerline.lib.config import ConfigLoader
from powerline.lib.unicode import safe_unicode, FailedUnicode
from powerline.lib.unicode import unicode, safe_unicode, FailedUnicode
from powerline.config import DEFAULT_SYSTEM_CONFIG_DIR
from powerline.lib.dict import mergedicts
from powerline.lib.encoding import get_preferred_output_encoding
Expand Down Expand Up @@ -121,7 +121,7 @@ def get_fallback_logger(stream=None):
handler.setLevel(level)
handler.setFormatter(formatter)

logger = logging.getLogger('powerline')
logger = logging.Logger('powerline')
logger.setLevel(level)
logger.addHandler(handler)
_fallback_logger = PowerlineLogger(None, logger, '_fallback_')
Expand Down Expand Up @@ -200,40 +200,102 @@ def load_config(cfg_path, find_config_files, config_loader, loader_callback=None
return ret


def _get_log_handler(common_config, stream=None):
'''Get log handler.
def _set_log_handlers(common_config, logger, get_module_attr, stream=None):
'''Set log handlers
:param dict common_config:
Configuration dictionary used to create handler.
:return: logging.Handler subclass.
:param logging.Logger logger:
Logger to which handlers will be attached.
:param func get_module_attr:
:py:func:`gen_module_attr_getter` output.
:param file stream:
Stream to use by default for :py:class:`logging.StreamHandler` in place
of :py:attr:`sys.stderr`. May be ``None``.
'''
log_file = common_config['log_file']
if log_file:
log_file = os.path.expanduser(log_file)
log_dir = os.path.dirname(log_file)
if not os.path.isdir(log_dir):
os.mkdir(log_dir)
return logging.FileHandler(log_file)
else:
return logging.StreamHandler(stream)


def create_logger(common_config, stream=None):
log_targets = common_config['log_file']
num_handlers = 0
for log_target in log_targets:
if log_target is None:
log_target = ['logging.StreamHandler', []]
elif isinstance(log_target, unicode):
log_target = os.path.expanduser(log_target)
log_dir = os.path.dirname(log_target)
if log_dir and not os.path.isdir(log_dir):
os.mkdir(log_dir)
log_target = ['logging.FileHandler', [[log_target]]]
module, handler_class_name = log_target[0].rpartition('.')[::2]
module = module or 'logging.handlers'
try:
handler_class_args = log_target[1][0]
except IndexError:
if module == 'logging' and handler_class_name == 'StreamHandler':
handler_class_args = [stream]
else:
handler_class_args = ()
try:
handler_class_kwargs = log_target[1][1]
except IndexError:
handler_class_kwargs = {}
module = str(module)
handler_class_name = str(handler_class_name)
handler_class = get_module_attr(module, handler_class_name)
if not handler_class:
continue
handler = handler_class(*handler_class_args, **handler_class_kwargs)
try:
handler_level_name = log_target[2]
except IndexError:
handler_level_name = common_config['log_level']
try:
handler_format = log_target[3]
except IndexError:
handler_format = common_config['log_format']
handler.setLevel(getattr(logging, handler_level_name))
handler.setFormatter(logging.Formatter(handler_format))
logger.addHandler(handler)
num_handlers += 1
if num_handlers == 0 and log_targets:
raise ValueError('Failed to set up any handlers')


def create_logger(common_config, use_daemon_threads=True, ext='__unknown__',
import_paths=None, imported_modules=None, stream=None):
'''Create logger according to provided configuration
'''
log_format = common_config['log_format']
formatter = logging.Formatter(log_format)
:param dict common_config:
Common configuration, from :py:func:`finish_common_config`.
:param bool use_daemon_threads:
Whether daemon threads should be used. Argument to
:py:class:`PowerlineLogger` constructor.
:param str ext:
Used extension. Argument to :py:class:`PowerlineLogger` constructor.
:param set imported_modules:
Set where imported modules are saved. Argument to
:py:func:`gen_module_attr_getter`. May be ``None``, in this case new
empty set is used.
:param file stream:
Stream to use by default for :py:class:`logging.StreamHandler` in place
of :py:attr:`sys.stderr`. May be ``None``.
:return: Three objects:
#. :py:class:`logging.Logger` instance.
#. :py:class:`PowerlineLogger` instance.
#. Function, output of :py:func:`gen_module_attr_getter`.
'''
logger = logging.Logger('powerline')
level = getattr(logging, common_config['log_level'])
handler = _get_log_handler(common_config, stream)
handler.setLevel(level)
handler.setFormatter(formatter)

logger = logging.getLogger('powerline')
logger.setLevel(level)
logger.addHandler(handler)
return logger

pl = PowerlineLogger(use_daemon_threads, logger, ext)
get_module_attr = gen_module_attr_getter(
pl, common_config['paths'],
set() if imported_modules is None else imported_modules)

_set_log_handlers(common_config, logger, get_module_attr, stream)

return logger, pl, get_module_attr


def finish_common_config(encoding, common_config):
Expand Down Expand Up @@ -264,7 +326,10 @@ def finish_common_config(encoding, common_config):
common_config.setdefault('additional_escapes', None)
common_config.setdefault('reload_config', True)
common_config.setdefault('interval', None)
common_config.setdefault('log_file', None)
common_config.setdefault('log_file', [None])

if not isinstance(common_config['log_file'], list):
common_config['log_file'] = [common_config['log_file']]

common_config['paths'] = [
os.path.expanduser(path) for path in common_config['paths']
Expand Down Expand Up @@ -324,6 +389,26 @@ def get_module_attr(module, attr, prefix='powerline'):
return get_module_attr


LOG_KEYS = set(('log_format', 'log_level', 'log_file', 'paths'))
'''List of keys related to logging
'''


def _get_log_keys(common_config):
'''Return a common configuration copy with only log-related config left
:param dict common_config:
Common configuration.
:return:
:py:class:`dict` instance which has only keys from
:py:attr:`powerline.LOG_KEYS` left.
'''
return dict((
(k, v) for k, v in common_config.items() if k in LOG_KEYS
))


class Powerline(object):
'''Main powerline class, entrance point for all powerline uses. Sets
powerline up and loads the configuration.
Expand Down Expand Up @@ -380,6 +465,7 @@ def init(self,
self.ext = ext
self.run_once = run_once
self.logger = logger
self.had_logger = bool(self.logger)
self.use_daemon_threads = use_daemon_threads

if not renderer_module:
Expand Down Expand Up @@ -430,8 +516,20 @@ def create_logger(self):
This function is used to create logger unless it was already specified
at initialization.
:return: Three objects:
#. :py:class:`logging.Logger` instance.
#. :py:class:`PowerlineLogger` instance.
#. Function, output of :py:func:`gen_module_attr_getter`.
'''
return create_logger(self.common_config, self.default_log_stream)
return create_logger(
common_config=self.common_config,
use_daemon_threads=self.use_daemon_threads,
ext=self.ext,
imported_modules=self.imported_modules,
stream=self.default_log_stream,
)

def create_renderer(self, load_main=False, load_colors=False, load_colorscheme=False, load_theme=False):
'''(Re)create renderer object. Can be used after Powerline object was
Expand Down Expand Up @@ -465,22 +563,24 @@ def create_renderer(self, load_main=False, load_colors=False, load_colorscheme=F
or not self.prev_common_config
or self.prev_common_config['default_top_theme'] != self.common_config['default_top_theme'])

self.prev_common_config = self.common_config

self.import_paths = self.common_config['paths']
log_keys_differ = (not self.prev_common_config or (
_get_log_keys(self.prev_common_config) != _get_log_keys(self.common_config)
))

if not self.logger:
self.logger = self.create_logger()
self.prev_common_config = self.common_config

if not self.pl:
self.pl = PowerlineLogger(self.use_daemon_threads, self.logger, self.ext)
if log_keys_differ:
if self.had_logger:
self.pl = PowerlineLogger(self.use_daemon_threads, self.logger, self.ext)
self.get_module_attr = gen_module_attr_getter(
self.pl, self.common_config['paths'], self.imported_modules)
else:
self.logger, self.pl, self.get_module_attr = self.create_logger()
self.config_loader.pl = self.pl

if not self.run_once:
self.config_loader.set_watcher(self.common_config['watcher'])

self.get_module_attr = gen_module_attr_getter(self.pl, self.import_paths, self.imported_modules)

mergedicts(self.renderer_options, dict(
pl=self.pl,
term_truecolor=self.common_config['term_truecolor'],
Expand Down
6 changes: 3 additions & 3 deletions powerline/bindings/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from powerline.config import POWERLINE_ROOT, TMUX_CONFIG_DIRECTORY
from powerline.lib.config import ConfigLoader
from powerline import generate_config_finder, load_config, create_logger, PowerlineLogger, finish_common_config
from powerline import generate_config_finder, load_config, create_logger, finish_common_config
from powerline.shell import ShellPowerline
from powerline.lib.shell import which
from powerline.bindings.tmux import (TmuxVersionInfo, run_tmux_command, set_tmux_environment, get_tmux_version,
Expand Down Expand Up @@ -221,8 +221,8 @@ def get_main_config(args):
def create_powerline_logger(args):
config = get_main_config(args)
common_config = finish_common_config(get_preferred_output_encoding(), config['common'])
logger = create_logger(common_config)
return PowerlineLogger(use_daemon_threads=True, logger=logger, ext='config')
logger, pl, get_module_attr = create_logger(common_config)
return pl


def check_command(cmd):
Expand Down
Loading

0 comments on commit eaa772f

Please sign in to comment.