How can I map linux syslog to printf in C

Mike picture Mike · Aug 27, 2012 · Viewed 7.4k times · Source

I have a linux application running on my desk top, and I wanted to redirect the syslog() calls to printf() calls.

Note: I do not want to replace the calls, just redirect

So I wrote some code to do this:

#ifndef EMBED
#define syslog(level, stuff) printf("SYSLOG: %s\n", stuff)
#endif

Works great in the one file where I was using it. I moved this to a new file and got an error:

error: macro "syslog" passed 3 arguments, but takes just 2

I know the error is because the calls in the new file are mixed, some are using 2 arguments to syslog, some are using 3. I also know I need to somehow redirect this via variable argument lists, but how exactly do I do this? I haven't gotten it working yet...

As I understand it, syslog() and printf() should be:

void syslog(int priority, const char *format, ...)
int printf(const char *format, ...)

So I tried:

#define ERR 3
#ifndef EMBED         // This is not defined in my env, btw
#define syslog(pri, fmt, ...) printf(fmt, ...)
#endif
...
void main() {
...
syslog(ERR, "test");

but that gives the error:

error: expected expression before ‘...’ token

Suggestions on how this macro should look/be used?

Answer

Jonathan Leffler picture Jonathan Leffler · Aug 27, 2012

GCC has an extension in this area, but the most portable way of handling it is:

#define syslog(priority, ...)    printf(__VA_ARGS__)

The priority is mandatory, but is ignored by the macro expansion. The rest of the arguments (the mandatory format plus optional following arguments) are in the __VA_ARGS__ used in the argument list to printf(). This will be OK whether the format string is a constant (literal) or a variable.


If you want to tag the output as being for the surrogate of syslog(), I'd call a function other than printf() to do the job:

#define syslog(priority, ...) syslog_print(__VA_ARGS__)

or even

#define syslog(priority, ...) syslog_print(__FILE__, __LINE__, __func__, __VA_ARGS__)

These would be declared as:

extern void syslog_printf(const char *fmt, ...);

or:

extern void syslog_printf(const char *file, int line, const char *func,
                          const char *fmt, ...);

The implementation is a straight-forward application of the macros in <stdarg.h> plus the v*printf() functions:

void syslog_printf(const char *file, int line, const char *func,
                   const char *fmt, ...)
{
    va_list args;
    printf("SYSLOG:%s:%d:%s: ", file, line, func);
    va_start(args, fmt);
    vprintf(fmt, args);
    va_end(args);
}

You can add timestamps and whatever else you like; you can also rather easily arrange for the output to go to a file instead of standard output; you can also arrange for it to be turned on or off inside the function. So, on average, I would replace syslog() with a surrogate that allows you to tune your codes use of the facilities.

In fact, since you've uncovered a requirement to change the behaviour of logging, I would suggest that instead of using syslog() directly in your code, you should use your own function (for example, the syslog_printf(), but possibly under a different name) in your code, and have various implementations of that function available to you. The only downside to doing so is that calling the real syslog() is now harder — there isn't a vsyslog() AFAIK. So, the basic call to syslog() would be done by formatting the string in syslog_printf() using vsnprintf() (or vasprintf() if that's available to you and memory exhaustion isn't a likely problem), and then calling syslog() with the preformatted string (syslog(priority, "%s", buffer);). Of course, you'd also want the priority passed to the surrogate function for relay to syslog().