Reputation: 8604
This style-guide for Linux libraries suggests "Allow the app to hook the libs logging into its logging facility." By what mechanism is this typically done? A function that takes a pointer to a logging function? Can you show examples?
Upvotes: 1
Views: 584
Reputation: 332836
Here's an example from SQLite. You can set up how it logs by calling sqlite3_config()
with a CONFIG_LOG
argument. You pass in a function pointer, and a void pointer. The function pointer takes three arguments; a void pointer, an integer, and a char *
. When SQLite needs to log something, it calls your function pointer, passing in the void pointer that you passed to CONFIG_LOG
, a result code, and a string containing the log message.
SQLITE_CONFIG_LOG
The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a function with a call signature of
void(*)(void*,int,const char*)
, and a pointer to void. If the function pointer is not NULL, it is invoked by sqlite3_log() to process each logging event. If the function pointer is NULL, the sqlite3_log() interface becomes a no-op. The void pointer that is the second argument to SQLITE_CONFIG_LOG is passed through as the first parameter to the application-defined logger function whenever that function is invoked. The second parameter to the logger function is a copy of the first parameter to the corresponding sqlite3_log() call and is intended to be a result code or an extended result code. The third parameter passed to the logger is log message after formatting via sqlite3_snprintf(). The SQLite logging interface is not reentrant; the logger function supplied by the application must not invoke any SQLite interface. In a multi-threaded application, the application-defined logger function must be threadsafe.
You can see the actual implementation of sqlite3_log()
in SQLite's printf.c
. It uses snprintf()
(their wrapper around it, that is) to print into a buffer on the stack, since logging functions may be called when you can't call malloc
, and then pass the result back into the function pointer the user has configured, along with the value they provided and the error code.
/*
** 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);
}
}
Or you could take a look at libpng. By default, it logs errors to stderr, but you can provide your own callback to override that. You call png_set_error_fn()
, passing your png_struct
, a void pointer, and two callbacks, one for errors and one for warnings. It will then call your function pointers with two arguments; the png_struct
, via which you can access your void pointer with png_get_error_ptr()
, and a char *
. Again, libpng handles the snprintf()
, and just passes a single char *
to the logging callback.
Upvotes: 2
Reputation: 16718
For example, in the configuration for your library, you could have a user-defined function pointer:
struct my_lib_config
{
void (* log) (const char * message);
};
With a sensible default:
void log_default (const char * message)
{
fprintf (stderr, "%s\n", message);
}
if (!config.log)
config.log = log_default;
With this your library would log to stderr by default, unless the application sets the log
function pointer to their own function.
Upvotes: 0