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?
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()
.