1

这个Linux 库的风格指南建议“允许应用程序将库挂接到其日志记录工具中”。这通常是通过什么机制完成的?一个带有指向日志记录函数的指针的函数?你能举出例子吗?

4

2 回答 2

2

这是来自 SQLite 的示例。sqlite3_config()您可以通过使用CONFIG_LOG参数调用来设置它的记录方式。您传入一个函数指针和一个 void 指针。函数指针接受三个参数;一个 void 指针、一个整数和一个char *. 当 SQLite 需要记录某些内容时,它会调用您的函数指针,传入您传递给的 void 指针、CONFIG_LOG结果代码和包含日志消息的字符串。

SQLITE_CONFIG_LOG

SQLITE_CONFIG_LOG 选项有两个参数:一个指向函数的指针,其调用签名为void(*)(void*,int,const char*), 和一个指向 void 的指针。如果函数指针不为 NULL,则 sqlite3_log() 调用它来处理每个日志记录事件。如果函数指针为 NULL,则 sqlite3_log() 接口变为空操作。每当调用该函数时,作为 SQLITE_CONFIG_LOG 的第二个参数的 void 指针作为第一个参数传递给应用程序定义的记录器函数。logger 函数的第二个参数是对应 sqlite3_log() 调用的第一个参数的副本,旨在作为结果代码或扩展结果代码。传递给记录器的第三个参数是通过 sqlite3_snprintf() 格式化后的日志消息。SQLite 日志接口不可重入;应用程序提供的记录器函数不得调用任何 SQLite 接口。在多线程应用程序中,

您可以sqlite3_log()SQLite 的printf.c. 它使用snprintf()(即它们的包装器)打印到堆栈上的缓冲区中,因为当您无法调用时可能会调用日志记录函数malloc,然后将结果传递回用户配置的函数指针,以及他们提供的值和错误代码。

/*
** This is the routine that actually formats the sqlite3_log() message.
** We house it in a separate routine from sqlite3_log() to avoid using
** stack space on small-stack systems when logging is disabled.
**
** sqlite3_log() must render into a static buffer.  It cannot dynamically
** allocate memory because it might be called while the memory allocator
** mutex is held.
*/
static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){
  StrAccum acc;                          /* String accumulator */
  char zMsg[SQLITE_PRINT_BUF_SIZE*3];    /* Complete log message */

  sqlite3StrAccumInit(&acc, zMsg, sizeof(zMsg), 0);
  acc.useMalloc = 0;
  sqlite3VXPrintf(&acc, 0, zFormat, ap);
  sqlite3GlobalConfig.xLog(sqlite3GlobalConfig.pLogArg, iErrCode,
                           sqlite3StrAccumFinish(&acc));
}

/*
** Format and write a message to the log if logging is enabled.
*/
void sqlite3_log(int iErrCode, const char *zFormat, ...){
  va_list ap;                             /* Vararg list */
  if( sqlite3GlobalConfig.xLog ){
    va_start(ap, zFormat);
    renderLogMsg(iErrCode, zFormat, ap);
    va_end(ap);
  }
}

或者你可以看看libpng。默认情况下,它会将错误记录到 stderr,但您可以提供自己的回调来覆盖它。你调用png_set_error_fn(),传递你的png_struct,一个空指针和两个回调,一个用于错误,一个用于警告。然后它会用两个参数调用你的函数指针;,png_struct您可以通过它访问您的 void 指针png_get_error_ptr(), 和char *. 同样,libpng 处理snprintf(), 并且只传递一个char *到日志回调。

于 2012-11-14T18:47:15.063 回答
0

例如,在您的库的配置中,您可以有一个用户定义的函数指针:

struct my_lib_config
{
    void (* log) (const char * message);
};

有一个合理的默认值:

void log_default (const char * message)
{
    fprintf (stderr, "%s\n", message);
}

if (!config.log)
    config.log = log_default;

这样,您的库将默认记录到 stderr,除非应用程序将log函数指针设置为自己的函数。

于 2012-11-14T18:42:51.737 回答