記一次由Python的__file__導致的“bug”

引子

最近在參與一個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

歡迎留言討論 :)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容