"""Unified connector to loguru-based logger with various configurations.""" import os import sys import logging # For PropagateHandler() below from datetime import datetime from loguru import logger from integration_scripts.config import MASTER_LOG, JSON_LOG, CSV_LOG, \ OTHER_LOG, DEBUG_LOG, LOG_DIR, LOGGER_NAME, LOGGER_DSN # Propagate handler for ows-logger # see: https://loguru.readthedocs.io/en/latest/overview.html#entirely-compatible-with-standard-logging # noqa class PropagateHandler(logging.Handler): """Wrapper to pass log messages to standard logger.""" def emit(self, record): logging.getLogger(LOGGER_NAME).handle(record) default_format = "{time:YYYY-MM-DD at h:mm:ss A zz} | {" \ "level} | {message}" if MASTER_LOG and '{timestamp}' in MASTER_LOG: MASTER_LOG = MASTER_LOG.format(timestamp=datetime.today().strftime( '%Y-%m-%d_%H_%M_%S')) if CSV_LOG and '{timestamp}' in CSV_LOG: CSV_LOG = CSV_LOG.format(timestamp=datetime.today().strftime( '%Y-%m-%d_%H_%M_%S')) if JSON_LOG and '{timestamp}' in JSON_LOG: JSON_LOG = JSON_LOG.format(timestamp=datetime.today().strftime( '%Y-%m-%d_%H_%M_%S')) if OTHER_LOG and '{timestamp}' in OTHER_LOG: OTHER_LOG = OTHER_LOG.format(timestamp=datetime.today().strftime( '%Y-%m-%d_%H_%M_%S')) def default_filter(record): return all( v not in record["extra"] for v in [ 'json_only', 'file_only', 'csv_only', 'other_only', 'multi' ]) and record["level"].no >= logger.level("INFO").no \ and record["level"].no != logger.level("ERROR").no def default_error_filter(record): return all( v not in record["extra"] for v in [ 'json_only', 'file_only', 'csv_only', 'other_only', 'multi' ]) and record["level"].no >= logger.level("ERROR").no def default_debug_filter(record): return all( v not in record["extra"] for v in [ 'json_only', 'file_only', 'csv_only', 'other_only', 'multi' ]) and record["level"].no == logger.level("DEBUG").no def default_multi_filter(record): return all( v not in record["extra"] for v in [ 'json_only', 'file_only', 'csv_only', 'other_only' ]) and record["level"].no >= logger.level("INFO").no \ and record["level"].no != logger.level("ERROR").no \ and 'multi' in record['extra'] def default_multi_error_filter(record): return all( v not in record["extra"] for v in [ 'json_only', 'file_only', 'csv_only', 'other_only' ]) and record["level"].no >= logger.level("ERROR").no \ and 'multi' in record['extra'] def default_multi_debug_filter(record): return all( v not in record["extra"] for v in [ 'json_only', 'file_only', 'csv_only', 'other_only' ]) and record["level"].no == logger.level("DEBUG").no \ and 'multi' in record['extra'] # Ensure handlers are only added once. try: # Remove and replace default logger logger.remove(0) except ValueError: pass else: # Default Main Logger logger.add(sys.stderr, format=default_format, filter=default_filter, colorize=True, level='INFO') # Default Error Logger logger.add(sys.stderr, filter=default_error_filter, colorize=True, level='ERROR') # Default Main Enqueued Logger logger.add(sys.stderr, format=default_format, filter=default_multi_filter, colorize=True, enqueue=True, level='INFO') # Default Error Enqueued Logger logger.add(sys.stderr, filter=default_multi_error_filter, colorize=True, enqueue=True, level='ERROR') # Debug level loggers if DEBUG_LOG: logger.add(sys.stderr, filter=default_debug_filter, level='DEBUG') logger.add(sys.stderr, filter=default_multi_debug_filter, enqueue=True, level='DEBUG') # PropogateHandler for ows-logger if LOGGER_DSN: logger.add(PropagateHandler(), format="{message}") # FILE LOGS --------------------------------------------------------------- if MASTER_LOG or CSV_LOG or JSON_LOG or OTHER_LOG: os.makedirs(LOG_DIR, exist_ok=True) # -- Text Log ------------------------------------------------------------- if MASTER_LOG: logger.info('Writing console log to {}', MASTER_LOG) logger.add(os.path.join(LOG_DIR, MASTER_LOG), rotation="5 MB", retention='30 days', filter=lambda record: any( v in record["extra"] for v in ['file', 'file_only']) and 'multi' not in record['extra']) # Enqueued logger.add(os.path.join(LOG_DIR, MASTER_LOG), rotation="5 MB", retention='30 days', filter=lambda record: any( v in record["extra"] for v in ['file', 'file_only']) and 'multi' in record['extra'], enqueue=True) # -- JSON Log ------------------------------------------------------------- if JSON_LOG: logger.add(os.path.join(LOG_DIR, JSON_LOG), serialize=True, rotation="5 MB", retention='30 days', filter=lambda record: any( v in record["extra"] for v in ['json', 'json_only']) and 'multi' not in record['extra']) # Enqueued logger.add(os.path.join(LOG_DIR, JSON_LOG), serialize=True, rotation="5 MB", retention='30 days', filter=lambda record: any( v in record["extra"] for v in ['json', 'json_only']) and 'multi' in record['extra'], enqueue=True) # -- CSV Log -------------------------------------------------------------- if CSV_LOG: logger.add(os.path.join(LOG_DIR, CSV_LOG), format='{message}', rotation="5 MB", retention='30 days', filter=lambda record: any( v in record["extra"] for v in ['csv', 'csv_only']) and 'multi' not in record['extra']) # Enqueued logger.add(os.path.join(LOG_DIR, CSV_LOG), format='{message}', rotation="5 MB", retention='30 days', filter=lambda record: any( v in record["extra"] for v in ['csv', 'csv_only']) and 'multi' in record['extra'], enqueue=True) # Secondary Text Log if OTHER_LOG: logger.add(os.path.join(LOG_DIR, OTHER_LOG), format='{message}', rotation="5 MB", retention='30 days', filter=lambda record: any( v in record["extra"] for v in ['other', 'other_only']) and 'multi' not in record['extra']) # Enqueued logger.add(os.path.join(LOG_DIR, OTHER_LOG), format='{message}', rotation="5 MB", retention='30 days', filter=lambda record: any( v in record["extra"] for v in ['other', 'other_only']) and 'multi' in record['extra'], enqueue=True) # # CSV_LOG EXAMPLE # logger.bind(csv=True).debug('csv_field_1, csv_field_2, etc.') # logger.bind(csv=True).debug('val_1_1, val_1_2, etc.') # logger.bind(csv=True).debug('val_2_1, val_2_2, etc.') # # OTHER_LOG EXAMPLE # logger.debug('Message in only MASTER_LOG') # logger.bind(other=True).debug('Message in both MASTER_LOG & OTHER_LOG') # Multiprocessing / Threading # logger.bind(multi=True).debug('Enqueued message for concurrency.' # welcome message logger.debug("Loguru logging enabled.")