如何在 C 中将 linux syslog 映射到 printf

Posted

技术标签:

【中文标题】如何在 C 中将 linux syslog 映射到 printf【英文标题】:How can I map linux syslog to printf in C 【发布时间】:2012-08-27 14:57:04 【问题描述】:

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

注意:我确实想替换调用,只是重定向

所以我写了一些代码来做到这一点:

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

在我使用它的一个文件中效果很好。我把它移到了一个新文件中并得到了一个错误:

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参数将始终是字符串文字。 使用宏替换调用,而不是重定向。如果您只想重定向,您可能需要使用 RTLD_NEXT 查看 LD_PRELOAD。 【参考方案1】:

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

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

优先级是强制性的,但被宏扩展忽略。其余参数(强制格式加上可选的后续参数)在printf() 的参数列表中使用的__VA_ARGS__。无论格式字符串是常量(文字)还是变量,这都可以。


如果您想将输出标记为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, ...);

或:

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

实现是<stdarg.h> 中的宏加上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_printf(),但可能使用不同的名称)在您的代码中,并为您提供该函数的各种实现。这样做的唯一缺点是调用真正的syslog() 现在更难了——没有vsyslog() AFAIK。因此,对syslog() 的基本调用将通过使用vsnprintf() 格式化syslog_printf() 中的字符串来完成(或vasprintf(),如果您可以使用并且内存耗尽不是可能的问题),然后调用@987654344 @ 带有预格式化的字符串 (syslog(priority, "%s", buffer);)。当然,您还希望将优先级传递给代理函数以中继到syslog()

【讨论】:

非常适合我。谢谢。 但这与 OP 想要的不同,即在字符串前面加上他自己的消息... :-S 这是你提到的vsyslog()吗? @dtmland:大概就是我提到的vsyslog() 的Solaris 实现。它可能在其他平台上可用,也可能不可用。如果定义了_BSD_SOURCE,我的Linux 机器上的手册页(一个Ubuntu 14.04 衍生产品,但我不知道这是否是衍生产品的一部分)也提供vsyslog()。 Mac OS X (10.10.3) 也有,不需要_BSD_SOURCE。我不记得 2012 年的现状。如果 HP-UX 和 AIX 也有(并非不可能),那么可移植性风险很小——除了 Windows。 (Gnulib 标识“在某些平台上缺少:Minix 3.1.8、AIX 5.1、HP-UX 11、OSF/1 5.1、mingw、MSVC 9、BeOS”,但该列表不是所有这些都是最新的;AIX 至少在版本 7 上,...)。【参考方案2】:

fmt 字符串应该包含它自己的格式说明符,这些说明符用可变参数填充。首先,您可以简单地 ignore 实际参数并打印格式化字符串。你需要一个可变参数宏:

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

其次,如果您愿意冒巨大的风险并期望用户将格式化字符串作为文字而不是变量提供,您可以连接自己的字符串:

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

这适用于syslog(1, "Hello");,但不适用于syslog(1, str);

【讨论】:

【参考方案3】:

如果您不想依赖可变参数宏(C99 功能),您也可以这样做:

#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;

或类似的。

【讨论】:

以上是关于如何在 C 中将 linux syslog 映射到 printf的主要内容,如果未能解决你的问题,请参考以下文章

c#在linux上向本地syslog服务发送消息

如何将windows日志转成syslog格式

无法登录到 docker 容器 syslog 文件

如何在windows服务器中使用syslog功能

C Syslog API - 如何覆盖 _PATH_LOG

如何在Vim中将菜单键(“应用程序键”)映射到Escape键?