# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright 2016 Anaconda, Inc. # # May be copied and distributed freely only as part of an Anaconda or # Miniconda installation. # ----------------------------------------------------------------------------- """Logger utilties.""" # yapf: disable # Standard library imports import json import logging.handlers import os # Local imports from navigator_updater.config import (LOG_FILENAME, LOG_FOLDER, MAX_LOG_FILE_SIZE) # yapf: disable # Constants JSON_SEPARATOR = '\x00' # Main logger instance logger = logging.getLogger('navigator_updater') class ContextFilter(logging.Filter): """This is a filter that replaces `\` by `/` for json parsing.""" @staticmethod def filter(record): """On windows make sure that slashes do not interfere with json.""" # print([i for i in dir(record) if not i.startswith('_')]) if isinstance(record.msg, dict): for k, v in record.msg.items(): if isinstance(v, str): record.msg[k] = v.replace('\\', '/') elif record.msg and isinstance(record.msg, str): record.msg = record.msg.replace('\\', '/') record.msg = repr(record.msg).replace('"', "'") if record.msg[0] == "'": record.msg = record.msg[1:] if record.msg[-1] == "'": record.msg = record.msg[:-1] if record.pathname: record.pathname = record.pathname.replace('\\', '/') return True def setup_logger( log_level=logging.WARNING, log_folder=LOG_FOLDER, log_filename=LOG_FILENAME, log_file_size=MAX_LOG_FILE_SIZE, log_backup_count=5 ): """Setup, create, and set logger.""" global logger if not os.path.isdir(log_folder): os.makedirs(log_folder) log_file_path = os.path.join(log_folder, log_filename) # Reset extra loggers root_logger = logging.getLogger() root_logger.handlers = [] # Setup logger for navigator application and reset handlers logger.setLevel(logging.DEBUG) logger.handlers = [] # Create file handler file_handler = logging.handlers.RotatingFileHandler( log_file_path, maxBytes=log_file_size, backupCount=log_backup_count, ) file_handler.setLevel(log_level) # Create console handler console_handler = logging.StreamHandler() console_handler.setLevel(log_level) # Create formatters file_formatter = logging.Formatter( '{' '"time": "%(asctime)s", ' '"level": "%(levelname)s", ' '"module": "%(module)s", ' '"method": "%(funcName)s", ' '"line": %(lineno)d, ' '"path": "%(pathname)s", ' '"message": "%(message)s"' '}' + JSON_SEPARATOR, ) console_formatter = logging.Formatter( '%(asctime)s - %(levelname)s %(module)s.%(funcName)s:%(lineno)d\n' '%(message)s\n', ) # Set formatters file_handler.setFormatter(file_formatter) console_handler.setFormatter(console_formatter) # Add filters filt = ContextFilter() file_handler.addFilter(filt) console_handler.addFilter(filt) # Add handlers logger.addHandler(file_handler) logger.addHandler(console_handler) logger.info('Setting up logger') def log_files(log_folder=LOG_FOLDER, log_filename=LOG_FILENAME): """ Return all available log files located inside the logs folder. Files starting with a `.` are ignored as well as files not including the `log_filename` as part of the name. """ paths = [] if os.path.isdir(log_folder): log_files = os.listdir(log_folder) for log_file in sorted(log_files): log_file_path = os.path.join(log_folder, log_file) if (os.path.isfile(log_file_path) and log_filename in log_file and not log_file.startswith('.')): paths.append(log_file_path) return paths def clean_logs(log_folder=LOG_FOLDER): """Remove logs in old plain text format.""" for path in log_files(log_folder): if os.path.isfile(path): with open(path, 'r') as f: data = f.read() new_lines = [] lines = data.split('\n') for line in lines: if JSON_SEPARATOR in line: new_lines.append(line) new_separator = JSON_SEPARATOR + '\n' with open(path, 'w') as f: f.write(new_separator.join(new_lines)) def load_log(log_file_path): """Load log file and return list of items.""" if os.path.isfile(log_file_path): with open(log_file_path, 'r') as f: data = f.read() json_lines = data.split(JSON_SEPARATOR) # Remove empty lines json_lines = [line for line in json_lines if line and line != '\n'] data = [] for i, line in enumerate(json_lines): try: data.append(json.loads(line)) except Exception as e: logger.warning('Line {0}. Exception - {1}'.format(i, str(e))) return data