【问题标题】:How to redirect stderr output (using fprintf )to a log file?如何将 stderr 输出(使用 fprintf )重定向到日志文件?
【发布时间】:2021-02-04 01:16:21
【问题描述】:

我正在做一些调试,并希望将标准错误消息重定向到一个文件;因此,它可以在调试日志文件中查看。 我确实需要从 C 代码完成,而不是通过 UNIX shell 重定向。

我需要调试一些这样的模块。 我有一组写入 strerr(console) 的调试工具。它很容易包含在源代码中,并提供一组舒适的宏和函数以用于调试源代码。

以下是头文件中的代码。

#include <stdio.h>

#define LOGWRAPPER(tag, fmt, args...) \
  fprintf(stderr, "%s: " fmt "\n", tag, ##args)   

#define LOG_DEBUG(...) LOGWRAPPER(__VA_ARGS__)

...

...

#define LOG_DEBUG(tag, fmt, args...)                 \
  do {                                               \
    (true) ? ALOG(LOG_DEBUG, tag, fmt, ##args)       \
           : fprintf(stderr, "%s" fmt, tag, ##args); \
  } while (0)
....

最终所有的打印都会回到这里,也就是头文件。所以我想我必须做一些改变

#define LOGWRAPPER(tag, fmt, args...) \
  fprintf(stderr, "%s: " fmt "\n", tag, ##args)

有什么想法吗?还是我应该创建一个文件路径并插入到 fprintf 中?

【问题讨论】:

  • 如果您使用宏进行记录,请将__func____FILE____LINE__ 添加到记录的数据中,以便立即清楚它的来源。并且该宏有一个问题 - 对于不是字符串文字的任何格式字符串,它将无法编译。
  • 我确实在我的消息行中使用了 funcFILELINE。注意:最终所有与打印相关的函数都会回调到这个头文件。我现在不明白的事情是如何将 stderr 输出重定向到文件/
  • __func____FILE____LINE__ 应该内置在宏中,这样调用者就不必重复输入它们。

标签: c file logging stderr io-redirection


【解决方案1】:

我确实需要通过 C 代码完成,而不是通过 UNIX shell 重定向。

了解 shell 如何实现重定向将使您对如何在自己的程序中执行此操作有必要的了解。

POSIX 定义了三个在每个进程开始时可用的标准文件描述符:0 = stdin,1 = stdout,2 = stderr

C 标准库还定义了三个FILE*通过全局可用符号公开,分别命名为stdinstdoutstderr,并直接对应于数字文件描述符; POSIX 标准定义了一个在 FILE* 流上运行的附加函数,称为 fileno

归根结底,一切都归结为文件描述符。

虽然可以freopen 一个现有的流到一个新的目的地,但这通常会产生一个新的、不同的文件描述符。虽然您响应的FILE* 流(可能通过stderr 之类的全局符号)将转到那个新位置,但它不会影响程序的某些部分,它会绕过C 标准库函数。 因此推荐的方法是替换文件描述符本身,这就是系统调用dup2 所做的。

使用dup2,您可以获取现有文件描述符并将其复制覆盖到另一个现有文件描述符上。因此,如果您打开一个日志文件,并将其复制到文件描述符 2 (=stderr) 上,它的输出将被重定向到那里。

这就是 shell 重定向的方式,您也可以在程序中使用它:

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
    /* replace fd=2, i.e. stderr, with a file of our choosing: */
    int fd_logfile = open("logfile.txt", O_RDWR | O_CREAT | O_APPEND, 0644);
    if( 0 <= fd_logfile ){
        dup2(fd_logfile, 2);
        close(fd_logfile);
    }
    
    /* ... */
    fprintf(stderr, "This should go to logfile.txt\n");
}

【讨论】:

  • 感谢先生的有用想法。但我在这里有一个小问题:为什么需要在复制后立即关闭 fd_logfile。为什么不在其他地方或其他功能?
  • @ahtong:调用dup2 后,您必须将文件描述符指向同一个文件。最初打开fd_logfile2。您只需要打开其中一个(2);另一个是多余的,可以(并且应该)在dup2-ing 之后立即关闭。
【解决方案2】:

在 C 代码中重定向 stderr 的典型方法是使用 dup2() (本质上)将文件描述符映射到新文件。例如,类似:

int f = open ("mylog.txt", O_CREAT | O_RDWR | O_APPEND, 0644); dup2 (f, STDERR_FILENO);

还有其他方法 - 例如,您可以在 stderr 上使用 freopen()

但是……

如果您使用自己定义的宏进行所有日志记录,为什么不简单地定义宏来做一些完全不同的事情呢?也就是说,定义LOG_DEBUG 来调用您提供的函数,而不是fprintf()

我确信对此还有其他意见,但我不喜欢使用stderr 进行例行日志记录。我只希望stderr 收到严重的、破坏程序的错误消息。有可能(也许)您的代码不是唯一可能写入stderr 的代码——库也可能这样做。收集它们的输出并将其发送到一般调试日志可能是合适的,但同样,它可能不合适。我总是使用专门为此目的的函数进行一般日志记录。

【讨论】:

  • 你能举例说明如何在 stderr 上使用 freopen() 吗?我确定 #define LOGWRAPPER(tag, fmt, args...) \ fprintf(stderr, "%s: " fmt "\n", tag, ##args) 是我唯一允许的变化
  • freopen ("mylog.txt", "a+", stderr); 但这并没有什么区别——改变stderr 的处理方式会改变写入stderr 的所有内容,但它确实如此。
猜你喜欢
  • 2011-07-25
  • 1970-01-01
  • 2013-04-15
  • 1970-01-01
  • 2016-10-07
  • 2017-05-31
  • 2019-03-20
  • 1970-01-01
  • 2012-10-11
相关资源
最近更新 更多