# 日志文件管理

> 本文以[Log File Management](http://swift.siphos.be/linux_sea/logfilemanagement.html)一文（案例操作系统为Gentoo Linux）为基础进行翻译整理，结合Red Hat Linux以及实践的经验记载。

## System Logger

`system logger`是一个服务，允许不同的软件工具通过一个统一标准的接口来记录程序事件。对于本地运行的程序，这些工具可以通过`/dev/log` socket。远程服务则可以童工网络发送事件。

### `/dev/log`

`/dev/log`是由系统日志程序（`system logger`）创建的可以被所有人写入的。每个工具都希望通过系统日志程序的socket来简单记录日志。这个系统日志程序在socket的`另一头`监听并处理程序发送过来的日志事件。

### 日志事件元信息（Log Event Meta Information）

当工具需要记录一个日志是，需要提供两个附加字段信息：

* 事件相关的类型（facility）

这里`facility`是指发送事件的程序类型。这样系统日志程序能够过滤消息，也可以将消息发送到不同的日志文件。例如，类型有`authpriv`（安全/认证消息），`cron`（定时执行的消息）和`kern`（内核消息）。完整的消息类型可以使用 `man 3 syslog`查询。

* 事件的重要程度

| 重要程度    | 说明                          |
| ------- | --------------------------- |
| DEBUG   | 调试目的的消息                     |
| INFO    | 信息类的消息                      |
| NOTICE  | 一个常规但是比信息类更重要的消息            |
| WARNING | 需要注意的警告信息                   |
| ERROR   | 错误消息需要进一步干预                 |
| CRIT    | 当关键错误发生时，常规操作可能被中断或者运行在降级模式 |
| ALERT   | 警报，需要立即采取行动                 |
| EMERG   | 系统不可用                       |

## 系统日志配置

这里的案例是`syslog-ng`日志程序，配置文件是 `/etc/syslog-ng/syslog-ng.conf` ，这个文件的配置非常直观，以下是案例配置

```bash
@version: 3.0
options {
  stats_freq(43200);
};

source src {
  unix-stream("/dev/log" max-connections(256));
  internal();
  file("/proc/kmsg");
};

destination messages { file("/var/log/messages"); };
destination cron { file("/var/log/cron.log"); };
destination auth { file("/var/log/auth.log"); };

filter f_messages { not facility(cron, auth, authpriv); };
filter f_cron { facility(cron); };
filter f_auth { facility(auth, authpriv); };
filter f_sys_app            { facility(local0,local1,local2,local3,local4); };

filter f_warnplus { level(warn, err, crit, emerg); };

log { source(src); filter(f_cron); filter(f_warnplus); destination(cron); };
log { source(src); filter(f_auth); destination(auth); };
log { source(src); filter(f_messages); destination(messages); };
log { source(src); filter(f_sys_app);         flags(final); };
log { source(src); destination(d_sys_loghost); };
```

* `source`部分定义消息的来源，然后`filter`部分定义了过滤器，以及`destination`定义消息存储的文件位置
* `filter`部分定义了消息的过滤方式，例如`f_warnplus`指接收`warn`以及以上重要级别的信息
* `source`部分定义了系统日志管理器从哪里接收消息，这里`/dev/log` socket表示接收本地通过socket传送的消息，另外还接收内核消息接口`kmsg`和它自己的内部日志

## logger工具

`logger`是一个shell工具，可以提供脚本方式向系统日志管理程序发送日志事件进行记录，以下是一个简单案例

```bash
ping 192.168.0.1 | logger -it logger_test -p local3.notice &
```

注意，上述命令会将消息记录到 `/var/log/messages`中类似如下

```bash
Apr 27 00:39:27 example-server logger_test[27478]: PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data.
Apr 27 00:39:27 example-server logger_test[27478]: 64 bytes from 192.168.0.1: icmp_seq=1 ttl=255 time=1.45 ms
Apr 27 00:39:28 example-server logger_test[27478]: 64 bytes from 192.168.0.1: icmp_seq=2 ttl=255 time=1.03 ms
Apr 27 00:39:29 example-server logger_test[27478]: 64 bytes from 192.168.0.1: icmp_seq=3 ttl=255 time=1.60 ms
```

详细使用方法参考 `man logger`

## `/proc/kmsg`和`/dev/log`

内核日志（使用`printk()`功能）发送消息给内核空间的ring buffer。这些消息有两种方式可以被用户空间应用程序访问：通过`/proc/kmsg`文件（需要`/proc`已经挂载），以及通过`sys_syslog`系统调用。

有两个主要的应用程序来读取（某种程度是控制）内核的ring buffer：`dmesg`和`klogd`。前者是用户打印出ring buffer中内容，后者是一个daemon服务，用来从`/proc/kmsg`中读取消息（如果`/proc`没有挂载就调用`sys_syslog`）并发送这些消息给`syslogd`或控制台。

在用户空间，有一个`syslogd`是一个监听在Unix domain socket的服务，并且可以选择让它监听在`514`的UDP端口。这个日志服务也可以从`klogd`来接收消息（`syslogd`不使用`/proc/kmsg`），然后将这些消息写入日志文件，或者命名管道，或者发送到远程主机（通过`syslog`协议，在`514` UDP端口）。可以通过`/etc/syslog.conf`配置。

用户空间应用程序通常使用`libc`的功能`syslog`来记录消息日志。`libc`发送这些消息到Unix domain socket `/dev/log` （可以被`syslogd`读取）。不过，如果应用程序是使用`chroot`运行则消息会写入到其他socket，例如`/var/named/dev/log`。此时，要配置`syslogd`来监听这些特定位于`/dev/log`socket。

`syslog`协议只是一个电报协议（datagram protocol），没有办法阻止一个应用程序发送syslog数据给任何Unix domain socket（只要它被允许打开socket），这样就完全绕开了`libc`的`syslog`功能。如果这些数据是符合`syslogd`格式的，就会使用这些数据来记录日志。

新型的`rsyslog`和`syslog-ng`已经取代了传统的`syslogd`，并且提供了通过加密的TCP连接将消息发送给远程主机，以及提供高可靠的时间戳。此外，`systemd`也具备自己的日志机制，

## 使用Logrotate维护日志

很多工具并没有提供日志轮转功能或日志清理功能，此时可以使用`logrotate`来管理日志。这个工具通过cron定时触发，并通过独立分离的配置文件来管理配置

这里的案例是 `/etc/logrotate.d/syslog-ng`

```bash
/var/log/messages /var/log/secure /var/log/maillog /var/log/spooler /var/log/boot.log /var/log/cron /var/log/kern{
        rotate 6
        monthly
        missingok
        sharedscripts
        postrotate
                /etc/rc.d/init.d/syslog-ng reload 2>/dev/null || true
        endscript
}
```

* `rotate 6`和`monthly`表示以每月为基础轮转6次（也就是可以保留7个月历史记录），旧日志文件会添加一个时间戳到文件名
* `missingok`表示如果日志文件不存在也是ok的
* `sharescripts`表示上面这些需要处理的日志文件都完成轮转之后在执行`postrotate`部分工作
* `postrotate`表示轮转结束以后执行的命令，也就是`syslog-ng`重新加载配置和日志文件

**logrotate配置参数**

| 参数                      | 功能                                                     |
| ----------------------- | ------------------------------------------------------ |
| compress                | 通过gzip 压缩转储以后的日志                                       |
| nocompress              | 不需要压缩时，用这个参数                                           |
| copytruncate            | 用于还在打开中的日志文件，把当前日志备份并截断                                |
| nocopytruncate          | 备份日志文件但是不截断                                            |
| create mode owner group | 转储文件，使用指定的文件模式创建新的日志文件                                 |
| nocreate                | 不建立新的日志文件                                              |
| delaycompress           | 和 compress 一起使用时，转储的日志文件到下一次转储时才压缩                     |
| nodelaycompress         | 覆盖 delaycompress 选项，转储同时压缩。                            |
| errors address          | 专储时的错误信息发送到指定的Email 地址                                 |
| ifempty                 | 即使是空文件也转储，这个是 logrotate 的缺省选项。                         |
| notifempty              | 如果是空文件的话，不转储                                           |
| mail address            | 把转储的日志文件发送到指定的E-mail 地址                                |
| nomail                  | 转储时不发送日志文件                                             |
| olddir directory        | 转储后的日志文件放入指定的目录，必须和当前日志文件在同一个文件系统                      |
| noolddir                | 转储后的日志文件和当前日志文件放在同一个目录下                                |
| prerotate/endscript     | 在转储以前需要执行的命令可以放入这个对，这两个关键字必须单独成行                       |
| postrotate/endscript    | 在转储以后需要执行的命令可以放入这个对，这两个关键字必须单独成行                       |
| daily                   | 指定转储周期为每天                                              |
| weekly                  | 指定转储周期为每周                                              |
| monthly                 | 指定转储周期为每月                                              |
| rotate count            | 指定日志文件删除之前转储的次数，0 指没有备份，5 指保留5 个备份                     |
| tabootext \[+] list     | 不转储指定扩展名的文件，缺省的扩展名是：.rpm-orig .rpmsave                 |
| size size               | 当日志文件到达指定的大小时才转储，可以指定bytes(缺省)以及KB(sizek)或者MB (sizem). |

在 `/etc/cron.daily/logrotate`配置了每天由cron程序调用一次logrotate

## 简单删除旧日志的方法

如果不想使用`logrotate`来管理日志，而是仅仅想简单地删除超过一个月的旧日志，可以配置一个cron定时任务。例如 `/etc/cron.weekly/elog-cleanup`

```bash
#!/bin/sh

find /var/log/portage/elog -type f -mtime +30 -exec rm '{}' \;
```

## 参考

* [Log File Management](http://swift.siphos.be/linux_sea/logfilemanagement.html)
* [Understand logging in Linux](http://unix.stackexchange.com/questions/205883/understand-logging-in-linux)
* [linux 日志logger](http://blog.csdn.net/hunanchenxingyu/article/details/21413245)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://huataihuang.gitbook.io/cloud-atlas-draft/os/linux/log/log_file_management.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
