【问题标题】:Managing logs/warnings in Python extensions在 Python 扩展中管理日志/警告
【发布时间】:2010-06-08 05:47:47
【问题描述】:

TL;DR 版本: 在 Python 项目的 C++ 位中,您使用什么来进行可配置(最好是捕获)日志记录?详情如下。

假设您有一些已编译的.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 并解析它们)。第一个是.. 可能有点尴尬,但一次性设置了整个模块并从每个单独的函数中删除了逻辑。

您对此有何看法?我也很喜欢替代解决方案。

谢谢,

【问题讨论】:

    标签: python unit-testing logging log4cxx


    【解决方案1】:

    我坚信在 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 记录器中。

    【讨论】:

    • 感谢您的建议。我还需要弄清楚的一件事是,如果日志记录不是立即发生在该 C 模块中,这将如何工作。我想在那种情况下我必须通过显式参数传递记录器..
    • 是的,您可以将签名设为log_it(char * logger_name, int level, char * msg)
    【解决方案2】:

    感谢您提供的信息。我发现 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 )
    

    【讨论】:

      猜你喜欢
      • 2013-11-09
      • 2020-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多