5

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?

Mike
  • 47,263
  • 29
  • 113
  • 177
  • A macro won't help *unless* you can guarantee that the `fmt` argument will *always* be a string literal. – Kerrek SB Aug 27 '12 at 15:01
  • 1
    Using a macro *is* replacing the calls, and is not redirecting. If you just want to redirect, you probably want to look into LD_PRELOAD using RTLD_NEXT. – William Pursell Aug 27 '12 at 15:16

3 Answers3

11

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

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Worked perfectly for me. Thanks. – Mike Aug 27 '12 at 15:13
  • But this is different from what the OP wanted, which is to prefix the string with his own message... :-S – Kerrek SB Aug 27 '12 at 15:15
  • 2
    Would this be the [vsyslog()](http://docs.oracle.com/cd/E19683-01/816-0213/6m6ne38fl/index.html) you were mentioning? – dtmland Apr 30 '15 at 22:50
  • @dtmland: That would be, presumably, the Solaris implementation of the `vsyslog()` that I mentioned. It may or may not be available on other platforms. The manual page on my Linux box (an Ubuntu 14.04 derivative, but I don't know whether this is part of the derivative or not) supplies `vsyslog()` too if `_BSD_SOURCE` is defined. Mac OS X (10.10.3) has it too, without requiring `_BSD_SOURCE`. I don't recall what the status quo was in 2012. If HP-UX and AIX also have it (not improbable), then the portability risk is minimal -- Windows apart. – Jonathan Leffler Apr 30 '15 at 23:37
  • ([Gnulib](https://www.gnu.org/software/gnulib/manual/html_node/vsyslog.html) identifies "missing on some platforms: Minix 3.1.8, AIX 5.1, HP-UX 11, OSF/1 5.1, mingw, MSVC 9, BeOS", but that list isn't all that up to date; AIX is on at least version 7, ...). – Jonathan Leffler Apr 30 '15 at 23:37
1

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

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
1

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.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711