2016-11-29 39 views
2

閱讀Python的logging庫(2.7版本)的文檔,我碰到下面傳來:爲什麼禁止覆蓋日誌記錄屬性?

Logger.debug(MSG,* ARGS,** kwargs)

[...]第二關鍵字參數是額外的,可用於傳遞一個字典,該字典用於使用用戶定義的屬性填充爲記錄事件創建的LogRecord的__dict__。這些自定義屬性可以隨意使用。例如,它們可以合併到記錄的消息中。 [...] 字典中傳遞的鍵不應與記錄系統使用的鍵衝突。 [emph。 mine]

那麼爲什麼這個約束存在?在我看來,這無法從庫中靈活地取消靈活性(開發人員需要檢查哪些鍵是內置的,哪些不是)。

假設你想要寫它記錄一個裝飾功能的入口和出口:

def log_entry_exit(func): 
    def wrapper(*args, **kwargs): 
     logger.debug('Entry') 
     result = func(*args, **kwargs) 
     logger.debug('Exit') 
     return result 
    return wrapper 

@log_entry_exit 
def foo(): 
    pass 

假設你也想記錄的封閉函數的名稱:

format_string = '%(funcName)s: %(message)s' 

糟糕!這不起作用。輸出是:

>>> foo() 
wrapper: Entry 
wrapper: Exit 

當然的功能名稱求wrapper,因爲這是封閉的功能。然而,這不是我想要的。我想打印裝飾功能的功能名稱。因此這將是非常方便的,只是修改我的記錄來電:

logger.debug('<msg>', extra={'funcName': func.__name__}) 

但是(如文檔已經指出的)這不工作:

KeyError: "Attempt to overwrite 'funcName' in LogRecord" 

不過這將是一個非常簡單的並解決問題的解決方案。

那麼,爲什麼logging阻止我爲內置屬性設置自定義值?

+0

我不確定這是否真的可以回答。該模塊按照文檔中的描述工作。至於*爲什麼*這個模塊就是這樣設計的,你就得問問開發人員。 – augurar

回答

2

不是作者,我不能確定,但​​我有一個預感。

看着 https://hg.python.org/cpython/file/3.5/Lib/logging/init.py,這似乎是扔你引用的錯誤代碼:

rv = _logRecordFactory(name, level, fn, lno, msg, args, exc_info, func, sinfo) 
if extra is not None: 
    for key in extra: 
     if (key in ["message", "asctime"]) or (key in rv.__dict__): 
      raise KeyError("Attempt to overwrite %r in LogRecord" % key) 
     rv.__dict__[key] = extra[key] 

望着_ _ INIT _在該文件中_()方法中,我們可以看到,它集長期屬性,其中至少有一些是用來跟蹤對象狀態的列表(從其他地方借用的術語,這些服務私有成員變量的目的):

self.args = args 
self.levelname = getLevelName(level) 
self.levelno = level 
self.pathname = pathname 
try: 
    self.filename = os.path.basename(pathname) 
    self.module = os.path.splitext(self.filename)[0] 
except (TypeError, ValueError, AttributeError): 
    self.filename = pathname 
    self.module = "Unknown module" 
self.exc_info = exc_info 
self.exc_text = None  # used to cache the traceback text 
self.stack_info = sinfo 
self.lineno = lineno 
self.funcName = func 
[...] 

該代碼使得在各個地方的假設這些屬性包含它們被初始化爲包含的內容;而不是防禦性地檢查每次使用該值時該值是否合理,則會阻止嘗試更新它們中的任何一個,如上所述。而且,它不是試圖區分「安全覆蓋」和「不安全覆蓋」屬性,而是簡單地阻止任何覆蓋。

在funcName的特殊情況下,我懷疑你不會因爲覆蓋它而遭受任何不良影響(除了顯示不同的funcName)。

可能的前進道路:

  • 現場與限制
  • 覆蓋Logger.makeRecord()允許了funcName的更新
  • 覆蓋記錄儀添加setFuncName()方法

當然,無論你做什麼,仔細測試你的修改,以避免意外。