【问题标题】:Capture c++ shared library log entries with ctypes使用 ctypes 捕获 c++ 共享库日志条目
【发布时间】:2016-05-14 14:15:01
【问题描述】:

我有一个 cplusplus 共享库,带有一个 c 接口,可以在标准输出中写入日志条目。我在使用ctypes 库的python 应用程序中使用它。 python 应用程序使用logging 库来写入日志条目。

我需要做的是捕获共享库的标准输出条目,以使用logging 模块写入日志条目。换句话说,我想将 c 库的 stdout 条目重定向到 logging 模块,因此我可以使用 logging 写入文件并使用其处理程序进行控制台。

我发现可以捕获标准输出 (see this SO question),但我只能在 c 模块调用结束时访问它,因此它对日志记录毫无用处。我想要一种无阻塞的方式来访问标准输出条目。

一个最小的例子如下。

module.cpp(用g++ -fPIC -shared module.cpp -o module.so编译)

#include <unistd.h>
#include <iostream>

using namespace std;

extern "C" int callme()
{
  cout<<"Hello world\n";
  sleep(2);
  cout<<"Some words\n";
  sleep(2);
  cout<<"Goodby world\n";
  return 0;
}

调用它的python应用程序:

import ctypes as ct
import logging

format='%(asctime)s - %(levelname)s - %(message)s', level=logging.DEBUG
logging.basicConfig(format=format)

logging.debug('This logging modules works like a charm!')

mymodule = ct.CDLL('./module.so')
mymodule.callme()

logging.info('I want to capture the shared library log entries')
logging.warning('Can I?')

这会产生:

2016-02-04 16:16:35,976 - DEBUG - This logging modules works like a charm!
Hello world
Some words
Goodby world
2016-02-04 16:16:39,979 - INFO - I want to capture the shared library log entries
2016-02-04 16:16:39,979 - WARNING - Can I?

我可以访问 c++ 库,因此也欢迎需要在库中修改的解决方案。

【问题讨论】:

  • "但我只能在 c 模块调用结束时访问它" - 你确定吗?你不能在另一个线程中运行模块调用,或者在另一个线程中运行管道检查吗?
  • @Claudiu 感谢您的关注。这句话的意思是另一个问题的答案不会“按原样”工作。我没有尝试您提出的基于线程的解决方案,我会尝试一下。但与此同时,如果你能用这个想法写一个答案,那将不胜感激:)

标签: python c++ c logging ctypes


【解决方案1】:

您应该能够通过在 C 模块调用运行时从线程中的管道读取来修改链接答案中的代码。以下应该可以工作,尽管我没有使用长时间运行的模块调用对其进行测试:

def redirected_printed_output(module_call):
    # the pipe would fail for some reason if I didn't write to stdout at some point
    # so I write a space, then backspace (will show as empty in a normal terminal)
    sys.stdout.write(' \b')
    pipe_out, pipe_in = os.pipe()
    # save a copy of stdout
    stdout = os.dup(1)
    # replace stdout with our write pipe
    os.dup2(pipe_in, 1)

    # check if we have more to read from the pipe
    def more_data():
        r, _, _ = select.select([pipe_out], [], [], 0)
        return bool(r)

    # read the pipe, writing to (former) stdout
    def write_pipe_to_stdout():
        while more_data():
            os.write(stdout, os.read(pipe_out, 1024))

    done = False
    def read_loop():
        # rewrite the pipe out to stdout in a loop while the call is running
        while not done:
            write_pipe_to_stdout()
        # Finish the remnants
        write_pipe_to_stdout()

    t = threading.Thread(target=read_loop)
    t.start()

    module_call()

    done = True
    t.join() # wait for the thread to finish

    # put stdout back in place 
    os.dup2(stdout, 1)

我测试如下(OSX):

import ctypes
libc = ctypes.CDLL('libc.dylib')
def zomg():
    for i in xrange(5):
        libc.printf('libc stdout: %d\n', i)
        time.sleep(1)

redirected_printed_output(zomg)

【讨论】:

  • 这是一个很好的解决方案。我会稍微调整一下并将其发布在答案中。
猜你喜欢
  • 1970-01-01
  • 2019-02-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-26
相关资源
最近更新 更多