How can I redirect output to a boost log?
Asked Answered
G

2

3

I have a C++ program that uses boost log, and I load a user-provided dynamic link library. I'd like to redirect stderr to the boost log, so that anytime the user's library does:

std::cerr << "Some stuff";

It produces the same result** as:

BOOST_LOG_SEV(log,info) << "Some stuff";

Is this possible, and if so then how do I do it?

(Also, I'm not sure what to do about the severity... since cerr << doesn't privide severity information. I'm open to suggestions on that as well...)

** By "same result" I mean that it gets logged to the same log file as the rest of the log messages, and the same log formatter is applied to those lines.

Grube answered 29/7, 2013 at 3:7 Comment(0)
C
3

Here's my C++11 implementation. This class can be used for anything (not just boost) to capture stdout/stderr on a line-by-line basis and calling a user function (could be a lambda) to process it.

Warning: if you redirect stderr and stdout and are using Boost, then redirect stderr first, then stdout. Otherwise, Boost will write loopback the stderr message back to stdout, and you'll get a boost log entry inside another boost log entry.

Usage example

cout << "testing out before 1 2 3 " << endl;
cerr << "testing err before 1 2 3 " << endl;
{
    StdErrHandler err([](const char* line){ 
          BOOST_LOG_TRIVIAL(error) << "ERROR:" << strlen(line) << " " << line; 
    });
    StdOutHandler out([](const char* line){
          BOOST_LOG_TRIVIAL(info) << "OUT:" << strlen(line) << " " << line; 
    });
    cout << "cout testing 1 2 3 " << endl;
    cerr << "cerr testing 1 2 3 " << endl;
}
cout << "testing out after 1 2 3 " << endl;
cerr << "testing err after 1 2 3 " << endl;

Example output

pa-poca$ ./test
testing out before 1 2 3
testing err before 1 2 3
[2014-08-01 12:24:56.468335] [0x000007f89d8990d4] [error]   ERROR:19 cerr testing 1 2 3
[2014-08-01 12:24:56.468360] [0x000007f89d8990d4] [info]    OUT:19 cout testing 1 2 3
testing out after 1 2 3
testing err after 1 2 3

Code

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>

class StdioHandler
{
private:
    pid_t pid = 0;
    int origfd;
    int streamid;
    int pipefd[2];
public:
    enum class Stream
    {
        stdout = STDOUT_FILENO,
        stderr = STDERR_FILENO
    };
    StdioHandler(Stream stream, std::function<void(const char*)> callback)
        :streamid(static_cast<int>(stream))
    {
            origfd = dup(streamid);

        pipe(pipefd); // create pipe
        pid = fork(); //spawn a child process to handle output of pipe
        if (pid == 0)
        {
            char line[256];
            FILE* output;

            close(pipefd[1]);
            output = fdopen(pipefd[0], "r");
            if (output)
            {
              while(fgets(line, sizeof(line), output)) 
              {

                 int n = strlen(line);
                 if (n > 0)
                     if (line[n-1] == '\n') line[n-1] = 0;
                 callback(line);
              }
              fclose(output);
            }
            abort();
        } else {
            // connect input of pipe to
            close(pipefd[0]);
            dup2(pipefd[1], streamid);
        }
    }

    ~StdioHandler()
    {
        int status;

        usleep(10000);

        close(pipefd[1]);
        kill(pid,SIGINT);

        waitpid(pid, &status, 0);

        dup2(origfd, streamid);
    }
};

class StdOutHandler : public StdioHandler
{
public:
    StdOutHandler(std::function<void(const char*)> callback) :
        StdioHandler(Stream::stdout, callback)
    {
    }
};

class StdErrHandler : public StdioHandler
{
public:
    StdErrHandler(std::function<void(const char*)> callback) :
        StdioHandler(Stream::stderr, callback)
    {
    }
};
Clomp answered 1/8, 2014 at 19:32 Comment(1)
Note that Boost.Log explicitly does not support process forking. This is likely to lead to log corruption if the child process happens to write to the log at the same time as the parent, which seems impossible to prevent.Vacillate
S
-2

I am guessing , You can redirect File descriptor STDERR to your stream file descriptor [you have to get the file desc of your stream] using dup/dup2 api [its a posix api]

Sarcophagus answered 29/7, 2013 at 5:55 Comment(1)
AFAIK, there is no documented way to get the underlying file descriptor for a boost log sink. I don't know if it's even possible (using some undocumented method). Besides, that would actually bypass the logging mechanism instead of going through it (e.g., bypassing all the filtering, formatting, etc.)Grube

© 2022 - 2024 — McMap. All rights reserved.