Как я могу временно перенаправить вывод printf в c-строку? - PullRequest
2 голосов
/ 01 апреля 2010

Я пишу задание, которое включает в себя добавление некоторых функций в PostgreSQL на коробке Solaris. Как часть задания, нам нужно напечатать некоторую информацию на стороне клиента (то есть, используя elog.)

PostgreSQL уже имеет множество вспомогательных методов, которые выводят необходимую информацию, однако вспомогательные методы упакованы сотнями вызовов printf, а метод elog работает только со строками в стиле c.

Есть ли способ, которым я мог бы временно перенаправить printf вызовы в буфер, чтобы я мог легко отправить его через elog клиенту?

Если это невозможно, каков будет самый простой способ изменить вспомогательные методы так, чтобы в результате получился буфер в качестве вывода?

Ответы [ 4 ]

7 голосов
/ 01 апреля 2010

Если вы определяете свою собственную версию printf и ссылаетесь на нее до до версии libc, ваша версия заменит стандартную версию.

Вы также сможете заменить стандартную версиюверсия с использованием LD_PRELOAD для загрузки библиотеки, для которой определен printf.

Чтобы написать свой собственный printf, вы захотите использовать функцию stdarg:

int printf(const char *fmt, ...)
{
    int rv;
    va_list ap;
    va_start(ap, fmt);

    if (redirect_printf)
    {
#ifdef HAVE_VLOG
        // If you have a vlog function that takes a va_list
        vlog(fmt, ap);
        rv = ...;
#else
        char buffer[LARGESIZE];
        rv = vsnprintf(buffer, sizeof(buffer), fmt, ap);

        log(buffer);
#endif;
    }
    else
    {
        rv = vprintf(fmt, ap);
    }

    return rv;
}

Эта простая версия будет обрезать данные, когдаокончательный форматированный вывод больше LARGESIZE.Если вы не хотите этого, вы также можете вызвать vsnprintf сначала с пустым буфером, чтобы получить окончательный размер, выполнить динамическое распределение, а затем второй вызов vsprintf для форматирования буфера.

4 голосов
/ 02 апреля 2010

Вы ошибаетесь & mdash; elog поддерживает строки формата так же, как printf. Вот пример из исходного кода Postgres:

elog(DEBUG4, "TZ \"%s\" gets max score %d", tzname, i);

Так что все, что вам нужно, это добавить elog, где есть printf, используя те же параметры.

2 голосов
/ 01 апреля 2010

Самый простой способ - изменить вспомогательные методы для вызова sprintf(). Можете ли вы легко это взломать, я не знаю. Может быть

#define printf(...) sprintf(buffer, __VA_ARGS__)

Сделаю это для вас. Вам все равно нужно будет определить buffer для каждой вспомогательной функции и вернуть ее содержимое тому, кто о них заботится.

1 голос
/ 02 апреля 2010

Если вы можете допустить использование временного файла, вы можете перенаправить стандартный вызов freopen(): -

newstdout = freopen("/tmp/log", "w", stdout);

Это заставит все printf записываться в / tmp / log вместо вывода на консоль. В какой-то удобный момент позже в вашей программе вы можете открыть тот же файл для чтения: -

readfd = fopen("/tmp/log", "r");

и переслать содержимое, которое было добавлено, используя что-то вроде этого: -

void forward_to_elog(void)
{
   int bytesread;
   char buf[100];

   memset(buf,0,100);
   do {
      memset(buf,0,100);
      bytesread = fread(buf, sizeof(buf)-1, 1, readfd);
      /* call elog(buf) */ ;
   } while(bytesread);

}

Если вы оставите файл открытым, вы можете несколько раз вызвать forward_to_elog(), чтобы постепенно переслать добавленное содержимое.

Функция tmpnam() может использоваться для получения имени для временного файла, если вы не хотите статически его кодировать.

...