如何将linux系统日志映射到C中的printf

如何将linux系统日志映射到C中的printf,c,linux,macros,syslog,C,Linux,Macros,Syslog,我的桌面上运行着一个linux应用程序,我想将syslog()调用重定向到printf()调用 注意:我不想替换呼叫,只需重定向即可 所以我写了一些代码来实现这一点: #ifndef EMBED #define syslog(level, stuff) printf("SYSLOG: %s\n", stuff) #endif #define syslog my_syslog static inline int my_syslog(int prio, const char *fmt, ...)

我的桌面上运行着一个linux应用程序,我想将syslog()调用重定向到printf()调用

注意:我不想替换呼叫,只需重定向即可

所以我写了一些代码来实现这一点:

#ifndef EMBED
#define syslog(level, stuff) printf("SYSLOG: %s\n", stuff)
#endif
#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;
}
在我使用它的一个文件中效果很好。我将此文件移动到一个新文件,但出现错误:

error: macro "syslog" passed 3 arguments, but takes just 2
我知道这个错误是因为新文件中的调用是混合的,一些调用使用了2个syslog参数,一些调用使用了3个。我也知道我需要通过变量参数列表以某种方式重定向它,但我到底要怎么做呢?我还没有让它工作

据我所知,syslog()和printf()应该是:

void syslog(int priority, const char *format, ...)
int printf(const char *format, ...)
所以我试着:

#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");
但这就产生了错误:

error: expected expression before ‘...’ token

关于该宏的外观/使用方法的建议?

预计
fmt
字符串将包含其自己的格式说明符,这些格式说明符由可变参数填充。首先,您可以忽略实际参数,只打印格式化字符串。您需要一个可变宏:

#define syslog(level, fmt, ...) printf("SYSLOG: %s\n", fmt)
其次,如果您愿意冒巨大的风险,并希望用户将格式化字符串作为文本而不是变量提供,则可以连接您自己的字符串:

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

这将适用于
syslog(1,“Hello”),但不适用于
系统日志(1,str)

GCC在这方面有一个扩展,但处理它的最方便的方式是:

#define syslog(priority, ...)    printf(__VA_ARGS__)
优先级是必需的,但被宏展开忽略。其余参数(强制格式加上可选的以下参数)位于
参数列表中使用的
\uu VA\u ARGS\uu
中,以
printf()
。无论格式字符串是常量(文字)还是变量,都可以


如果要将输出标记为作为
syslog()
的代理,我将调用
printf()
以外的函数来执行此任务:

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

#define syslog(priority, ...) syslog_print(__FILE__, __LINE__, __func__, __VA_ARGS__)
这些将被宣布为:

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

该实现是对
中的宏以及
v*printf()
函数的直接应用:

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);
}
你可以添加时间戳和任何你喜欢的东西;您还可以很容易地安排输出到文件,而不是标准输出;您还可以在函数内部设置它的打开或关闭。所以,平均而言,我会用一个代理来替换
syslog()
,该代理允许您调整代码对设施的使用


事实上,由于您发现了更改日志行为的要求,我建议您不要在代码中直接使用
syslog()
,而应该在代码中使用自己的函数(例如,
syslog\u printf()
,但可能使用不同的名称),并为您提供该功能的各种实现。这样做的唯一缺点是调用真正的
syslog()
现在更难了-没有
vsyslog()
AFAIK。因此,对
syslog()
的基本调用可以通过使用
vsnprintf()
(或者
vasprintf()
,如果您可以使用并且内存耗尽不太可能出现问题的话)在
syslog\u printf()
中格式化字符串,然后使用预先格式化的字符串(
syslog(优先级,“%s”)调用
syslog()
,缓冲区);
)。当然,您还希望将优先级传递给代理函数,以便中继到
syslog()

如果您不想依赖可变宏(C99功能),也可以执行以下操作:

#ifndef EMBED
#define syslog(level, stuff) printf("SYSLOG: %s\n", stuff)
#endif
#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;
}

或类似的。

除非您能保证
fmt
参数始终是字符串文字,否则宏不会有帮助。使用宏是替换调用,而不是重定向。如果您只是想重定向,您可能想使用RTLD_NEXT查看LD_预加载。这对我来说非常有效。谢谢。但是这与OP想要的不同,OP想要在字符串前面加上他自己的消息…:-这会是您提到的吗?@dtmland:这可能是我提到的
vsyslog()
的Solaris实现。它可能在其他平台上可用,也可能不可用。如果定义了
\u BSD\u SOURCE
,我的Linux机器上的手册页(Ubuntu 14.04的衍生版本,但我不知道这是否是衍生版本的一部分)也会提供
vsyslog()
。MacOSX(10.10.3)也有它,不需要
\uBSD\uSource
。我不记得2012年的现状是什么。如果HP-UX和AIX也有它(并非不可能),那么可移植性风险是最小的——除了Windows之外(标识“在某些平台上缺失:Minix 3.1.8、AIX 5.1、HP-UX 11、OSF/1 5.1、mingw、MSVC 9、BeOS”,但该列表并不是最新的;AIX至少在版本7上,…)。