自定义python logging handler

1. 在实际操作中要考虑的点

  1. 在工作中,日志是必不可少的信息来源,特别是在排错的时候。在不同公司,有这不同的日志文件风格,有的是每个日志文件按照日期,放在同一个文件夹下面,有的是根据pid去区分,可能都有自己特殊的需求。总结起来,就是如何去分割日志文件。

  2. 在python中,logging模块只是线程安全的,并没有保证进程安全,因此,在实际的生产环境中,都是每个应用启动多个进程,实现longging的多进程安全是很有必要的。

2. 多进程安全

先安利一个开源库,concurrent-log-handler,地址是Preston-Landers: concurrent-log-handler

在这个库里面,通过文件锁实现了进程安全。

其进入日志的写操作核心是:

def emit(self, record):
        """
        Emit a record.

        Override from parent class to handle file locking for the duration of rollover and write.
        This also does the formatting *before* locks are obtained, in case the format itself does
        logging calls from within. Rollover also occurs while the lock is held.
        """
        # noinspection PyBroadException
        try:
            msg = self.format(record)
            try:
                self._do_lock()

                try:
                    if self.shouldRollover(record):
                        self.doRollover()
                except Exception as e:
                    self._console_log("Unable to do rollover: %s" % (e,), stack=True)
                    # Continue on anyway

                self.do_write(msg)

            finally:
                self._do_unlock()
        except Exception:
            self.handleError(record)

可以看到,先调用 self._do_lock(), 然后进行日志文件的写操作,然后调用self._do_unlock().


def _do_lock(self):
        self._open_lockfile()
        if self.stream_lock:
            lock(self.stream_lock, LOCK_EX)
        else:
            self._console_log("No self.stream_lock to lock", stack=True)


elif os.name == 'posix':
    def lock(file, flags):
        try:
            fcntl.flock(file, flags)
        except IOError as exc_value:
            # The exception code varies on different systems so we'll catch
            # every IO error
            raise LockException(str(exc_value))


    def unlock(file):
        fcntl.flock(file.fileno(), fcntl.LOCK_UN)

fcntl.flock:

https://docs.python.org/2/library/fcntl.html#fcntl.flock

fcntl.flock(fd, op)¶
Perform the lock operation op on file descriptor fd (file objects providing a fileno() method are accepted as well). See the Unix manual flock(2) for details. (On some systems, this function is emulated using fcntl().)

If the flock() fails, an IOError exception is raised.

可以看到,是调用unix中的flock实现了文件锁,op这个参数,用来规定了文件锁的类型:
* LOCK_UN – unlock
* LOCK_SH – acquire a shared lock, 共享锁,所有进程没有写访问权限,即使是加锁进程也没有。所有进程有读访问权限。
* LOCK_EX – acquire an exclusive lock, 排它锁,除加锁进程外其他进程没有对已加锁文件读写访问权限。
使用LOCK_EX参数,就实现了进程安全。

进程安全的实现方式:fcntl.flock(file, LOCK_EX)

3. 实现对文件分割的需求

一般条件下,ConcurrentRotatingFileHandler基本可以满足我们的要求,但是,特殊情况,我们就需要对ConcurrentRotatingFileHandler来进行改造了。

以需要根据日期分文件夹,存储日志文件为例:

思路是:
1. 每天肯定是需要创建文件夹,这里就要考虑到多进程安全。
2. ConcurrentRotatingFileHandler是基于BaseRotatingHandler的,会涉及到baseFilename这个日志文件路径。

关于第1点考虑,参照文件锁的方法即可。第2点,修改日志文件路径baseFilename,对文件stream实例进行关闭后再进行打开即可。
这个需要重写方法shouldRollover, 并在重写的同时,创建新的文件夹目录,修改日志文件路径baseFilename

4. 总体思路

用文件锁实现进程安全,然后修改日志文件路径,正确地对日志文件流进行打开和关闭

猜你喜欢

转载自blog.csdn.net/qq_39469761/article/details/81211755