2016-02-04 31 views
4

我有一個cplusplus共享庫,具有c接口,寫入標準輸出日誌條目。我在使用​​庫的python應用程序中使用它。 python應用程序使用logging庫來寫入日誌條目。捕獲與ctypes C++共享庫日誌條目

我需要做的是捕獲共享庫的stdout條目,以便用logging模塊寫入日誌條目。換句話說,我想將c庫的標準輸出項重定向到logging模塊,因此我可以使用logging使用其處理程序寫入文件和控制檯。

我發現可以捕獲標準輸出(see this SO question),但只有在c模塊調用結束時纔可以訪問它,因此對於日誌記錄來說它是無用的。我想要一種無阻塞的方式來訪問stdout條目。

最小的例子如下。

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++庫,因此需要在庫中進行修改的解決方案也是受歡迎的。

+0

「,但我可以訪問它,只有當c模塊調用結束「 - 你確定嗎?你不能在另一個線程中運行模塊調用,或者在另一個線程中執行管道檢查嗎? – Claudiu

+0

@Claudiu感謝您的關注。這句話我的意思是說,另一個問題的答案不會「按原樣」工作。我沒有嘗試你提出的基於線程的解決方案,我會嘗試。但與此同時,如果你可以用這個想法寫一個答案,它將非常感謝:) – eguaio

回答

2

在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) 
+0

這是一個很好的解決方案。我會稍微調整它,並將其發佈在答案中。 – eguaio