自定義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. 總體思路

用文件鎖實現進程安全,然後修改日誌文件路徑,正確地對日誌文件流進行打開和關閉

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章