python logging 代码研读笔记

2020/06/06 技术

python logging 代码研读笔记

1. 可重入锁 threading.RLock 的使用

logging 库中, 使用 threading.RLock 实现线程安全.

引用7. 使用RLock进行线程同步 的说明:

如果你想让只有拿到锁的线程才能释放该锁,那么应该使用 RLock() 对象。

和 Lock() 对象一样, RLock() 对象有两个方法: acquire() 和 release() 。

当你需要在类外面保证线程安全,又要在类内使用同样方法的时候 RLock() 就很实用了。

2. stream 对象和文件操作线程安全

logging 包, 将 file, sys.stderr, sys.stdout 抽象为 stream:

stream 初始化:

# file
self.stream = open("file_name", "a", encoding="utf-8")

# stdout
self.stream = sys.stdout

# stderr
self.stream = sys.stderr

stream 写操作:

    def flush(self):
        self.acquire()
        try:
            if self.stream and hasattr(self.stream, "flush"):
                self.stream.flush()
        finally:
            self.release()


    def emit(self, record):
        try:
            msg = self.format(record)
            stream = self.stream
            # issue 35046: merged two stream.writes into one.
            stream.write(msg + self.terminator)
            self.flush()
        except RecursionError:  # See issue 36272
            raise
        except Exception:
            self.handleError(record)

stream 关闭:

    def close(self):
        """
        Closes the stream.
        """
        self.acquire()
        try:
            try:
                if self.stream:
                    try:
                        self.flush()
                    finally:
                        stream = self.stream
                        self.stream = None
                        if hasattr(stream, "close"):
                            stream.close()
            finally:
                # Issue #19523: call unconditionally to
                # prevent a handler leak when delay is set
                StreamHandler.close(self)
        finally:
            self.release()

多个 python 实例, 可以同时安全将日志写入到同一个文件中.

观察上述代码, logger 简单使用 追加模式 a 和 马上落盘 的方式, 实现写文件的多进程安全.

3. 日志文件回滚

日志文件回滚相关代码如下:

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

        Output the record to the file, catering for rollover as described
        in doRollover().
        """
        try:
            if self.shouldRollover(record):
                self.doRollover()
            logging.FileHandler.emit(self, record)
        except Exception:
            self.handleError(record)


    def doRollover(self):
        """
        Do a rollover, as described in __init__().
        """
        if self.stream:
            self.stream.close()
            self.stream = None
        if self.backupCount > 0:
            for i in range(self.backupCount - 1, 0, -1):
                sfn = self.rotation_filename("%s.%d" % (self.baseFilename, i))
                dfn = self.rotation_filename("%s.%d" % (self.baseFilename,
                                                        i + 1))
                if os.path.exists(sfn):
                    if os.path.exists(dfn):
                        os.remove(dfn)
                    os.rename(sfn, dfn)
            dfn = self.rotation_filename(self.baseFilename + ".1")
            if os.path.exists(dfn):
                os.remove(dfn)
            self.rotate(self.baseFilename, dfn)
        if not self.delay:
            self.stream = self._open()

所以, 要实现多进程安全的 RotatingFileHandler, 可以利用 fcntl 文件锁, 确保回滚过程的安全.

Search

    Table of Contents