# 日志

# 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)日志文件。

  • 官方文档 (opens new window)
  • 假设一个程序不断生成日志到一个日志文件,导致文件体积越来越大,不方便查看和搜索,甚至可能占满磁盘。
    • 为了解决该问题,通常将历史每天的日志分别保存到一个归档(archive)文件,与新产生的日志位于不同文件。该过程称为日志轮换、日志切割。

# 配置

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 字符串作为文件名后缀。归档文件名示例: error-2019-12-23-1577083161.log
        dateformat -%Y-%m-%d-%s     # 日期字符串的格式
        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 变化,因此需要重启一次程序,让程序重新根据文件路径打开该日志文件,否则程序还会将日志写入旧 inode 文件。
      • Nginx 支持发送 -USR1 信号,通知程序在不重启的情况下重新打开日志文件。
  • 使用 copytruncate 指令
    • 原理:先创建一个归档文件 error-xx.log ,然后将 error.log 的内容拷贝到 error-xx.log 中,然后将 error.log 文件的长度截断为 0 ,供程序继续写入日志。
    • 优点:轮换之后,日志文件的 inode 不变,因此不必重启程序。
    • 缺点:在拷贝完成之后、截断之前,如果程序写入新的日志,则会因为截断而丢失。
      • 程序调用 open() 函数打开日志文件时,应该采用 O_APPEND 追加模式。否则,当文件长度被截断为 0 时,程序会依然从之前的偏移量处继续写入数据,而偏移量之前的位置用空字节填充,导致文件变成稀疏文件。

# 启动

  • 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 。

# 日志清理方案

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

  • 程序只产生一个日志文件。此时可采用 logrotate 定期轮换、自动删除。
  • 程序只产生一个日志文件,且不需要保存。此时可添加 crontab 任务,定期截断:
    0 1 * * *   truncate -s 1G /data/*.log
    
  • 程序自己会每天轮换日志,并删除多天以前的日志。此时已满足要求,不需要额外操作。
  • 程序自己会每天轮换日志,但不会删除多天以前的日志。此时可添加 crontab 任务,定期删除:
    0 1 * * *   find /data/ -name '*.log' -mtime +7 | xargs rm -f   # 删除超过 7 天未修改的日志文件