【问题标题】:Why and how do we redirect debug statements?为什么以及如何重定向调试语句?
【发布时间】:2012-01-30 09:36:09
【问题描述】:

我想知道为什么更喜欢将调试语句重定向到标准错误,就像这里所做的那样:

#ifdef DEBUG_TEST
  #define DEBUG_TEST 1
  #else
  #define DEBUG_TEST 0
  #endif

  #define DEBUG_PRINT(fmt, ...) \
             do { if (DEBUG_TEST) fprintf(stderr, fmt, ##__VA_ARGS__); } while (0)

另外:我们如何将这些调试语句重定向到一个单独的日志文件,该文件上有时间戳?我想在我的代码中使用宏来做到这一点。

平台:Linux,gcc编译器

【问题讨论】:

    标签: c linux macros output-redirect


    【解决方案1】:

    使用 stderr 优于 stdout 的优势在于,如果您将输出重定向到文件或将其(使用管道)流式传输到另一个进程,则调试消息不会妨碍。

    如果你想将 stderr 重定向到 Unix 上的一个文件,你可以像这样运行你的程序:

    ./program 2>logfile
    

    【讨论】:

    • 谢谢...这意味着我们也可以安全地重定向到标准输出...是否正确。
    • 你能告诉我如何在我的代码中定义一个“类似宏的函数”来做到这一点(意味着将日志重定向到日志文件)...
    • 记录到文件需要的不仅仅是宏。您可以使用 google-glog 之类的日志库,或者只是将 stderr 或 stdout 重定向到程序外部的文件。
    【解决方案2】:

    我可以举两个例子说明为什么要这样做:然后您可以轻松地将标准输出重定向到文件,同时在终端中保留标准错误(这似乎正是您在第二个问题中想要做的) .您的工具可能会突出显示发送到 stderr 的消息,从而更容易立即发现它们。

    您可以使用命令行版本来重定向 stderr,正如 thesamet 所建议的那样。要在文件名中获取时间戳,您可以在运行程序时执行以下操作:

    ./program 2>"logfile-`date`.txt"
    

    如果您想在程序本身中执行此操作,一种方法是简单地使用 fopen 打开另一个文件并写入该文件。这是一个您可以使用的完整示例:

    #include <time.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <stdbool.h>
    
    #define DEBUG_TEST true
    
    FILE *debug_file;
    
    #define DEBUG_PRINT(fmt, ...) \
            do { if (DEBUG_TEST) fprintf(debug_file, fmt, ##__VA_ARGS__); } while (false);
    
    
    
    int main()
    {
            time_t timestamp = time(NULL);
            char * s = malloc(snprintf(NULL, 0, "debug-%d.txt", timestamp));
            sprintf(s, "debug-%d.txt", timestamp);
    
            debug_file=fopen(s, "w");
    
            DEBUG_PRINT("YEAH\n");
    
            fclose(debug_file);
    
            return EXIT_SUCCESS;
    }
    

    【讨论】:

      【解决方案3】:

      您将调试信息报告给stderr 而不是stdout 的原因之一是,stdout 可能会沿着管道发送,而您的诊断将与实际数据一起使用,从而混淆管道的后续阶段。

      如果您可能想要重定向输出,或添加时间戳(或 PID 或任何其他信息),请不要直接使用 fprintf()。调用您自己设计的函数,以您想要的方式处理您想要的信息。

      因此,您的宏可能是:

      extern void dbg_print(const char *fmt, ...);
      
      #define DEBUG_PRINT(fmt, ...) \
               do { if (DEBUG_TEST) dbg_print(fmt, __VA_ARGS__); } while (0)
      

      或者:

      extern void dbg_print(const char *func, const char *file, int line, const char *fmt, ...);
      
      #define DEBUG_PRINT(fmt, ...) \
               do { if (DEBUG_TEST) dbg_print(__func__, __FILE__, __LINE__, fmt, __VA_ARGS__); } while (0)
      

      这包括信息中的函数名、文件名和行号

      例如,我有一个中等复杂的包可以做到这一点。核心内部例程之一是:

      /* err_stdio - report error via stdio */
      static void err_stdio(FILE *fp, int flags, int errnum, const char *format, va_list args)
      {
          if ((flags & ERR_NOARG0) == 0)
              fprintf(fp, "%s: ", arg0);
          if (flags & ERR_STAMP)
          {
              char timbuf[32];
              fprintf(fp, "%s - ", err_time(timbuf, sizeof(timbuf)));
          }
          if (flags & ERR_PID)
              fprintf(fp, "pid=%d: ", (int)getpid());
          vfprintf(fp, format, args);
          if (flags & ERR_ERRNO)
              fprintf(fp, "error (%d) %s\n", errnum, strerror(errnum));
      }
      

      调试包装器可以使用适当的标志调用该函数,并生成所需的输出。系统的其他部分控制使用的文件流(stderr 是默认值,但有一个功能可以将输出重定向到任何其他流),等等。

      如果您通过在调试宏中直接使用fprintf() 来限制自己,那么您会被fprintf() 的功能所困扰,或者重新编译所有内容。

      另请参阅我对'C #define macro for debug printing' 的回答,了解有关调试宏以及如何使用它们的更多信息(尽管看起来你已经接受了我在船上所说的大部分内容)。

      【讨论】:

        【解决方案4】:

        如果我错了,请原谅,但似乎没有人提到缓冲。

        默认情况下,在大多数平台上 stdout 是行缓冲的,而 stderr 是无缓冲的。基本上这意味着默认情况下,对 stdout 的写入会写入内存并以块的形式写入文件或控制台,因为逐个字符输出是很慢的。

        如果您正在调试,您不希望消息出现太晚,那么您实际上是要打印它(或者如果您的程序崩溃或陷入无限循环,甚至可能永远不会打印(这真的会让您感到困惑)当您认为它在错误的地方循环或崩溃时)并且您通常不介意速度下降。理论上它可能会由于时间和同步错误而有所作为,但无论如何都很难捕捉/调试这些错误。

        TL;在调试打印到 stderr 而不是 stdout 时的 DR(其他人可能建议改为记录到文件,或者允许运行时选项,如启用/禁用打印文件、样式、阈值等)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-10-10
          • 2018-05-18
          相关资源
          最近更新 更多