# 日志

# journalctl

$ journalctl         # 显示系统日志(默认显示全部日志)
            -f       # 跟踪日志的末尾并保持显示
            -u ssh   # 只显示某个 unit 的日志
            -b       # 只显示本次开机以来的日志
            -b -1    # 只显示上一次开机的日志(取值为+1 则表示正数第 1 次开机)
            --since "2019-08-12 17:00:00" # 只显示从某个时间点开始的日志
            --until "2019-08-14 00:00:00"
            --since "1 hour ago"
            --until "10 min ago"

            --disk-usage          # 显示日志占用的磁盘空间
            --vacuum-time=1months # 清理日志,只保留最近一段时间的
  • 其日志保存在 /var/log/journal//run/log/journal/ 目录下。

# systemd-analyze

$ systemd-analyze       # 显示系统启动的耗时
                blame   # 分别显示系统启动时各个进程的耗时

# logrotate

:一个 Linux 自带的命令行工具,用于定期轮询(rotate)日志文件,进行切割。

# 配置

logrotate 默认使用 /etc/logrotate.conf 作为主配置文件(用于保存 logrotate 本身的配置),还会导入 /etc/logrotate.d/ 目录下的其它配置文件(用于保存各个日志切割任务的配置)。

  • 这些配置文件不限制扩展名,采用 logrotate 自定的语法。
  • 用 # 声明单行注释,但必须独占一行。

例:编写一个配置文件 /etc/logrotate.d/nginx ,用于切割 Nginx 的日志文件

/usr/local/nginx/logs/*.log {   # 待切割的日志文件的绝对路径(如果有多个路径,则用空格分隔)
    su nginx nginx              # 切换到指定的用户、用户组来执行切割任务
    # create 644 nginx nginx    # 以指定权限创建新的日志文件

    # 判断是否切割:
    daily                       # 每天轮询一次日志文件(还可设置 weekly、monthly、yearly),如果需要切割,则切割生成一个归档日志
    rotate 5                    # 最多保留 5 个归档日志,超过该数量则删掉最旧的归档日志
    missingok                   # 忽略日志文件不存在时的报错
    notifempty                  # 如果日志文件为空,则不切割
    # size 10k                  # 如果日志文件低于指定大小,则不切割日志

    # 保存归档日志:
    # olddir old_logs           # 将归档日志放到指定目录下(默认放在源目录下),可以使用相对路径或绝对路径,但不能跨越磁盘
    dateext                     # 创建归档日志时,加上 dateformat 作为文件名后缀
    dateformat -%Y-%m-%d-%s     # 日期字符串的格式(文件名示例: error-2019-12-23-1577083161.log )
    extension .log              # 让归档日志采用这种扩展名(会负载 dateext 之后)
    # compress                  # 启用 gzip 压缩归档日志(扩展名为 .gz )
    # delaycompress             # 每次轮询生成的归档日志会等到下一次轮询时才压缩

    # copytruncate
    postrotate                  # 每次切割日志之后,执行 postrotate 与 endscript 之间的命令
      if [ -f /var/run/nginx.pid ]; then
          /bin/kill -USR1 `cat /var/run/nginx.pid`
      fi
    endscript
}

# 切割方式

假设让 logrotate 定期轮询一次应用程序 Nginx 的日志文件 error.log ,如果需要切割,则有两种切割方式:

  • 使用 postrotate 指令
    • 原理:将 error.log 重命名为归档日志 error-xx.log ,然后创建一个新的 error.log 文件,供程序继续写入日志。
    • 缺点:切割之后,日志文件的 inode 号会变化,因此需要将程序重启一次,重新根据文件路径打开该日志文件。
      • 不过 Nginx 支持发送 -USR1 信号,通知它在不重启的情况下重新打开日志文件。
  • 使用 copytruncate 指令
    • 原理:先创建一个归档日志 error-xx.log ,然后将 error.log 的内容拷贝到 error-xx.log 中,然后将 error.log 的长度截断为 0 ,供程序继续写入日志。
    • 优点:这样切割之后,日志文件的 inode 号没有改变,因此不必重启程序。
    • 缺点:在拷贝完成之后、截断完成之前,如果程序写入新的日志,则会丢失。
    • 程序调用 open() 函数打开日志文件时,应该采用 O_APPEND 追加模式。否则,当文件长度被截断为 0 时,程序会依然从之前的偏移量处继续写入数据,而偏移量之前的位置用空字节填充,导致文件变成稀疏文件。
  • 如果 copytruncate 和 postrotate 指令都不使用,则 logrotate 只会执行 mv error.log error-xx.log ,导致并没有切割日志文件。

# 启动

  • logrotate 安装之后不需要保持运行,它会被 crond 每天以 root 权限启动一次,启动之后会根据配置文件开始工作,切割日志。
    • 也可以添加 crontab 任务,每小时执行一次 logrotate 。
  • 可以手动执行一次 logrotate ,测试一下效果:
    logrotate <configfile>  # 启动 logrotate 并读取某个配置文件
              -d            # 开启调试模式,此时不会影响实际的日志文件
              -f            # 主动执行一次日志切割,不过 logrotate 可能认为此时不需要进行日志切割
    

# 示例

  1. 创建一个日志文件:
    [root@CentOS ~]# echo Hello > test.log
    [root@CentOS ~]#  ll
    -rw-r--r--. 1 root root    6 Jan 12 13:45 test.log
    
  2. 切割一次:
    [root@CentOS ~]#  logrotate /etc/logrotate.d/test -f
    [root@CentOS ~]#  ll
    -rw-r--r--. 1 root root    6 Jan 12 13:46 test-2020-01-12.log   # 切割成功,这里归档日志的日期字符串格式为 dateformat -%Y-%m-%d
    -rw-r--r--. 1 root root    0 Jan 12 13:46 test.log              # 原日志文件的内容变为空
    
  3. 再切割一次:
    [root@CentOS ~]# echo Hello > test.log        # 使原日志文件不为空
    [root@CentOS ~]#  logrotate /etc/logrotate.d/test -f
    [root@CentOS ~]#  ll
    -rw-r--r--. 1 root root    6 Jan 12 13:51 test-2020-01-12-2020-01-12.log
    -rw-r--r--. 1 root root    0 Jan 12 13:46 test-2020-01-12.log
    -rw-r--r--. 1 root root    6 Jan 12 13:51 test.log
    
    • test.log 的内容不为空,但并没有切割。因为存在一个名为 test-2020-01-12.log 的归档日志,导致 logrotate 认为已经切割过了。除非按 dateformat -%Y-%m-%d-%s 的格式生成归档日志。
    • 此时归档日志 test-2020-01-12.log 也被轮询、切割了。为了避免循环切割,建议在 logrotate 配置中取消使用文件名通配符,或者使用其它目录作为 olddir 。

# 日志清理方案

应用程序产生的日志文件需要定时清理,否则会占用越来越多的磁盘空间。主要分为以下几种情况:

  • 程序自己会每天切割日志,并删除多天以前的日志。这样已满足要求,不需要额外操作。
  • 程序自己会每天切割日志,但不会删除多天以前的日志。此时建议用 crontab 定期删除,如下:
    0 12 * * *     find /data/myproject/logs      -mtime +7 | xargs rm -f   # 删除超过 7 天未修改的日志文件
    
  • 程序只产生一个日志文件,不断写入。这样建议采用 logrotate 定期切割、自动删除的方案。