Some checks failed
Detach Plugins / check (FlyGrep.vim) (push) Has been cancelled
Detach Plugins / check (GitHub.vim) (push) Has been cancelled
Detach Plugins / check (JavaUnit.vim) (push) Has been cancelled
Detach Plugins / check (SourceCounter.vim) (push) Has been cancelled
Detach Plugins / check (cpicker.nvim) (push) Has been cancelled
Detach Plugins / check (dein-ui.vim) (push) Has been cancelled
Detach Plugins / check (git.vim) (push) Has been cancelled
Detach Plugins / check (iedit.vim) (push) Has been cancelled
Detach Plugins / check (scrollbar.vim) (push) Has been cancelled
Detach Plugins / check (vim-chat) (push) Has been cancelled
Detach Plugins / check (vim-cheat) (push) Has been cancelled
Detach Plugins / check (vim-todo) (push) Has been cancelled
Detach Plugins / check (xmake.vim) (push) Has been cancelled
test / Linux (nvim, nightly) (push) Has been cancelled
test / Linux (nvim, v0.3.8) (push) Has been cancelled
test / Linux (nvim, v0.4.0) (push) Has been cancelled
test / Linux (nvim, v0.4.2) (push) Has been cancelled
test / Linux (nvim, v0.4.3) (push) Has been cancelled
test / Linux (nvim, v0.4.4) (push) Has been cancelled
test / Linux (nvim, v0.5.0) (push) Has been cancelled
test / Linux (nvim, v0.5.1) (push) Has been cancelled
test / Linux (nvim, v0.6.0) (push) Has been cancelled
test / Linux (nvim, v0.6.1) (push) Has been cancelled
test / Linux (nvim, v0.7.0) (push) Has been cancelled
test / Linux (nvim, v0.7.2) (push) Has been cancelled
test / Linux (nvim, v0.8.0) (push) Has been cancelled
test / Linux (nvim, v0.8.1) (push) Has been cancelled
test / Linux (nvim, v0.8.2) (push) Has been cancelled
test / Linux (nvim, v0.8.3) (push) Has been cancelled
test / Linux (nvim, v0.9.0) (push) Has been cancelled
test / Linux (nvim, v0.9.1) (push) Has been cancelled
test / Linux (true, vim, v7.4.052) (push) Has been cancelled
test / Linux (true, vim, v7.4.1689) (push) Has been cancelled
test / Linux (true, vim, v7.4.629) (push) Has been cancelled
test / Linux (true, vim, v8.0.0027) (push) Has been cancelled
test / Linux (true, vim, v8.0.0183) (push) Has been cancelled
test / Linux (vim, nightly) (push) Has been cancelled
test / Linux (vim, v8.0.0184) (push) Has been cancelled
test / Linux (vim, v8.0.1453) (push) Has been cancelled
test / Linux (vim, v8.1.2269) (push) Has been cancelled
test / Linux (vim, v8.2.2434) (push) Has been cancelled
test / Linux (vim, v8.2.3995) (push) Has been cancelled
test / Windows (nvim, nightly) (push) Has been cancelled
test / Windows (nvim, v0.3.8) (push) Has been cancelled
test / Windows (nvim, v0.4.2) (push) Has been cancelled
test / Windows (nvim, v0.4.3) (push) Has been cancelled
test / Windows (nvim, v0.4.4) (push) Has been cancelled
test / Windows (nvim, v0.5.0) (push) Has been cancelled
test / Windows (nvim, v0.5.1) (push) Has been cancelled
test / Windows (nvim, v0.6.0) (push) Has been cancelled
test / Windows (nvim, v0.6.1) (push) Has been cancelled
test / Windows (nvim, v0.7.0) (push) Has been cancelled
test / Windows (nvim, v0.7.2) (push) Has been cancelled
test / Windows (nvim, v0.8.0) (push) Has been cancelled
test / Windows (nvim, v0.8.1) (push) Has been cancelled
test / Windows (nvim, v0.8.2) (push) Has been cancelled
test / Windows (nvim, v0.8.3) (push) Has been cancelled
test / Windows (nvim, v0.9.0) (push) Has been cancelled
test / Windows (nvim, v0.9.1) (push) Has been cancelled
test / Windows (vim, nightly) (push) Has been cancelled
test / Windows (vim, v7.4.1185) (push) Has been cancelled
test / Windows (vim, v7.4.1689) (push) Has been cancelled
test / Windows (vim, v8.0.0027) (push) Has been cancelled
test / Windows (vim, v8.0.1453) (push) Has been cancelled
test / Windows (vim, v8.1.2269) (push) Has been cancelled
test / Windows (vim, v8.2.2434) (push) Has been cancelled
test / Windows (vim, v8.2.3995) (push) Has been cancelled
docker / docker (push) Has been cancelled
mirror / check (coding) (push) Has been cancelled
mirror / check (gitee) (push) Has been cancelled
mirror / check (gitlab) (push) Has been cancelled
158 lines
5.3 KiB
Python
158 lines
5.3 KiB
Python
# ============================================================================
|
|
# FILE: logger.py
|
|
# AUTHOR: Tommy Allen <tommy at esdf.io>
|
|
# License: MIT license
|
|
# ============================================================================
|
|
import sys
|
|
import time
|
|
import logging
|
|
import typing
|
|
|
|
from collections import defaultdict
|
|
from functools import wraps
|
|
from pynvim import Nvim
|
|
|
|
log_format = '%(asctime)s %(levelname)-8s [%(process)d] (%(name)s) %(message)s'
|
|
log_message_cooldown = 0.5
|
|
|
|
root = logging.getLogger('deoplete')
|
|
root.propagate = False
|
|
init = False
|
|
|
|
FUNC = typing.Callable[..., typing.Any]
|
|
|
|
|
|
def getLogger(name: str) -> logging.Logger:
|
|
"""Get a logger that is a child of the 'root' logger.
|
|
"""
|
|
return root.getChild(name)
|
|
|
|
|
|
def setup(vim: Nvim, level: str, output_file: str = '') -> None:
|
|
"""Setup logging for Deoplete
|
|
"""
|
|
global init
|
|
if init:
|
|
return
|
|
init = True
|
|
|
|
if output_file:
|
|
formatter = logging.Formatter(log_format)
|
|
handler = logging.FileHandler(filename=output_file)
|
|
handler.setFormatter(formatter)
|
|
handler.addFilter(DeopleteLogFilter(vim))
|
|
root.addHandler(handler)
|
|
|
|
level = str(level).upper()
|
|
if level not in ('DEBUG', 'INFO', 'WARN', 'WARNING', 'ERROR',
|
|
'CRITICAL', 'FATAL'):
|
|
level = 'DEBUG'
|
|
root.setLevel(getattr(logging, level))
|
|
|
|
log = getLogger('logging')
|
|
log.info('--- Deoplete Log Start ---')
|
|
log.info('%s, Python %s',
|
|
vim.call('deoplete#util#neovim_version'),
|
|
'.'.join(map(str, sys.version_info[:3])))
|
|
|
|
if 'deoplete#_logging_notified' not in vim.vars:
|
|
vim.vars['deoplete#_logging_notified'] = 1
|
|
vim.call('deoplete#util#print_debug', 'Logging to %s' % (
|
|
output_file))
|
|
|
|
|
|
def logmethod(func: FUNC) -> typing.Callable[[FUNC], FUNC]:
|
|
"""Decorator for setting up the logger in LoggingMixin subclasses.
|
|
|
|
This does not guarantee that log messages will be generated. If
|
|
`LoggingMixin.is_debug_enabled` is True, it will be propagated up to the
|
|
root 'deoplete' logger.
|
|
"""
|
|
@wraps(func)
|
|
def wrapper(self, # type: ignore
|
|
*args: typing.Any,
|
|
**kwargs: typing.Any) -> typing.Any:
|
|
if not init or not self.is_debug_enabled:
|
|
return
|
|
if self._logger is None:
|
|
self._logger = getLogger(getattr(self, 'name', 'unknown'))
|
|
return func(self, *args, **kwargs)
|
|
return wrapper
|
|
|
|
|
|
class LoggingMixin(object):
|
|
"""Class that adds logging functions to a subclass.
|
|
"""
|
|
is_debug_enabled = False
|
|
_logger = None # type: logging.Logger
|
|
|
|
@logmethod
|
|
def debug(self, msg: str,
|
|
*args: typing.Any, **kwargs: typing.Any) -> None:
|
|
self._logger.debug(msg, *args, **kwargs)
|
|
|
|
@logmethod
|
|
def info(self, msg: str,
|
|
*args: typing.Any, **kwargs: typing.Any) -> None:
|
|
self._logger.info(msg, *args, **kwargs)
|
|
|
|
@logmethod
|
|
def warning(self, msg: str,
|
|
*args: typing.Any, **kwargs: typing.Any) -> None:
|
|
self._logger.warning(msg, *args, **kwargs)
|
|
warn = warning
|
|
|
|
@logmethod
|
|
def error(self, msg: str,
|
|
*args: typing.Any, **kwargs: typing.Any) -> None:
|
|
self._logger.error(msg, *args, **kwargs)
|
|
|
|
@logmethod
|
|
def exception(self, msg: str,
|
|
*args: typing.Any, **kwargs: typing.Any) -> None:
|
|
# This will not produce a log message if there is no exception to log.
|
|
self._logger.exception(msg, *args, **kwargs)
|
|
|
|
@logmethod
|
|
def critical(self, msg: str,
|
|
*args: typing.Any, **kwargs: typing.Any) -> None:
|
|
self._logger.critical(msg, *args, **kwargs)
|
|
fatal = critical
|
|
|
|
|
|
class DeopleteLogFilter(logging.Filter):
|
|
def __init__(self, vim: Nvim, name: str = ''):
|
|
self.vim = vim
|
|
self.counter: typing.Dict[str, int] = defaultdict(int)
|
|
self.last_message_time: float = 0
|
|
self.last_message: typing.Tuple[typing.Any, ...] = ()
|
|
|
|
def filter(self, record: logging.LogRecord) -> bool:
|
|
t = time.time()
|
|
elapsed = t - self.last_message_time
|
|
self.last_message_time = t
|
|
|
|
message = (record.levelno, record.name, record.msg, record.args)
|
|
if message == self.last_message and elapsed < log_message_cooldown:
|
|
# Ignore if the same message comes in too fast.
|
|
return False
|
|
self.last_message = message
|
|
|
|
if record.levelno >= logging.ERROR:
|
|
self.counter[record.name] += 1
|
|
if self.counter[record.name] <= 2:
|
|
# Only permit 2 errors in succession from a logging source to
|
|
# display errors inside of Neovim. After this, it is no longer
|
|
# permitted to emit any more errors and should be addressed.
|
|
self.vim.call('deoplete#util#print_error', record.getMessage(),
|
|
record.name)
|
|
if record.exc_info and record.stack_info:
|
|
# Add a penalty for messages that generate exceptions to avoid
|
|
# making the log harder to read with doubled stack traces.
|
|
self.counter[record.name] += 1
|
|
elif self.counter[record.name] < 2:
|
|
# If below the threshold for silencing a logging source, reset its
|
|
# counter.
|
|
self.counter[record.name] = 0
|
|
return True
|