FPs

Systemd 日志管理相关

coredump管理

注:debian testing中systemd(204)的coredumpctl目前不可用,打包时候选择了--disable-coredump,原因不明。本节实验在fedora20上完成(with systemd-208)。

开启core dump

#ulimit -c

输入以上命令看看返回值,如果是非0,则表示core dump功能已开启,如果是0,需要手动开启。

手动开启(例:无限制存储、64MB)

#ulimit -c unlimited
#ulimit -c 67108864

注:以上设置只在当前终端下生效,全局设置可以在/etc/profile.d/目录新建配置文件写入配置信息。

获取core dump文件

systemd下使用coredumpctl工具获取的core dump是来自它的journal,即在磁盘目录上是找不到存储文件的(注:215有变化,见下面)。

查看所有的core dump记录:

# systemd-coredumpctl
# systemd-coredumpctl list '要匹配的字符串'

需要将core dump导出为文件,只需指定--output=FILE。另外coredumpctl可以根据PID、进程名称、执行路径等对code dump进行筛选,更多选项请man coredumpctl

注:systemd-215开始,可以配置为自动将coredumps存储在磁盘上(/var/lib/systemd/coredump,可能会经过压缩)。这个选项是默认开启的,配置文件在/etc/systemd/coredum.conf

若要配置为在指定目录获取core dump文件,而不通过systemd,只需配置kernel.core_pattern参数即可。

/sysctl/kernel.txt#core_pattern:

If the first character of the pattern is a '|', the kernel will treat the rest of the pattern as a command to run. The core dump will be written to the standard input of that program instead of to a file.

这个文件的默认值在systemd 下,是一个以'|' 字符(其实就是个管道)开头的字符串,一般类似:

| /usr/lib/systemd/systemd-coredump [参数](参数请在上文链接中中搜索'corename format specifiers')

若我们期望在指定目录(例如当前目录)立刻产生转储文件,那么只需要修改core_pattern 为如下:

#sysctl -w 'kernel.core_pattern=%t-%e-%p-%c.core'

在Fedora上,kernel.core_pattern=core,即默认可在程序目录下看到core.*文件。

关于core dump的过滤,systemd并没有提供更多的途径,依然是通过内核存储掩码来配置(coredump_filter),具体请查看上文的/sysctl/kernel.txt

本节参考链接


日志的记录,分类,过滤,分发和监控

Systemd Journal 特点

systemd 自带日志服务 journald,该日志服务的设计初衷是克服现有的 syslog 服务的缺点。比如:

  • syslog 不安全,消息的内容无法验证。每一个本地进程都可以声称自己是 Apache PID 4711,而 syslog 也就相信并保存到磁盘上。
  • 数据没有严格的格式,非常随意。自动化的日志分析器需要分析人类语言字符串来识别消息。一方面此类分析困难低效;此外日志格式的变化会导致分析代码需要更新甚至重写。

Systemd Journal 用二进制格式保存所有日志信息,用户使用 journalctl 命令来查看日志信息。无需自己编写复杂脆弱的字符串分析处理程序。

Systemd Journal 的优点如下:

  • 简单性:代码少,依赖少,抽象开销最小。
  • 零维护:日志是除错和监控系统的核心功能,因此它自己不能再产生问题。举例说,自动管理磁盘空间,避免由于日志的不断产生而将磁盘空间耗尽。
  • 移植性:日志 文件应该在所有类型的 Linux 系统上可用,无论它使用的何种 CPU 或者字节序。
  • 性能:添加和浏览 日志 非常快。
  • 最小资源占用:日志 数据文件需要较小。
  • 统一化:各种不同的日志存储技术应该统一起来,将所有的可记录事件保存在同一个数据存储中。所以日志内容的全局上下文都会被保存并且可供日后查询。例如一条固件记录后通常会跟随一条内核记录,最终还会有一条用户态记录。重要的是当保存到硬盘上时这三者之间的关系不会丢失。Syslog 将不同的信息保存到不同的文件中,分析的时候很难确定哪些条目是相关的。
  • 扩展性:日志的适用范围很广,从嵌入式设备到超级计算机集群都可以满足需求。
  • 安全性:日志 文件是可以验证的,让无法检测的修改不再可能。

日志记录

Messages coming in via /dev/log, via the native protocol, via STDOUT/STDERR of all services and via the kernel are received in the journal daemon.

Journal通过以下三种方式收集日志:

  • 程序使用libc库 syslog()输出的日志
  • 内核使用printk()函数打印的日志
  • 任何服务进程输出到STDOUT/STDERR的所有内容
syslog()

用法例1(C):

#include <syslog.h>

void syslog(int priority, const char *message, ... /* argument */);
#include <syslog.h>

int main(int argc, char *argv[]) {
        syslog(LOG_NOTICE, "Hello World");
        return 0;
}

用法例2(Python): - syslog

#!/usr/bin/evn python

import syslog
syslog.syslog('Hello World')

编写简单的service单元文件调用log.py。

python-syslog

或用journalctl命令查看:

python-syslog

查看生成的日记格式(JSON)(journalctl -o json-pretty -f):

{
        "__CURSOR" : "s=0e4330b5048f4ee09c213caf4c99f294;i=d1f;b=49cbe7a3d....",
        "__REALTIME_TIMESTAMP" : "1406216993978340",
        "__MONOTONIC_TIMESTAMP" : "24171673254",
        "_BOOT_ID" : "49cbe7a3d9f646beb526340832a145e6",
        "PRIORITY" : "6",
        "_UID" : "0",
        "_GID" : "0",
        "_SYSTEMD_SLICE" : "system.slice",
        "_MACHINE_ID" : "0de206a66f9cf937239d126753c62e2f",
        "_HOSTNAME" : "debian",
        "_CAP_EFFECTIVE" : "1fffffffff",
        "_TRANSPORT" : "syslog",
        "MESSAGE" : "Hello World",
        "SYSLOG_FACILITY" : "1",
        "SYSLOG_IDENTIFIER" : "log.py",
        "_PID" : "7643",
        "_COMM" : "python",
        "_EXE" : "/usr/bin/python2.7",
        "_CMDLINE" : "python /root/log.py",
        "_SYSTEMD_CGROUP" : "/system.slice/python-syslog.service",
        "_SYSTEMD_UNIT" : "python-syslog.service",
        "_SOURCE_REALTIME_TIMESTAMP" : "1406216993976092"
}
prntf()

如上文提到的,journal可以捕获服务进程往STDOUT/STDERR输出的所有内容,即例如C语言中的print函数打印的内容,Python中print打印的内容,以及Shell脚本中echo打印的内容等等都可以被journal捕获到,加入服务进程日志。

例:

#include <stdio.h>

int main(int argc, char *argv[]) {
        printf("Hello World\n");
        return 0;
}

如一般的printf打印内容,日志等级为LOG_INFO(可以通过service单元文件中的SyslogLevel=更改默认日志等级)。如何指定日志等级,可在每行打印内容前加上"",N为需要指定的日志等级。如下:

#include<stdio.h>

#define PREFIX_NOTICE "<5>"

int main(void){

  printf(PREFIX_NOTICE "Hello World\n");
  fprintf(stderr, "<3>Hello  Error\n");

  return 0;

}

log

其他例子:

#!/usr/bin/env python
print '<5>Hello World'
#!/bin/bash
echo "<5>Hello World"
Native Messages

systemd提供了原生的C语言库(systemd/sd-journal.h),用于向系统journal提交日志。 详情请查看systemd/sd-journal.h

例(未实验,例子与实验结果摘录自Lennart的blog):

#include <systemd/sd-journal.h>

int main(int argc, char *argv[]) {
        sd_journal_print(LOG_NOTICE, "Hello World");
        return 0;
}

相比上文使用print()或者syslog()提交的日志,使用sd_journal_print可以直观的指定LOG等级、日志内容等等,另外输出的日志会包含执行代码的位置信息,例如执行到的函数,代码文件位置,代码具体行数,方便开发人员调试。见下图粗体条目:

log

除了可以包含代码信息,也可以向提交的日志中加入自定义段包含更多自定义信息,配合journalctl工具,可以更方便的过滤日志。

例:

#include <systemd/sd-journal.h>
#include <unistd.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
        sd_journal_send("MESSAGE=Hello World!",
                        "MESSAGE_ID=52fb62f99e2c49d89cfbf9d6de5e3555",
                        "PRIORITY=5",
                        "HOME=%s", getenv("HOME"),
                        "TERM=%s", getenv("TERM"),
                        "PAGE_SIZE=%li", sysconf(_SC_PAGESIZE),
                        "N_CPUS=%li", sysconf(_SC_NPROCESSORS_ONLN),
                        NULL);
        return 0;
}

见下图粗体部分:

native-message

使用journalctl,通过指定自定义条目,快速过滤日志。

native-message

extension for systemd

除了systemd提供的一套C语言库,目前其他个别语言也衍生了systemd的相关扩展,其中就包含了和journald相关的扩展。

from systemd import journal
journal.send('Hello world')
journal.send('Hello, again, world', FIELD2='Greetings!', FIELD3='Guten tag')
var journald = require('journald').Log;
journald.log({
MESSAGE: 'Hello world',
ARG1: '',
ARG2: '',
ARG3: ''
});
<?php
sd_journal_send('MESSAGE=Hello world.');
sd_journal_send('MESSAGE=Hello, again, world.', 'FIELD2=Greetings!', 'FIELD3=Guten tag.');
sd_journal_send('ARBITRARY=anything', 'FIELD3=Greetings!');
local fmt = require('string').format
local Journal = require('systemd-journal')
j = Journal:new()
j:print(1, "Hello World From Print")
j:send({PRIORITY=1, MESSAGE="Hello World From Send", VERSION=process.version})
本小节参考链接

日志存储

systemd中日志不再以文本格式保存, Journal用二进制格式保存所有日志信息,用户使用 journalctl 命令来查看日志信息。

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

手动创建日志储存目录:

# mkdir -p /var/log/journal

重启之后,可在/var/log/journal/$MACHINE_ID/目录发现存储的日志文件,\$MACHINE_ID为系统本机的ID,详细介绍请参考Machine ID一节。

注:journal会自动管理磁盘空间,避免由于日志的不断产生而将磁盘空间耗尽,默认日志存储限制为所在文件系统容量的10%。 可在journald.conf文件中通过配置SystemMaxUse=, SystemKeepFree=, SystemMaxFileSize=, RuntimeMaxUse=, RuntimeKeepFree=, RuntimeMaxFileSize= 等选项来设置日志占的占的磁盘空间比率、systemd-journal必须给系统空出的文件系统容量等等,具体请参考本节参考链接4。

日志过滤和分发

过滤

systemd journal的工具journalctl提供了强大的日志过滤功能,可以按日志等级、进程ID、UID、GID、单元、执行路径、设备路径、时间区间、主机名、程序名等过滤,设置将这些选项进行配合使用过滤日志,详情请参考本节参考链接3与journalctl 用户手册。

使用示例:

显示本次启动后的所有日志:

# journalctl -b

显示本地启动后,且等级为err的日志:

#journalctl -b -p err

动态跟踪最新日志:

# journalctl -f

显示特地时间区间内的日志:

#journalctl --since=yesterday
#journalctl --since=2012-10-15 --until="2011-10-16 23:59:59"

显示某磁盘设备相关的日志:

#journalctl /dev/sda

显示特定程序的所有消息:

# journalctl /usr/lib/systemd/systemd

显示特定进程的所有消息:

# journalctl _PID=1

显示指定单元的消息(+时间区间):

# journalctl -u httpd
# journalctl -u httpd --since=00:00 --until=9:30

显示与指定用户相关的日志:

# journalctl _UID=70
# journalctl _UID=70 _UID=71

http://0pointer.de/blog/projects/journalctl.html #And now, it becomes magic!

分发

systemd journal本身未提供日志分发功能。

  • 常见的解决方案,做好单机日志的存储配置之后,通过rsync、btsync等工具收集同步日志至某中心节点进行分析处理。
  • 另外在systemd-193添加了systemd-journal-gatewayd.service,服务器开启此服务之后,将监听本地19531端口,其他机器可通过HTTP或JSON协议访问服务器得到后者日志,详细介绍(登录验证等)请查看systemd-journal-gatewayd.service
  • 注:systemd-212引入了 systemd-journal-remote >systemd-journal-remote is a command to receive serialized journal events and store them to the journal.
日志监控和报警

journal没有监控和报警方面的特性与功能,可以通过日志工具的过滤再配合脚本或程序做监控与报警。

本节参考链接
  1. 浅析 Linux 初始化 init 系统,第 3 部分: Systemd#日志服务
  2. Log and Service Status
  3. Using the Journal
  4. journald.conf — Journal service configuration file
  5. Writing syslog Daemons Which Cooperate Nicely With systemd
  6. systemd Journal译文

Journal与Rsyslog(syslog)

systemd 提供了 socket /run/systemd/journal/syslog,以兼容传统日志服务。所有系统信息都会被传入。要使传统日志服务工作,需要让syslog daemon绑定该 socket, 而非 /dev/log,见systemd v38 released

debian testing下默认安装rsyslog用于代替syslog,

/etc/systemd/system/syslog.service:注意ExecStart 选项

[Unit]
Description=System Logging Service
Requires=syslog.socket

[Service]
Type=notify
ExecStart=/usr/sbin/rsyslogd -n
StandardOutput=null

[Install]
WantedBy=multi-user.target
Alias=syslog.service

/lib/systemd/system/rsyslog.service:注意Alias选项

[Unit]
Description=System Logging Service
Requires=syslog.socket

[Service]
Type=notify
ExecStart=/usr/sbin/rsyslogd -n
StandardOutput=null

[Install]
WantedBy=multi-user.target
Alias=syslog.service

而默认情况下,journald.conf中ForwardToSyslog=默认开启,journal的日志会转发给syslog daemon,通过套接字(/run/systemd/journal/syslog)。

/lib/systemd/system/syslog.socket

[Unit]
Description=Syslog Socket
Documentation=man:systemd.special(7)
Documentation=http://www.freedesktop.org/wiki/Software/systemd/syslog
DefaultDependencies=no
Before=sockets.target syslog.target shutdown.target

# Don't allow logging until the very end
Conflicts=shutdown.target

# Pull in syslog.target to tell people that /dev/log is now accessible
Wants=syslog.target

[Socket]
ListenDatagram=/run/systemd/journal/syslog
SocketMode=0666
PassCredentials=yes
PassSecurity=yes
ReceiveBuffer=8M

注:关于进程向journal提交日志的接口请参考 详细介绍--日志管理--日志的记录..一节,另外可关注服务单元的标准输出StandardOutput=,可选择将 进程标准输出连接到inherit, null, tty, syslog, kmsg, journal, syslog+console, kmsg+console, journal+console 还是 socket,默认为journal,即systemd的日志系统。

Messages coming in via /dev/log, via the native protocol, via STDOUT/STDERR of all services and via the kernel are received in the journal daemon.

Audit record generation for started services

介绍

systemd Status Update:

We hooked systemd up to the Linux auditing subsystem: as first init system at all systemd now generates auditing records for all services it starts/stops, including their failure status.

systemd下服务单元的启动、停止以及进入失败状态都会生成Audit记录,日志记录在/var/log/audit/audit.log文件中。

实例

安装auditd:

# aptitude install -y auditd

默认的auditd.conf与auditd.rulues下(实验环境debian stable:sysv,debian testing:systemd,两者默认auditd.conf差别只是num_logs项,不影响;两者默认auditd.rulues相同),设置auditd开启自动启动,我们看看开启自动启动的那些服务信息是否会被记录到audit.log中。

(sysv与systemd下的系统中,都安装了nginx服务,并随机启动)

sysv下,见图sysv-auditd-log。

sysv-auditd-log

systemd下,见图systemd-auditd-log。

systemd-auditd-log

可发现systemd下的auditd.log中记录了systemd下service单元启动的信息,sysv下见不到sysv管理的服务的启动信息。

我们手动restart或停止systemd下的nginx.service,见图 systemd-auditd-nginx-log,同样发现有相关信息,从左侧的Type,可区分哪些记录是启动,哪些记录是停止。

systemd-auditd-nginx-log

本节参考链接

Systemd-analyze

systemd-analyze 是一个内置的启动性能分析工具,它除了可以分析和直观的展现系统启动时候每项服务的耗时,也可以图表的方式直观得绘出启动阶段各服务的依赖关系,方便系统管理员优化系统启动,理解系统服务依赖关系等,具体用法请参考用户手册,非常简单。

注:systemd-209,systemd-analyze增加远程控制功能(over SSH)。

本节参考链接

2016-03-18 Systemd