引子
最近在參與一個Python項目,在其中寫Python代碼時碰到個很奇怪的現(xiàn)象,執(zhí)行一個打開日志功能的Python腳本,日志文件有時在Python腳本的同目錄創(chuàng)建,有時在項目的log目錄中創(chuàng)建,鼓搗了好一會才搞清楚原因,其實并不是代碼bug,而是執(zhí)行Python腳本的姿勢錯了,追根溯源的debug過程很有意思,遂記錄一下。
場景還原
項目工程位于centos,Python3.6.8
為了簡化與保密,假設項目工程根目錄是/..x/,則日志目錄是/..x/log,而我的Python腳本在/..x/scheduler中,為了還原場景,在/..x/scheduler中新建了一個test.py,導入了公共的日志模塊,代碼如下:
import xxxx # 關于日志的模塊,其中已經定義好了LOG_DIR='/..x/log'
if __name__ == '__main__':
log_add_default_logger(__file__, LOG_DIR)
log_info("llll")
調用log_info函數(shù)就可以直接往日志中寫一行
log_add_default_logger()是公共日志庫提供的接口,一般情況下(項目開發(fā)者所希望的),日志文件的絕對路徑名是/..x/log/test.py.20200520.log,觀察了一下,/..x/log中有很多類似于這種命名的文件(先提示一下,后綴.20200520.log是在log_info內部某函數(shù)添加的,獲取當前系統(tǒng)的日期,所以與log_add_default_logger無關)
我用 vscode remote 連接了 centos ,用 vscode 打開test.py文件,在編輯器區(qū)域單擊鼠標右鍵,點擊"Run Python File in Terminal",可以直接運行test.py腳本,這種“快速運行”
的方式用起來很爽,其效果等同于:
[root@VM /..x/scheduler]$ /usr/bin/python3 /..x/scheduler/test.py
現(xiàn)在應該寫入日志文件了,猜猜log_info("llll")中的llll輸出在哪?
- 選項1:
/..x/scheduler/test.py.20200520.log - 選項2:
/..x/log/test.py.20200520.log
答案是選項1,我第一次也是懵的,為啥別人的日志文件都乖乖輸出在/..x/log目錄中,而我的日志文件輸出在了同目錄(/..x/scheduler)?
查看/..x/scheduler/test.py.20200520.log的內容:
[2020-05-20 20:36:39,042][9677][MainThread][INFO][log.py:494][test.py:<module>:16][/..x/scheduler/test.py] : llll
唔......看來得看看log_add_default_logger內部做了啥
在 vscode 項目中ctrl+鼠標左鍵單擊log_add_default_logger(__file__, LOG_DIR)層層 dive into,發(fā)現(xiàn)了這行代碼:
# 某個函數(shù)內
filename = os.path.join(log_dir, log_id) # log_id就是傳入的__file__
看來還挺簡單的,就是用Python os模塊自帶的join函數(shù),把路徑與文件名組合起來,最后就形成了絕對路徑filename,那接下來就調試一下,首先修改一下test.py:
import xxxx # 關于日志的模塊,其中已經定義好了LOG_DIR='/..x/log'
if __name__ == '__main__':
log_add_default_logger(__file__, LOG_DIR)
print("__file__: " + __file__)
print("LOG_DIR: " + LOG_DIR)
print("os.path.join(LOG_DIR, __file__): " + os.path.join(LOG_DIR, __file__))
log_info("llll")
繼續(xù)在vscode中“快速運行”,terminal輸出如下:
[root@VM /..x/scheduler]$ /usr/bin/python3 /..x/scheduler/test.py
__file__: /..x/scheduler/test.py
LOG_DIR: /..x/log
os.path.join(LOG_DIR, __file__): /..x/scheduler/test.py
看到第三行輸出了嗎,組合起來之后不是/..x/log/test.py,而是/..x/scheduler/test.py,看來罪魁禍首是這個os.path.join()函數(shù)?
想到這,我想起centos有兩個Python版本,一個是3.6.8,一個是3.7.2,但兩個版本對于這個場景無甚差別,看來不是Python版本的原因,也就不是os.path.join()的原因,/..x/log與/..x/scheduler/test.py進行join的結果就是/..x/scheduler/test.py沒錯
于是Google,查到這篇文章:https://blog.csdn.net/cjh6311882/article/details/22078347,這是由于__file__輸出絕對路徑所導致的!
保持test.py不變,不使用vscode的“快速運行”了,老老實實在terminal手動執(zhí)行腳本:
[root@VM /..x/scheduler]$ python3 test.py # python3 等同于 /usr/bin/python3
__file__: test.py
LOG_DIR: /..x/log
os.path.join(LOG_DIR, __file__): /..x/log/test.py
BINGO! 看來是執(zhí)行Python腳本的方式錯誤,不能用絕對路徑調用,查看日志文件,果然在/..x/log/中!
[2020-05-20 21:16:01,302][17384][MainThread][INFO][log.py:494][test.py:<module>:16][test.py] : llll
注意到日志文件中的輸出,文件名是[test.py],回頭看看錯誤姿勢的日志文件內容,文件名是[/..x/scheduler/test.py],它們倆不同是因為__file__不同,至此,這次debug過程前因后果都說清楚了
布置作業(yè)
位于/..x/scheduler中的腳本以后會由作業(yè)系統(tǒng)周期性調用,假設現(xiàn)在讓你用Linux中的crontab模擬一下,你是聰明的,你知道要怎么編寫crontab才能準確地讓日志文件輸出到目錄/..x/log中嗎?
(提示:* * * * * python3 /..x/scheduler/test.py會讓日志文件輸出到目錄/..x/scheduler中
歡迎留言討論 :)