Write C ++ shared library journal entries using ctypes

I have a cplusplus shared library with a c interface that writes log entries to stdout. I use it in a python application using a library ctypes. The python application uses a library loggingto write log entries.

What I need to do is grab the stdout entries in the shared library for writing log entries using the module logging. In other words, I want to redirect the stdout entries of the c library to the module logging, so I can use it loggingto write to files and the console using my handlers.

I found that it is possible to write stdout ( see this SO question ), but I can only access it after the c module call is completed, and therefore it is useless to register. I want no one to block access to stdout elements.

The minimum example is as follows.

module.cpp (compiled with 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;
}

The python application that calls it:

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?')

This gives:

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?

I have access to the C ++ library, so a solution that needs modifications to the library is also welcome.

+4
source share
1 answer

, , 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)
+2

Source: https://habr.com/ru/post/1627344/


All Articles