随着 systemd 成了主流的 init 系统, systemd 的功能也在不断的增加, 比如对系统日志的管理. Systemd 设计的日志系统好处多多, 这里笔者就不再赘述了, 本文笔者主要介绍 systemd journal 收集日志的三种方式:
程序使用 libc 库中的 syslog() 函数输出的日志
使用 printf() 函数打印的日志
任何服务进程输出到 STDOUT/STDERR 的所有内容
说明: 本文的演示环境为 Ubuntu 16.04.
syslog()
该函数的声明如下:
- #include <syslog.h>
- void syslog(int priority, const char *message, ... /* argument */);
创建下面的 C 语言代码, 并保存到文件 clog.c 文件中:
- #include <syslog.h>
- int main(int argc, char *argv[]) {
- syslog(LOG_NOTICE, "C Hello World");
- return 0;
- }
用下面的命令编译程序:
$ gcc -Wall clog.c -o clog
然后执行编译好的 clog 程序, 就可以从 journal -f 的输出中看到对应的日志:
这里笔者执行了三次 clog 程序, 所以日志输出了三遍.
代码中的 LOG_NOTICE 代表日志的严重等级, 我们可以使用下面定义好的等级:
- #define LOG_EMERG 0 /* system is unusable */
- #define LOG_ALERT 1 /* action must be taken immediately */
- #define LOG_CRIT 2 /* critical conditions */
- #define LOG_ERR 3 /* error conditions */
- #define LOG_WARNING 4 /* warning conditions */
- #define LOG_NOTICE 5 /* normal but significant condition */
- #define LOG_INFO 6 /* informational */
- #define LOG_DEBUG 7 /* debug-level messages */
下面尝试在 python 代码中做同样的事情, 把下面的代码保存到文件 plog.py 中:
- #!/usr/bin/evn python
- import syslog
- syslog.syslog('P Hello World')
然后执行下面的命令:
$ python plog.py
笔者同样执行了三遍, 这次输出的是 python 代码中的日志.
我们还可以通过 journalctl -o JSON-pretty -f 命令查看 JSON 格式的日志:
printf()
journal 可以捕获服务进程往 STDOUT/STDERR 输出的所有内容, 比如 C 语言中 print 函数打印的内容, Python 中 print 打印的内容, 以及 Shell 脚本中 echo 打印的内容等等都可以被 journal 捕获到并加入到日志中. 注意, 只有以 service 的方式运行程序时, journal 才会捕获 STDOUT/STDERR 输出的内容.
创建下面的 C 语言代码, 并保存到文件 printlog.c 文件中:
- #include <stdio.h>
- int main(int argc, char *argv[]) {
- printf("C Print Hello World.\n");
- return 0;
- }
用下面的命令编译程序:
$ gcc -Wall printlog.c -o printlog
配置一个简单的 service, 先创建 一个配置文件 /lib/systemd/system/testlog.service, 其内容如下:
- [Unit]
- Description=test log
- [Service]
- ExecStart=/home/nick/projects/journaldemo/printlog
- [Install]
- WantedBy=multi-user.target
- $ sudo systemctl daemon-reload
- $ sudo systemctl start testlog.service
Journal 会捕获 STDOUT/STDERR 输出的内容:
默认情况下, 这样输出的日志等级为 LOG_INFO(6), 我们可以通过 JSON 格式的日志看到日志等级信息:
我们还可以在打印日志时指定日志的等级, 比如在每行打印的内容前加上 "<N>",N 为需要指定的日志等级. 看下面的 C 语言示例:
- #include<stdio.h>
- #define PREFIX_NOTICE "<5>"
- int main(void){
- printf(PREFIX_NOTICE "Hello World\n");
- fprintf(stderr, "<3>Hello Error\n");
- return 0;
- }
把上面的代码编译为 printlog 程序, 再查看下日志, 显示的就是我们自己设置的日志等级:
在 Python 中的用法如下:
- #!/usr/bin/env python
- print '<5>Hello World'
在 bash 中的用法如下:
- #!/bin/bash
- echo "<5>Hello World"
Systemd 日志库
Systemd 提供了原生的 C 语言库 (systemd/sd-journal.h) 用于向 journal 输出日志 (Ubuntu 16.04 需要通过 sudo apt install libsystemd-dev 命令安装 libsystemd-dev 包), 相关函数的声明为:
- #include <systemd/sd-journal.h>
- int sd_journal_print(int priority, const char *format, ...);
- int sd_journal_send(const char *format, ...);
把下面的示例代码会把日志发送给 journal:
- #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 等级, 日志内容等等, 另外输出的日志会包含执行代码的位置信息, 例如执行到的函数, 代码文件位置, 代码具体行数, 方便开发人员调试.
除了可以包含代码信息, 也可以向提交的日志中加入自定义段包含更多自定义信息, 配合 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;
- }
我们在日志中添加了自定义的字段, 并且可以通过 journalctl 过滤这些字段.
除了 systemd 提供的一套 C 语言库, 目前其他个别语言也衍生了 systemd 的相关扩展, 其中就包含了和 journald 相关的扩展. 下面是段 python 的演示代码:
- from systemd import journal
- journal.send('Hello world')
- journal.send('Hello, again, world', FIELD2='Greetings!', FIELD3='Guten tag')
在运行上面的代码前你需要先通过 pip 安装 python 的 systemd 模块 (pip install systemd ), 然后运行这段代码, 你就可以从日志中看到它输出的信息了:
展开成 JSON 格式看下:
我们自定义的字段 FIELD2 和 FIELD3 也都输出到日志中了.
总结
本文介绍了常见的一些往 systemd journal 中写入日志的方式, 了解这些日志的写入方式可以帮助我们更好的设计应用的日志输出, 并有助于我们通过日志解决问题.
参考:
Systemd 日志管理相关 https://fangpeishi.com/systemd_chapter4.html
systemd for Developers III
来源: https://www.cnblogs.com/sparkdev/p/10509938.html