zoukankan      html  css  js  c++  java
  • 通过 Systemd Journal 收集日志

    随着 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.
    ");
        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
    ");
      fprintf(stderr, "<3>Hello  Error
    ");
    
      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 日志管理相关
    systemd for Developers III

  • 相关阅读:
    Jquery Ajax 调用 WebService
    Dapper.NET
    HTML5 canvas标签
    SQL内外左右交叉连接
    水晶报表纵向重复
    AngularJS 菜鸟
    什么是MVC框架?
    伪类和伪元素的区别
    常用的本地存储-----cookie篇
    JavaScript中基本数据类型和引用数据类型的区别
  • 原文地址:https://www.cnblogs.com/sparkdev/p/10509938.html
Copyright © 2011-2022 走看看