Reputation: 3747
I have a function log_info
(copied from printf
's implementation) that accepts variable no. of arguments and passes it to vprintf:
int log_info(const char *format, ...) {
va_list arg;
int done;
va_start (arg, format);
done = vfprintf (stdout, format, arg);
va_end (arg);
return done;
}
I want to prepend and append strings to these arguments. So if a user calls the above function this way:
log_info("at iteration %d, float value is %f", i, f);
instead of printing
at iteration 4, float value is 102.34
I want to print
[INFO] [at iteration 4, float value is 102.34] [timestamp: xxxx]
I could do it in 3 separate steps
fprintf(stdout, "[INFO] [");
vprintf(stdout, format, arg);
fprintf(stdout, "] [timestamp:%f]", ts);
But the program is multi-threaded, and hence I want all data to be written in a single call to vprintf (vprintf being thread-safe).
The other option is to lock a mutex and then write it in 3 steps as shown above, but if appending strings to the args is not too complex, I would like to try that.
Edit: Performance overhead due to use of mutex is not really an issue, but I dont want to use one unless necessary.
Thanks in advance
Upvotes: 1
Views: 1849
Reputation: 593
You want to add new fields to the output, so just construct a new format string.
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
int loginfo(const char *format, ...) {
int ret;
int len;
va_list ap;
char *new_fmt;
char *timestamp;
const char fmt_template[] = "[INFO] [%s] [timestamp: %s]";
/* Grab the timestamp now, since if we call it twice, it may change in length */
timestamp = get_time_string();
/* Calculate length for the augmented format string and allocate. */
len = snprintf(NULL, 0, fmt_template, format, timestamp);
new_fmt = malloc(len + 1);
/* Construct the new format string */
snprintf(new_fmt, len + 1, fmt_template, format, timestamp);
/* Print as before, using new format string */
va_start (ap, format);
ret = vfprintf (stdout, new_fmt, ap);
va_end (ap);
free(new_fmt);
return ret;
}
Upvotes: 5
Reputation: 2216
One approach is to use vsnprintf to write the original message to a (dynamically allocated) string. Then use fprintf to output the metadata plus original message:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
...
static const char * get_timestamp_str()
{
...
}
static void Log(const char *format, ...)
{
va_list arg;
int len;
char * orig_msg;
/* Compute length of original message */
va_start(arg, format);
len = vsnprintf(NULL, 0, format, arg);
va_end(arg);
/* Allocate space for original message */
orig_msg = (char *)calloc(len+1, sizeof(char));
/* Write original message to string */
va_start(arg, format);
vsnprintf(orig_msg, len+1, format, arg);
va_end(arg);
/* Write metadata plus original message to stderr */
fprintf(stderr, "[INFO] [timestamp %s] %s\n", get_timestamp_str(), orig_msg);
free(orig_msg);
}
Upvotes: 3