import datetime
import logging
import os
import re
import time


class ScheduledDeletionDayHandler(logging.FileHandler):
    """
    删除过多log文件handler
    """
    def __init__(self, filepath, suffix, ext_match, mode='a', back_count=10, encoding=None, delay=False):
        """
        :param filepath: 文件路径
        :param suffix:时间格式
        :param ext_match:文件名匹配正则表达式
        :param mode:模式
        :param back_count:备份数量
        :param encoding:
        :param delay:
        """
        logging.FileHandler.__init__(self, filepath, mode, encoding=encoding, delay=delay)
        self.suffix = suffix
        self.back_count = back_count
        self.ext_match = re.compile(ext_match)
        self.suffix_time = ""

    def emit(self, record):
        """
        复写FileHandler的emit方法
        :param record:
        """
        try:
            if self.check_base_filename():
                self.build_base_filename()
            logging.FileHandler.emit(self, record)
        except (KeyboardInterrupt, SystemExit):
            raise
        except BaseException:
            self.handleError(record)

    def find_remove_files(self):
        """
        找出超出备份数量待删除的日志路径
        :return:
        """
        dir_path = os.path.dirname(self.baseFilename)
        file_names = os.listdir(dir_path)
        file_list = list()
        for file_name in file_names:
            if self.ext_match.search(file_name):
                file_list.append((os.path.join(dir_path, file_name)))
        file_list.sort()
        files_count = len(file_list)
        if files_count > self.back_count:
            return file_list[:files_count - self.back_count]
        return file_list

    @classmethod
    def remove_file(cls, remove_files):
        """
        删除日志
        :param remove_files:
        """
        for file in remove_files:
            try:
                os.remove(file)
            except PermissionError:
                pass

    def check_base_filename(self):
        """
        检查日志文件输入流是否指向当前时间段的日志文件,是否需要修改文件输入流
        """
        time_tuple = time.localtime()
        if self.suffix_time != time.strftime(self.suffix, time_tuple) or not os.path.exists(self.baseFilename):
            return True
        return False

    def build_base_filename(self):
        """
        修改文件输入流,指向当前时间段的日志文件,判断并删除超出备份数量的日志文件
        """
        # 如果有打开的文件输入流,先关闭文件输入流
        if self.stream:
            self.stream.close()
            self.stream = None
        # 检查log文件数量是否有超过备份数量
        remove_files = self.find_remove_files()
        if remove_files:
            self.remove_file(remove_files)
        # 根据suffix,获取当前时间戳,修改baseFilename,baseFilename为当前指向的文件输入流的文件路径
        current_time_tuple = time.localtime()
        self.suffix_time = time.strftime(self.suffix, current_time_tuple)
        prefix = re.split(self.ext_match, self.baseFilename)[0]
        self.baseFilename = "{0}{1}.log".format(prefix, self.suffix_time)

        self.mode = "a"


class Logger:
    level_relation = {
        "debug": logging.DEBUG,
        "info": logging.INFO,
        "warning": logging.WARNING,
        "error": logging.ERROR,
        "critical": logging.CRITICAL
    }

    def __init__(self, level='info', back_count=10,
                 fmt='%(asctime)s - %(pathname)s[line:(lineno)d] - %(levelname)s: %(message)s'):
        file_path = '{0}/../../logs/{1}'.format(
            __file__, 'all-{0}'.format(datetime.datetime.now().strftime("%Y-%m-%d")))
        logs_dir = os.path.dirname(os.path.abspath(file_path))
        if not os.path.exists(logs_dir):
            os.makedirs(logs_dir, exist_ok=True)
        self.logger = logging.getLogger()
        if not self.logger.handlers:
            format_str = logging.Formatter(fmt)  # 设置日志格式
            self.logger.setLevel(self.level_relation.get(level))  # 设置日志级别
            sh_out = logging.StreamHandler()  # 往屏幕上输出
            sh_out.setFormatter(format_str)  # 设置屏幕上显示的格式
            # 指定备份log文件数量的处理器
            time_handler = ScheduledDeletionDayHandler(
                file_path, "%Y-%m-%d", r"\d{4}-\d{2}-\d{2}.log", back_count=back_count)
            time_handler.setFormatter(format_str)  # 设置文件里写入的文件格式
            self.logger.addHandler(time_handler)  # 把对象加到logger里
            self.logger.addHandler(sh_out)