"""Application Logger. ====================== Creates application logger instance, that will send the logs to Loggly. If config.LOGGER_DSN value is not set, the logs will be sent to stdout. """ from datetime import datetime import json import logging import traceback import uuid from owslogger import logger as owslogger from switchboard_consumer import config def get_ows_logger(correlation_id=None): """Get logger with a correlation_id attached. Args: correlation_id: correlation_id that will be sent along with log record Returns: logger.OwsLoggingAdapter: instance of OwsLoggingAdapter with correlation_id attached. """ app_logger = owslogger.setup( config.ENVIRONMENT, config.ENVIRONMENT, config.LOGGER_LEVEL, config.APP_NAME, config.APP_VERSION, dsn=config.LOGGER_DSN) ows_adapter = owslogger.OwsLoggingAdapter(app_logger, { 'correlation_id': correlation_id or uuid.uuid4() }) return ows_adapter def get_default_context(correlation_id): """Return default logging context.""" return { 'tag': 'switchboard', 'service': 'daemon-switchboard-consumer', 'service_version': None, 'process_type': 'kafka consumer', 'process_id': None, 'environment': config.ENVIRONMENT, 'client_host': None, 'correlation_id': correlation_id or str(uuid.uuid4()), 'user_id': None, 'custom_fields': {} } class ELKCentralAdapter(logging.LoggerAdapter): """Adapter for ELK json logs.""" def __init__(self, logger, extra, ows_adapter): """Override default init method.""" self.logger = logger self.extra = extra.copy() self.ows_adapter = ows_adapter def log(self, level, msg, *args, **kwargs): """Override default adapter method.""" # extracting extra args extra_copy = self.extra.copy() for kwarg in list(kwargs.keys()): if kwarg in self.extra.keys(): extra_copy[kwarg] = kwargs[kwarg] del kwargs[kwarg] if self.isEnabledFor(level): msg, kwargs = self.process(msg, kwargs) if isinstance(msg, Exception): msg = '{}:{}'.format(repr(msg), traceback.format_exc()) message = { 'message': str(msg), 'level': logging.getLevelName(level), 'timestamp': datetime.now().strftime( '%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z' } message.update(extra_copy) msg_json = json.dumps(dict(message), default=lambda x: None) self.logger.log(level, msg_json, *args, **kwargs) self.ows_adapter.log(level, msg, *args, **kwargs) def get_elk_cental_logger(): """Return logger compatible with ELK Switchboard.""" elk_logger = logging.getLogger('elk-central') elk_logger.addHandler(logging.StreamHandler()) elk_logger.setLevel(logging.INFO) return elk_logger # On import get desired loggers and setup handlers elk_logger = get_elk_cental_logger() ows_logger = get_ows_logger() def get_current_logger(correlation_id=None): """Return logger.""" return ELKCentralAdapter(elk_logger, get_default_context(correlation_id), ows_logger)