2010-06-08 38 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)