关于Journal Log

简介

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一般不启用。
流程图
关于Journal Log_第1张图片
systemd-journal 日志流

参考文章

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

你可能感兴趣的:(关于Journal Log)