forked from LBRYCommunity/lbry-sdk
155 lines
4.9 KiB
Python
155 lines
4.9 KiB
Python
import json
|
|
import logging
|
|
import logging.handlers
|
|
import sys
|
|
import traceback
|
|
|
|
from requests_futures.sessions import FuturesSession
|
|
|
|
import lbrynet
|
|
from lbrynet.conf import settings
|
|
from lbrynet.core import utils
|
|
|
|
session = FuturesSession()
|
|
|
|
|
|
def bg_cb(sess, resp):
|
|
""" Don't do anything with the response """
|
|
pass
|
|
|
|
|
|
class HTTPSHandler(logging.Handler):
|
|
def __init__(self, url, fqdn=False, localname=None, facility=None):
|
|
logging.Handler.__init__(self)
|
|
self.url = url
|
|
self.fqdn = fqdn
|
|
self.localname = localname
|
|
self.facility = facility
|
|
|
|
def get_full_message(self, record):
|
|
if record.exc_info:
|
|
return '\n'.join(traceback.format_exception(*record.exc_info))
|
|
else:
|
|
return record.getMessage()
|
|
|
|
def emit(self, record):
|
|
try:
|
|
payload = self.format(record)
|
|
session.post(self.url, data=payload, background_callback=bg_cb)
|
|
except (KeyboardInterrupt, SystemExit):
|
|
raise
|
|
except:
|
|
self.handleError(record)
|
|
|
|
|
|
DEFAULT_FORMAT = "%(asctime)s %(levelname)-8s %(name)s:%(lineno)d: %(message)s"
|
|
DEFAULT_FORMATTER = logging.Formatter(DEFAULT_FORMAT)
|
|
LOGGLY_URL = "https://logs-01.loggly.com/inputs/{token}/tag/{tag}"
|
|
|
|
|
|
def remove_handlers(log, handler_name):
|
|
for handler in log.handlers:
|
|
if handler.name == handler_name:
|
|
log.removeHandler(handler)
|
|
|
|
|
|
def _log_decorator(fn):
|
|
def helper(*args, **kwargs):
|
|
log = kwargs.pop('log', logging.getLogger())
|
|
level = kwargs.pop('level', logging.INFO)
|
|
if not isinstance(level, int):
|
|
# despite the name, getLevelName returns
|
|
# the numeric level when passed a text level
|
|
level = logging.getLevelName(level)
|
|
handler = fn(*args, **kwargs)
|
|
if handler.name:
|
|
remove_handlers(log, handler.name)
|
|
handler.setLevel(level)
|
|
log.addHandler(handler)
|
|
if log.level > level:
|
|
log.setLevel(level)
|
|
return helper
|
|
|
|
|
|
def disable_third_party_loggers():
|
|
logging.getLogger('requests').setLevel(logging.WARNING)
|
|
logging.getLogger('urllib3').setLevel(logging.WARNING)
|
|
logging.getLogger('BitcoinRPC').setLevel(logging.INFO)
|
|
|
|
def disable_noisy_loggers():
|
|
logging.getLogger('lbrynet.analytics.api').setLevel(logging.INFO)
|
|
logging.getLogger('lbrynet.core').setLevel(logging.INFO)
|
|
logging.getLogger('lbrynet.dht').setLevel(logging.INFO)
|
|
logging.getLogger('lbrynet.lbrynet_daemon').setLevel(logging.INFO)
|
|
logging.getLogger('lbrynet.core.Wallet').setLevel(logging.INFO)
|
|
logging.getLogger('lbrynet.lbryfile').setLevel(logging.INFO)
|
|
logging.getLogger('lbrynet.lbryfilemanager').setLevel(logging.INFO)
|
|
|
|
|
|
@_log_decorator
|
|
def configure_console(**kwargs):
|
|
"""Convenience function to configure a logger that outputs to stdout"""
|
|
handler = logging.StreamHandler(sys.stdout)
|
|
handler.setFormatter(DEFAULT_FORMATTER)
|
|
handler.name = 'console'
|
|
return handler
|
|
|
|
|
|
@_log_decorator
|
|
def configure_file_handler(file_name, **kwargs):
|
|
handler = logging.handlers.RotatingFileHandler(file_name, maxBytes=2097152, backupCount=5)
|
|
handler.setFormatter(DEFAULT_FORMATTER)
|
|
handler.name = 'file'
|
|
return handler
|
|
|
|
|
|
def get_loggly_url(token=None, version=None):
|
|
token = token or utils.deobfuscate(settings.LOGGLY_TOKEN)
|
|
version = version or lbrynet.__version__
|
|
return LOGGLY_URL.format(token=token, tag='lbrynet-' + version)
|
|
|
|
|
|
@_log_decorator
|
|
def configure_loggly_handler(url=None, **kwargs):
|
|
url = url or get_loggly_url()
|
|
formatter = JsonFormatter(**kwargs)
|
|
handler = HTTPSHandler(url)
|
|
handler.setFormatter(formatter)
|
|
handler.name = 'loggly'
|
|
return handler
|
|
|
|
|
|
class JsonFormatter(logging.Formatter):
|
|
"""Format log records using json serialization"""
|
|
def __init__(self, **kwargs):
|
|
self.attributes = kwargs
|
|
|
|
def format(self, record):
|
|
data = {
|
|
'loggerName': record.name,
|
|
'asciTime': self.formatTime(record),
|
|
'fileName': record.filename,
|
|
'functionName': record.funcName,
|
|
'levelNo': record.levelno,
|
|
'lineNo': record.lineno,
|
|
'levelName': record.levelname,
|
|
'message': record.getMessage(),
|
|
}
|
|
data.update(self.attributes)
|
|
if record.exc_info:
|
|
data['exc_info'] = self.formatException(record.exc_info)
|
|
return json.dumps(data)
|
|
|
|
|
|
def failure(failure, log, msg, *args):
|
|
"""Log a failure message from a deferred.
|
|
|
|
Args:
|
|
failure: twisted.python.failure.Failure
|
|
log: a python logger instance
|
|
msg: the message to log. Can use normal logging string interpolation.
|
|
the last argument will be set to the error message from the failure.
|
|
args: values to substitute into `msg`
|
|
"""
|
|
args += (failure.getErrorMessage(),)
|
|
log.error(msg, *args, exc_info=failure.getTracebackObject())
|