关于Journal Log

作者: JesseZhou | 来源:发表于2020-03-22 11:01 被阅读0次

    简介

    Journal是systemd 为自己提供的日志系统。使用systemd日志,无须额外提供日志服务(syslog)。读取日志的命令:

    # journalctl
    

    默认情况下(当 Storage= 在文件 /etc/systemd/journald.conf 中被设置为 auto),日志记录将被写入 /var/log/journal/。该目录是 systemd 软件包的一部分。若被删除,systemd 不会自动创建它,直到下次升级软件包时重建该目录。如果该目录缺失,systemd 会将日志记录写入 /run/systemd/journal。这意味着,系统重启后日志将丢失

    提示: 如果 /var/log/journal/ 位于 btrfs 文件系统,应该考虑对这个目录禁用写入时复制,方法参阅Btrfs#Copy-on-Write (CoW)

    Systemd 日志事件提示信息的记录按照优先级功能进行分离,符合经典的 BSD syslog 协议风格([Syslog],RFC 5424)。

    输出过滤

    journalctl 可以根据特定字段过滤输出。

    日志大小限制

    如果按上面的操作保留日志的话,默认日志最大限制为所在文件系统容量的 10%,即:如果 /var/log/journal 储存在 50GiB 的根分区中,那么日志最多存储 5GiB 数据。可以修改配置文件指定最大限制。如限制日志最大 50MiB:

    /etc/systemd/journald.conf

     SystemMaxUse=50M
    

    还可以通过配置片段而不是全局配置文件进行设置:

    /etc/systemd/journald.conf.d/00-journal-size.conf

    [Journal]
    SystemMaxUse=50M
    

    修改配置后要立即生效,需重启 systemd-journald.service 服务。

    详情参见 journald.conf(5).

    Journal log 日志流

    日志输入
    • 通过kmsg,获取内核日志
      systemd-journald 会监听 socket dev/kmsg 来获取 kernel log messages.
      journald-kmsg.c

      int server_open_dev_kmsg(Server *s) {
          ...
          s->dev_kmsg_fd = open("/dev/kmsg", mode);
          ...
      }
      
    • 通过syslog(3) 调用,获取简单系统日志。
      src/core/namespace.c

      static int mount_private_dev(MountEntry *m) {
          ...
          devlog = strjoina(temporary_mount, "/dev/log");
          if (symlink("/run/systemd/journal/dev-log", devlog) < 0)
          ...
      }
      

      journald-server.c

      int server_init(Server *s, const char *namespace) {
          ...
          /* systemd-journald-dev-log.socket: /run/systemd/journal/dev-log */
          r = server_open_syslog_socket(s, syslog_socket);
          ...
      }
      

      Journal log 兼容syslog, 即通过syslog(3) 打印的log 也会存储到 Journal log 中.
      systemd-journald 通过监听 socket /run/systemd/journal/dev-log 获取到 syslog(3). 因为通常在装有systemd的系统中 /dev/log/run/systemd/journal/dev-log 的一个软连接,而syslog(3)会将log 发送到/dev/log

    • 通过原生Journal API sd_journal_print(4),获取结构化系统日志
      journal-send.c

      _public_ int sd_journal_send(const char *format, ...) {
          ...
          r = sd_journal_sendv(iov, i);
          ...
      }
      
      _public_ int sd_journal_sendv(const struct iovec *iov, int n) {
          ...
          static const union sockaddr_union sa = {
                  .un.sun_family = AF_UNIX,
                  .un.sun_path = "/run/systemd/journal/socket",
          };
          ...
      }
      
      _public_ int sd_journal_printv(int priority, const char *format, va_list ap) {
          ...
          return sd_journal_sendv(iov, 2);
          ...
      }
      
    • 通过标准输出以及错误输出,获取systemd service 日志。
      journal-send.c

      _public_ int sd_journal_send(const char *format, ...) {
          ...
          r = sd_journal_sendv(iov, i);
          ...
      }
      
      _public_ int sd_journal_sendv(const struct iovec *iov, int n) {
          ...
          static const union sockaddr_union sa = {
                  .un.sun_family = AF_UNIX,
                  .un.sun_path = "/run/systemd/journal/socket",
          };
          ...
      }
      
      _public_ int sd_journal_printv(int priority, const char *format, va_list ap) {
          ...
          return sd_journal_sendv(iov, 2);
          ...
      }
      

      systemd service 可以通过设置 StandardOutput=journal/ StandardError=journal 将标准输出和错误输出存储到Journal log 中.
      systemd-journald 通过监听 socket /run/systemd/journal/stdout 获取service的log(以systemd 启动的service, fd stdout/stderr (1/2) 会被重定向到/run/systemd/journal/stdout)。

    • 通过内核audit 子系统,获取Audit log
      journald-audit.c

      int server_open_audit(Server *s) {
          ...
          s->audit_fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_AUDIT);
          ...
      }           
      
    日志输出

    systemd 提供了 socket /run/systemd/journal/syslog,以兼容传统日志服务。所有系统信息都会被传入。要使传统日志服务工作,需要让服务链接该 socket,而非 /dev/log官方说明)。

    journald.conf 使用 no 转发socket . 为了使 syslog-ng 配合 journald , 你需要在 /etc/systemd/journald.conf 中设置 ForwardToSyslog=yes . 参阅 Syslog-ng#Overview 了解更多细节.
    journald-syslog.c

    static void forward_syslog_iovec(
        ...
        j = strjoina(s->runtime_directory, "/syslog");
        ...
    }
    

    如果选择使用 rsyslog , 因为 rsyslog 从日志中 直接 读取Log (sd_journal_open/sd_journal_get_data),所以不再需要改变那个选项.

    rsyslog/plugins/imjournal/imjournal.c

    static rsRetVal openJournal(void) {
        ...
        if ((r = sd_journal_open(&journalContext.j, cs.bRemote? 0 : SD_JOURNAL_LOCAL_ONLY)) < 0) {
            LogError(-r, RS_RET_IO_ERROR, "imjournal: sd_journal_open() failed");
            iRet = RS_RET_IO_ERROR;
        }
        ...
    }
    
    static int journalGetData(const char *field, const void **data, size_t *length)
    {
        ...
        ret = sd_journal_get_data(journalContext.j, field, data, length);
        ...
    }
    
    相关 socket
    socket 说明
    /run/systemd/journal/dev-log 用以监听syslog(3)的输出
    /run/systemd/journal/stdout 用以监听systemd service 的标准/错误输出
    /run/systemd/journal/socket 用以监听jurnal原始打印API日志输入
    /run/systemd/journal/syslog 如果存在,systemd-journal 将收到的log 转发到该socket。但由于rsyslog更喜欢从日志中提取信息,而不是通过套接字接收消息,所以该socket一般不启用。
    流程图
    systemd-journal 日志流

    参考文章

    systemd/Journal (简体中文)
    systemd-journald.socket (8) - Linux Man Pages

    相关文章

      网友评论

        本文标题:关于Journal Log

        本文链接:https://www.haomeiwen.com/subject/lmsmyhtx.html