Как я могу перенаправить вывод в журнал повышения?

У меня есть программа на C++, которая использует журнал повышения, и я загружаю предоставленную пользователем библиотеку динамической компоновки. Я хотел бы перенаправить stderr в журнал повышения, чтобы в любое время пользовательская библиотека делала:

std::cerr << "Some stuff";

Он дает тот же результат**, что и:

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

Возможно ли это, и если да, то как мне это сделать?

(Кроме того, я не уверен, что делать с серьезностью... поскольку cerr << не предоставляет информацию о серьезности. Я также открыт для предложений по этому поводу...)

** Под «тот же результат» я подразумеваю, что он записывается в тот же файл журнала, что и остальные сообщения журнала, и к этим строкам применяется тот же форматировщик журнала.


person Dave Lillethun    schedule 29.07.2013    source источник


Ответы (2)


Вот моя реализация С++ 11. Этот класс можно использовать для чего угодно (не только для ускорения), чтобы захватывать stdout/stderr построчно и вызывать пользовательскую функцию (может быть лямбда) для ее обработки.

Предупреждение: если вы перенаправляете stderr и stdout и используете Boost, сначала перенаправьте stderr, а затем stdout. В противном случае Boost запишет сообщение stderr обратно в stdout, и вы получите запись журнала повышения внутри другой записи журнала повышения.

Пример использования

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;

Пример вывода

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

Код

#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)
    {
    }
};
person Mark Lakata    schedule 01.08.2014
comment
Обратите внимание, что Boost.Log явно не поддерживает разветвление процессов. Это может привести к повреждению журнала, если дочерний процесс будет писать в журнал одновременно с родительским процессом, что кажется невозможным предотвратить. - person Tom Hunt; 30.01.2017

Я предполагаю, что вы можете перенаправить File descriptor STDERR на дескриптор файла вашего потока [вам нужно получить описание файла вашего потока], используя dup/dup2 api [это posix api]

person Anand Rathi    schedule 29.07.2013
comment
Насколько мне известно, нет документированного способа получить базовый файловый дескриптор для приемника журналов повышения. Я не знаю, возможно ли это (используя какой-то недокументированный метод). Кроме того, это фактически обошло бы механизм ведения журнала, а не проходило бы через него (например, обход всей фильтрации, форматирования и т. д.). - person Dave Lillethun; 29.07.2013