2010-06-08 54 views
2

TL; DR版本:你在Python項目中用於C++位內可配置(並且最好是捕獲)的日誌記錄?詳情如下。在Python擴展中管理日誌/警告

假設你有一些編譯好的.so模塊,可能需要做一些錯誤檢查並警告用戶(部分)不正確的數據。目前我正在使用Python代碼的logging框架和來自C/C++的log4cxx庫。 log4cxx日誌級別在文件(log4cxx.properties)中定義,目前已修復,我正在考慮如何使其更加靈活。我看到的幾個選擇:

  1. 控制它的一種方法是進行模塊範圍的配置調用。

    # foo/__init__.py 
    import sys 
    from _foo import import bar, baz, configure_log 
    configure_log(sys.stdout, WARNING) 
    
    # tests/test_foo.py 
    def test_foo(): 
        # Maybe a custom context to change the logfile for 
        # the module and restore it at the end. 
        with CaptureLog(foo) as log: 
         assert foo.bar() == 5 
         assert log.read() == "124.24 - foo - INFO - Bar returning 5" 
    
  2. 讓每個執行日誌記錄的編譯函數都接受可選的日誌參數。

    # foo.c 
    int bar(PyObject* x, PyObject* logfile, PyObject* loglevel) { 
        LoggerPtr logger = default_logger("foo"); 
        if (logfile != Py_None) 
         logger = file_logger(logfile, loglevel); 
        ... 
    } 
    
    # tests/test_foo.py 
    def test_foo(): 
        with TemporaryFile() as logfile: 
         assert foo.bar(logfile=logfile, loglevel=DEBUG) == 5 
         assert logfile.read() == "124.24 - foo - INFO - Bar returning 5" 
    
  3. 還有其他的方法嗎?

第二個似乎有點乾淨,但它需要功能簽名改變(或使用kwargs和解析它們)。首先是...可能有些尷尬,但是一次性創建整個模塊並從每個單獨的函數中刪除邏輯。

你對此有何看法?我非常樂意接受其他解決方案。

感謝,

回答

2

我在具有Python作爲多的工作發生地,只留下在C到發生在C所以我喜歡的工作一個大信徒#2比#1,但是你是對的,它會讓你的所有功能簽名變得混亂。

我會創建一個模塊級別的對象來處理日誌記錄,有點像回調。 Python代碼可以以任何方式創建對象,然後將其分配給模塊對象。 C代碼可以簡單地使用全局對象做了記錄:

# Python: 

import my_compiled_module 

def log_it(level, msg): 
    print "%s: Oh, look: %s" % (level, msg) 

my_compiled_module.logger = log_it 

# C 

static void log_it(unsigned int level, char * msg) 
{ 
    PyObject * args = Py_BuildValue("(Is)", level, msg); 
    PyObject_Call(log_it, args, NULL); 
    Py_DECREF(args); 
} 

現在,你可以簡單地調用C log_it功能在你的代碼,而不必擔心Python代碼如何得到它完成。當然,你的Python log_it函數比這個更豐富,它可以讓你將所有的日誌整合到一個Python記錄器中。

+0

感謝您的建議。我還需要弄清楚的一件事是,如果記錄不是立即發生在C模塊內,它將如何工作。我想在這種情況下,我必須通過顯式參數傳遞記錄器。 – 2010-06-09 16:25:16

+0

是的,您可以使簽名爲'log_it(char * logger_name,int level,char * msg)'。 – 2011-11-14 19:43:25

0

感謝您的信息傢伙。我發現PyObject_CallFunction更易於使用。

// C++ code with logger passed as 2nd argument 
static PyObject *lap_auction_assign(PyObject *self, PyObject *args) 
{ 
    PyObject *cost_dict; PyObject *pyLogger; 
/* the O! parses for a Python object (listObj) checked to be of type PyList_Type */ 
    if(!PyArg_ParseTuple(args, "O!O", &PyDict_Type, &cost_dict, &pyLogger)) 
    return NULL; 
/* 
....... then call the logger 
*/ 

char astring[2048]; 

sprintf(astring, "%d out of %d rows un-assigned", no_new_list, num_rows); 
PyObject_CallFunction(pyLogger, "s", astring); 

/* python */ 
# where logging is the python logging module and lap_auction is your extension 
cost_dict = {} 
tmp_assign_dict = lap_auction.assign(cost_dict, logging.info)