[Python] 使用 fcntl() 取得 zlog 使用的 file lock,避免衝突
之前的一個 C 專案程式,用了 zlog 這個 3rd-party 套件來寫記錄檔 (log),
不過因為 zlog 它雖然可以自動 rotate log
(就是 a.log 滿的時候會改名成 a.log.1,再把新的 log 寫到全新的 a.log 裡面),
但 zlog 並沒有壓縮 log 的功能,所以我們是另外用 python 來壓縮 log…
上面的作法本來也沒太大問題,
只是偶爾會遇到「Python 正在壓縮 log 時,zlog 也跑來要 rotate log」的問題…
這遇到的時候,正在被壓縮的檔案可能突然被 zlog 改名或移走…
要怎麼解決呢?
這時候還是得來看一下 zlog 的原始碼…
從原始碼裡面可以看到,zlog 在 rotate log 之前,
會先呼叫 zlog_rotater_trylock() 想要取得一個 file lock,取到之後才會做 log rotate。
而取得 file lock 的方式是用 fcntl():
fcntl(a_rotater->lock_fd, F_SETLK, &fl)
有用 file lock 就簡單多了~
我們只要在 python 壓縮 log 之前,也去取得一樣的 file lock,
就能避免兩邊同時做事打架了~
下面是改寫後的 python 程式,
首先用 open() 將 zlog 的 lock file 打開,接著用 fcntl() 取得 file lock,
做完檔案壓縮後,再將 file lock 放掉 (寫在 finally 裡確保一定會被執行到):
# Acquire file lock for test_zlog.lock, # so that there would be no log rotation # while compressing test.log*. zlog_lock_file = "/tmp/test_zlog.lock" with open(zlog_lock_file, "w") as f: try: fcntl.fcntl(f.fileno(), fcntl.F_SETLKW, struct.pack('hhllhh', fcntl.F_WRLCK, 0, 0, 0, 0, 0)) # Compress test.log.* # ...... finally: # Release file lock for test_zlog.lock fcntl.fcntl(f.fileno(), fcntl.F_SETLKW, struct.pack('hhllhh', fcntl.F_UNLCK, 0, 0, 0, 0, 0))
這樣子改寫之後,zlog 和 python 兩邊就能遵守 file lock,
不再互相打架囉~
參考資料:
Python: fcntl — The fcntl and ioctl system calls
Linux Programmer’s Manual: fcntl