Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I map linux syslog to printf in C

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?

like image 667
Mike Avatar asked Aug 27 '12 14:08

Mike


3 Answers

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

like image 175
Jonathan Leffler Avatar answered Oct 21 '22 14:10

Jonathan Leffler


The fmt string is expected to contain its own formatting specifiers, which are filled in with the variadic arguments. First off, you could simply ignore the actual arguments and just print the formatting string. You need a variadic macro:

#define syslog(level, fmt, ...) printf("SYSLOG: %s\n", fmt)

Second, if you're willing to take a gigantic risk and expect the user to provide the formatting string as a literal rather than a variable, you can concatenate your own string:

#define syslog(level, fmt, ...) printf("SYSLOG[%d] " fmt, level, ##__VA_ARGS__)

This will work for syslog(1, "Hello");, but not for syslog(1, str);.

like image 31
Kerrek SB Avatar answered Oct 21 '22 15:10

Kerrek SB


If you don't want to depend on variadic macros (C99 feature), you could also do this:

#define syslog my_syslog

static inline int my_syslog(int prio, const char *fmt, ...)
{
    int r;
    va_list ap;
    va_start(ap, fmt);
    r = vprintf(fmt, ap);
    va_end(ap);
    return r;
}

or similar.

like image 34
R.. GitHub STOP HELPING ICE Avatar answered Oct 21 '22 14:10

R.. GitHub STOP HELPING ICE