系统日志守护进程,syslogd 和 klog
自 BSD 早期以来(Linux 当然也属于这一类),大多数类 UNIX 系统都为应用程序提供了一个 API,用于向系统发送日志消息,系统操作员可以集中处理这些消息。 在此机制创建之前,每个应用程序程序都会以自己的方式处理日志消息。 有些会写入 STDERR,有些会写入文件,有些会写入管道,有些会提供所有这些选项或更多。
随着系统上应用程序的数量和复杂性增长,系统管理员的工作复杂性也随之增加。 应用程序及其消息对于某些受众的重要性差异很大。 如果许多应用程序被认为是“关键”的,并且其状态是系统管理员的责任,那么他不想搜索以找出每个关键应用程序如何记录其状态。 这就是 syslogd 的用武之地。
BSD 在标准库中添加了一个用于日志记录的 API。 Linux 也提供了它。 此 API 由三个函数调用组成
#include <syslog.h> void openlog( char *ident, int option, int facility) void syslog ( int priority, char *format, ...) void closelog( void )
希望使用 syslogd 进行日志记录的应用程序会使用这些调用。 关于此 API 使用的简要介绍可以在侧边栏“在应用程序中使用 syslog API”中找到。 需要了解的关键一点是,来自此 API 的所有消息至少都具有“facility”(设施)和“priority”(优先级)。
设施包括 LOG_AUTHPRIV、LOG_CRON、LOG_KERN、LOG_DAEMON 等等。 这些用于标识来源的“系统”。 请注意,它不是来源的“程序”。 例如,许多不同的程序组成了 UUCP,但它们都记录为 LOG_UUCP。 程序名称可以是日志消息的组成部分,但这与设施无关。 有些程序会记录为多个设施。 例如,telnetd 可能会将失败的登录记录为 LOG_AUTHPRIV,但可能会将其他消息记录为 LOG_DAEMON。“优先级”指定消息应得的“严重级别”或“关注级别”。 我们将在本文中更详细地讨论这些概念。
我们在此处的主要重点是处理通过此 API 发送的消息的工具。 虽然 syslogd 最初是为 4.2BSD 开发的,但我们将介绍今天大多数 Linux 发行版附带的版本,特别是 syslogd 版本 1.3-3。 syslogd 实用程序通常在启动时读取配置文件,以确定如何处理消息。 此文件(通常是 /etc/syslog.conf)告诉 syslogd 如何处理消息。 本文档的其余部分将描述如何使用 syslog.conf 来自定义系统上的日志记录。
syslog.conf 文件遵循或多或少普遍存在的 UNIX 约定,使用井号 (#) 作为注释字符。 在接下来的讨论中,我们将使用列表 1 中的示例 syslog.conf 文件。 这是我的 Red Hat 6.1 笔记本电脑上的“开箱即用” syslog.conf。 我个人使用其他发行版,主要是 Debian 和 SuSE,但 Red Hat 似乎是最流行的。 让我们了解一下这个文件在做什么。
syslog.conf 中的“规则”是单行,由两部分组成。 第一部分是“选择器”,它指定规则要作用的消息集。 第二部分是操作,它指定如何处理与选择器匹配的消息。
选择器进一步分为“facility”(设施)和“priority”(优先级)。 是的,这些与上面在 syslog API 简要描述中提到的术语相匹配。 设施和级别具有数值,您可以在 syslog.conf 文件中使用它们,但强烈建议您不要这样做。 支持符号值,如果 syslog API 发生更改,数值可能会更改,而人们会期望符号名称与任何此类更改保持一致。 因此,为了安全起见,请使用符号名称。
符号设施名称为 auth、authpriv、cron、daemon、kern、lpr、mail、mark、news、security(与 auth 相同)、syslog、user、uucp 以及 local0 到 local7。 security 关键字已被弃用,建议使用 auth 关键字。 mark 关键字是内部关键字,不应由应用程序使用; 可以将 syslogd 程序设置为定期生成 mark,这提供了一种判断您是否没有收到消息的方法,因为没有生成任何消息或者 syslogd 已死。 其余的对应于您的 Linux 机器上的主要子系统。
优先级关键字为 debug、info、notice、warning、warn(与 warning 相同)、err、error(与 err 相同)、crit、alert、emerg 和 panic(与 emerg 相同)。 关键字 error、warn 和 panic 已被弃用,不应再使用。
选择器由 facility(设施)和 priority(优先级)组成,两者之间用句点 (.) 字符分隔。 因此,mail.crit 将选择来自 mail 设施的所有 critical(严重)消息。
BSD syslog 系统的默认行为是指定或更高优先级的所有消息都由操作处理。 Linux syslogd 默认情况下也这样做。 但是,它确实有许多扩展。
您可以使用星号 (*) 字符来指示所有 facility(设施)或所有 priority(优先级)(取决于它出现在句点之前还是之后)。 因此,示例中的 authpriv.* 行会将来自 authentication(身份验证)设施的所有消息(无论优先级如何)发送到 /var/log/secure。 您可以使用特殊的优先级 none 来指示 不 对来自给定 facility 规则的消息执行操作。
您可以在单个规则中指定具有相同优先级的多个 facility(设施),方法是在句点之前列出以逗号 (,) 分隔的 facility。 因此,uucp,news.crit 行会将来自 uucp 和 news 设施的所有 critical(严重)及以上优先级消息发送到 /var/log/spooler。
您可以通过列出以分号字符分隔的选择器来为单个操作指定多个选择器。 每个后续选择器可能会覆盖前一个选择器。 因此,*.info;mail.none;news.none;authpriv.none 规则会将来自所有 facility(设施)的所有高于 info 优先级的消息(由于 *)发送到 /var/log/messages,但 排除来自 mail、news 或 authentication 设施的消息(由于 none 关键字,并且规则按从左到右的顺序应用)。
一条消息可能适用多个规则! 重要的是要理解,消息将发送到 所有 具有匹配选择器的操作。 这不是说消息一旦匹配就被吞噬了。 这意味着如果一条消息与多个选择器匹配,您可以将该消息存储到多个操作。
还有更多的优先级选择扩展。 首先,请记住,默认是选择指定优先级或更高优先级的消息。 您也可以使用感叹号反转含义。 因此,例如,诸如
*.!err /var/log/routine
之类的规则会将 不 处于 err 或更高级别的所有消息发送到 /var/log/routine(显然,这是一个用于“例行”消息的文件)。
您还可以使用等号 (=) 将选择限制为精确优先级,而不是给定的优先级或更高优先级。 因此,我们示例中的 news.=crit 规则将仅将来自 news 设施的 critical(严重)消息发送到 /var/log/news.crit。
此时,您可能对这些各种优先级到底意味着什么感到有点模糊。 让我们通过查看“优先级”表来阐明这个问题。
在软件设计中,一个经典问题是试图找到一种经验方法来区分您报告为 crit 与 alert 的条件。 实际上,有时甚至更难决定何时应使用 notice 与 warn。 您不会在软件包之间找到关于消息级别落在何处的完全一致意见。 一个困难在于试图决定谁将阅读日志。 对于业务部门来说,emerg 可能只是网络管理员的 warn。
对于这个问题,没有一个好的答案。 至少,通过创建一种处理程序消息的统一方法,我们避免了不同报告系统的激增,并且随着时间的推移出现了一些约定。 因为我们能够将消息匹配到多个操作,所以我们可以将消息输出到目标受众。 例如,我们可以将所有 auth 消息报告给安全部门的主目录,但系统管理员可能会选择仅接收“crit”或更高级别的消息。 syslogd 让我们能够做到这一点。
使用“开箱即用”的 Red Hat syslog.conf 文件有一些缺点。 值得注意的是,所有“操作”基本上都是写入本地文件。 syslogd 守护进程可以做更多的事情。 接下来让我们看一下操作。
操作可以将消息发送到以下任何目标
常规文件:这是您在我们的示例中看到的。 这只是消息附加到的文件的名称。
命名管道:命名管道或 FIFO(先进先出)是 Linux 和许多其他操作系统支持的一种简单的进程间通信形式。 您可以使用 mkfifo 命令创建命名管道; FIFO 出现在文件系统中。 通过在 FIFO 名称前面放置管道字符 (|),您可以告诉 syslogd 它正在写入 FIFO 而不是文件。 查看 mkfifo 的手册页(包括命令和系统调用)以及“fifo”的手册页,其中描述了特殊文件。 您可以使用普通的文件系统调用来读取和写入 FIFO。 FIFO 编程的描述超出了我们的范围,但我强烈推荐 W. Richard Stevens 的优秀著作 UNIX 网络编程(Prentice-Hall 出版)。
终端或控制台:如果您指定一个 tty 设备(例如 /dev/console),syslogd 会足够智能地识别出它是一个设备而不是文件,并相应地处理它。 如果您有一个哑终端,这会很有趣——您可以将所有消息发送到 /dev/ttyS1(例如),并在控制台上工作时在终端屏幕上获取所有消息。 这是 1970 年代最先进的技术——我喜欢玻璃电传打字机!
远程机器:现在 这 才是真正的力量。 假设您的网络上有许多 Linux 机器。 您想登录到每台机器以检查其日志中是否存在某些条件吗? 当然你不想,你也不必这样做。 可选地,syslogd 也在网络上监听消息。 只需放置一个 at 符号 (@),后跟主机名
*.crit @sol.n0zes.ampr.org
最后一个操作会将来自所有 facility(设施)的所有 critical(严重)及以上消息发送到 sol.n0zes.ampr.org,然后 sol.n0zes.ampr.org 将应用自己的 syslog.conf 文件来保存它们。 Syslogd 不会将从网络接收的消息转发到另一台主机:换句话说,您只能跳一次。 这可以在调用 syslogd 时使用开关覆盖。 这似乎是合理的,因为即使是循环消息路由的可能性也足以吓跑任何网络管理员。
此功能对于集中式日志记录和日志扫描以查找安全违规等情况具有明显的优势。 它也有明显的缺陷。 当您的网络关闭时,很难维护完整的日志。 利用您可以将消息路由到多个操作的事实,确保每条消息在发送到远程记录器之前都找到通往文件的路径。
Linux 的新用户可能没有意识到的一项功能是控制台消息传递。 由于 talk 和 irc 以及其他用户界面更简洁的交互式“聊天”机制,此功能已不再常用。 但是,您可以使用 “write” 命令向登录到您的系统的任何用户发送文本消息。 由于以下几个原因,这是一种不受欢迎的功能。 首先,在当今的窗口环境中,用户可能有很多活动的“终端”,很难知道要写入哪个终端。 其次,如果他们正处于某些密集的全屏活动中(例如使用 vi 编辑大文件),并且您向他们发送大量文本,这会使他们的编辑器感到困惑并搞砸他们的屏幕,他们不会非常喜欢您。 我见过的许多 Linux 默认情况下都将其用户的消息传递设置为关闭。 此功能使用相同的写入用户控制台的能力,将消息直接发送到他们的屏幕。 只需放置一个逗号分隔的用户名列表作为操作。 将其用于真正重要的内容。 您可能会打开此功能尝试一下,但我敢打赌您很快就会再次将其关闭。
有一种类似的方法,称为 wall 或 write-to-all(写入所有人)。 这使您可以向登录到系统的每个用户发送文本消息。 无论您是否选择接受消息,超级用户都可以执行此操作。 这就是 shutdown 发送其警告消息的方式。 您可以通过指定星号 (*) 作为操作,让 syslogd 通过此机制向所有人发送消息。 如果您必须使用此功能,请将其用于最可怕的消息。 这应该用于警告即将发生的崩溃——任何程度的降低都可能矫枉过正。
此时,您可能会问 klogd 与这一切有什么关系。 答案很简单。 内核无法调用 syslog API。 这有很多原因。 核心原因,也是最容易理解的原因,是 Linux 实际上提供了两个完全独立的 API。 较为常见的是用户空间应用程序使用的“标准库”; 这是使用 syslog 的库。 另一个 API 通常不被应用程序使用:这是作为内核一部分运行的内核 API 代码。 此代码需要类似于应用程序编程接口提供的服务,但由于许多技术(和一些美观)原因,内核代码无法使用应用程序的 API。 因此,内核有其自己完全独立的消息生成机制。 klog 守护进程 klogd 是一个应用程序进程,它将内核消息传递系统与 syslogd 联系起来。 实际上,它也可以将内核消息分派到文件,但大多数配置使用 klogd 默认设置,即准备内核消息,并本质上通过 syslog 重新提交它们。
如果您想深入研究,klogd 还有很多内容,但就本文而言,只需知道 klogd 将内核消息馈送到 syslogd,在那里它们似乎来自 kern 设施。
syslogd 提供了一种强大而简单的机制,用于以高度可配置的方式管理来自多个应用程序的消息。 它“解复用”消息流的能力使使用 syslog API 成为应用程序开发人员有吸引力的选择,我鼓励您考虑在自己的程序中使用该 API。
