这个用于Linux库的style-guide建议“允许应用程序挂钩登录其日志记录工具的lib。”通常采用什么机制?一个带指向日志功能的指针的函数?你能举例说明吗?
答案 0 :(得分:2)
这是SQLite的一个例子。您可以使用sqlite3_config()
参数调用CONFIG_LOG
来设置日志记录方式。传入函数指针和void指针。函数指针有三个参数; void指针,整数和char *
。当SQLite需要记录某些内容时,它会调用您的函数指针,传入传递给CONFIG_LOG
的void指针,结果代码和包含日志消息的字符串。
SQLITE_CONFIG_LOG
SQLITE_CONFIG_LOG选项有两个参数:一个指向调用签名为
void(*)(void*,int,const char*)
的函数的指针,以及一个指向void的指针。如果函数指针不为NULL,则由sqlite3_log()调用它来处理每个日志记录事件。如果函数指针为NULL,则sqlite3_log()接口变为no-op。每当调用该函数时,作为SQLITE_CONFIG_LOG的第二个参数的void指针作为第一个参数传递给应用程序定义的记录器函数。记录器函数的第二个参数是相应sqlite3_log()调用的第一个参数的副本,旨在作为结果代码或扩展结果代码。传递给记录器的第三个参数是通过sqlite3_snprintf()格式化后的日志消息。 SQLite日志记录界面不可重入;应用程序提供的记录器功能不得调用任何SQLite接口。在多线程应用程序中,应用程序定义的记录器功能必须是线程安全的。
您可以在SQLite's printf.c
中查看sqlite3_log()
的实际实施情况。它使用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
,您可以使用png_get_error_ptr()
和char *
访问您的空指针。同样,libpng处理snprintf()
,只是将一个char *
传递给日志记录回调。
答案 1 :(得分: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
函数指针设置为它们自己的函数。