由一个简单需求到Linux环境下的syslog、unix domain socket

  本文记录了因为一个简单的日志需求,继而对linux环境下syslog、rsyslog、unix domain socket的学习。本文关注使用层面,并不涉及rsyslog的实现原理,感兴趣的读者可以参考rsyslog官网。另外,本文实验的环境实在debian8,如果是其他linux发行版本或者debian的其他版本,可能会稍微有些差异。

需求:

  工作中有一个在Linux(debian8)环境下运行的服务器程序,用python语言实现,代码中有不同优先级的日志需要记录,开发的时候都是使用python的logging模块输出到文件,示例代码如下:

  

 1 import logging, os
 2
 3 logger = None
 4 def get_logger():
 5     global logger
 6     if not logger:
 7         logger = logging.getLogger(‘ServerLog‘)
 8         logger.setLevel(logging.INFO)
 9         filehandler = logging.FileHandler(os.environ[‘HOME‘] + ‘/Server.log‘, encoding=‘utf8‘)
10         filehandler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
11         logger.addHandler(filehandler)
12     return logger
13
14 def some_func():
15     get_logger().info("call some_func")
16
17 if __name__ == ‘__main__‘:
18     some_func()

  运行上面这段代码,就会在home目录下面产生一个server.log文件。

  后来数据分析的部门说他们希望能够实时拿到一部分日志,他们有一台专门处理日志的服务器,那么怎么把日志发给他们呢?笔者之前并没有相关经验,数据分析部门的同事说,这种需求他们都是找运维人员帮忙。运维同事给出的方案很简单:产品把日志写到syslog,然后他们负责把带有某些关键字的日志转发给数据分析部门,在运维同事的指导下,把代码改成了这样:

 1 import logging
 2 import logging.handlers
 3
 4 logger = None
 5 def get_logger():
 6     global logger
 7     if not logger:
 8         logger = logging.getLogger(‘ServerLog‘)
 9         logger.setLevel(logging.INFO)
10
11         sys_handler = logging.handlers.SysLogHandler(‘/dev/log‘, facility=logging.handlers.SysLogHandler.LOG_LOCAL0)
12         syslog_tag = ‘ServerLog‘
13         sys_handler.setFormatter(logging.Formatter(syslog_tag + ":%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
14
15         logger.addHandler(sys_handler)
16     return logger
17
18 def some_func():
19     get_logger().info("call some_func")
20
21 if __name__ == ‘__main__‘:
22     some_func()

  上面的代码修改了日志的输出形式,直观的感受就是从文件server.log 到了 /dev/log,但/dev/log对应的是SysLogHandler,并不是FileHandler,所以肯定不是一个普通的文件。此时,我有两个疑问:第一,这里我并没有将日志输出到home目录下的Server.log文件,但是程序运行的时候生成了这么一个文件;第二,怎么讲日志发送到数据分析部门的服务器。

  不懂就问:

  Q:新的代码下怎么生成Server.log文件,日志内容又是怎么转发到数据分析部门的服务器?

  A:  这个是/etc/init.d/rsyslog这个后台程序根据/etc/rsyslog.conf 这个配置文件 将日志输出到不同的文件,包括网络文件,即其他服务器。看/etc/rsyslog.conf这个配置就明白了。

  Q:OK,那python代码将文件输出到/dev/log跟 rsyslog又是什么关系呢?

  A:python的sysloghandler会将日志发送到rsyslog,他们之间使用unix domain socket通信,具体看logging模块的源码就知道了

unix domain socket:

  按照上面的对话的意思,python程序先将日志发送给rsyslog这个程序,然后rsyslog再处理收到的日志数据,所以先看logging代码:

  SysLogHandler这个类在logging.handlers.py, 核心代码如下:

 1     def __init__(self, address=(‘localhost‘, SYSLOG_UDP_PORT),
 2                  facility=LOG_USER, socktype=socket.SOCK_DGRAM):
 3         """
 4         Initialize a handler.
 5
 6         If address is specified as a string, a UNIX socket is used. To log to a
 7         local syslogd, "SysLogHandler(address="/dev/log")" can be used.
 8         If facility is not specified, LOG_USER is used.
 9         """
10         logging.Handler.__init__(self)
11
12         self.address = address
13         self.facility = facility
14         self.socktype = socktype
15
16         if isinstance(address, basestring):
17             self.unixsocket = 1
18             self._connect_unixsocket(address)
19         else:
20             self.unixsocket = 0
21             self.socket = socket.socket(socket.AF_INET, socktype)
22             if socktype == socket.SOCK_STREAM:
23                 self.socket.connect(address)
24         self.formatter = None
25
26     def _connect_unixsocket(self, address):
27         self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
28         # syslog may require either DGRAM or STREAM sockets
29         try:
30             self.socket.connect(address)
31         except socket.error:
32             self.socket.close()
33             self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
34             self.socket.connect(address)

  在__init__.doc里面写得很清楚,如果address是一个字符串(默认值是一个tuple),那么会建立一个unix socket(unix domain socket)。如果address为“/dev/log”(正如我们之前的python代码),那么输出到本机的syslogd程序。另外,在第27行 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) socket.socket的第一个参数family 的值为AF_UNIX,而不是我们经常使用的AF_INET(IPV4)或者AF_INET6(IPV6)。那么什么是unix domain socket呢?

  

  unix domain socket是进程间通信(IPC:inter-process communication)的一种方式,其他还有管道、命名管道、消息队列、共享内存、socket之类的。unix domain socket与平常使用的socket(狭义的internet socket)有什么区别呢,那就是unix domain socket只能在同一台主机上的进程之间通信,普通的socket也可以通过‘localhost‘来在同一台主机通信,那么unix domain socket有哪些优势呢?

  第一:不需要经过网络协议栈

  第二:不需要打包拆包、计算校验和、维护序号和应答等

  所以,优势就是性能好,一个字,快。

  下面用一个简单的服务器客户端例子来看看unix domain socket的使用方法与过程:

  服务器:uds_server.py

 1 ADDR = ‘/tmp/uds_tmp‘
 2
 3 import socket, os
 4
 5 def main():
 6     try:
 7         sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
 8         if os.path.exists(ADDR):
 9             os.unlink(ADDR)
10         sock.bind(ADDR)
11         sock.listen(5)
12         while True:
13                 connection, address = sock.accept()
14                 print "Data : %s" % connection.recv(1024);
15                 connection.send("hello uds client")
16                 connection.close()
17     finally:
18         sock.close()
19
20 if __name__ == ‘__main__‘:
21         main()

  客户端:uds_client.py

 1 ADDR = ‘/tmp/uds_tmp‘
 2
 3 import socket
 4
 5 def main():
 6         sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
 7         sock.connect(ADDR)
 8         sock.send(‘hello unix domain socket server‘)
 9         print ‘client recieve‘, sock.recv(1024)
10         sock.close()
11
12 if __name__ == ‘__main__‘:
13         main()

  首先:运行服务器 python uds_server.py,这个时候在/tmp 目录下产生了文件,用ls查看详细信息如下:

  

  可以看到,文件类型(第一个字段)为s,代表socket文件。(PS: 如果进程间用命令管道通信,也是利用中间文件,ls显示的文件类型为p)

  运行客户端 python uds_client.py,在客户端和服务器端都有相应的输出,使用方法与普通socket没有什么大的差异。

日志转发流程:

  在了解了unix domain socket这个概念之后,下面就比较简单了,首先是/dev/log这个文件,我们用ls来查看这个文件的信息

  

  可以看到这个文件是一个符号链接文件,真实的文件是/run/systemd/journal/dev-log, 那么再来查看这个文件

  

  ok,是一个socket文件,复合预期,按照之前的unix domain socket的例子,rsyslog也应该咋这个文件上监听,我们来看看

  

  lsof fd可以列出所有使用了这个文件(linux下文件的概念比较宽泛)的进程,事实上我们看到只有systemd和systemd-j两个不明所以的进程。那么直接看看rsyslog使用的unix domain socket吧

  

  额,可以看到rsyslogd使用的socket domain socket是/run/systemd/journal/syslog,并不是/run/systemd/journal/dev-log,这两个文件在同一个目录下,那么再来看看还有哪些进程使用了/run/systemd/journal/syslog。

  

  so,systemd和rsyslogd都使用了这个文件,感觉像是应用进程(e.g. 上面的python程序)将日志通过/run/systemd/journal/dev-log(/dev/log背后真正的文件)发送到systemd, 然后systemd 再将日志通过/run/systemd/journal/syslog发送到rsyslogd,是不是这样呢,google了一下,发现了这篇文章understand-logging-in-linux,确实是这么一个过程:

  

systemd has a single monolithic log management program, systemd-journald. This runs as a service managed by systemd.

  • It reads /dev/kmsg for kernel log data.
  • It reads /dev/log (a symbolic link to /run/systemd/journal/dev-log) for application log data from the GNU C library‘s syslog() function.
  • It listens on the AF_LOCAL stream socket at /run/systemd/journal/stdout for log data coming from systemd-managed services.
  • It listens on the AF_LOCAL datagram socket at /run/systemd/journal/socket for log data coming from programs that speak the systemd-specific journal protocol (i.e. sd_journal_sendv() et al.).
  • It mixes these all together.
  • It writes to a set of system-wide and per-user journal files, in /run/log/journal/ or /var/log/journal/.
  • If it can connect (as a client) to an AF_LOCAL datagram socket at /run/systemd/journal/syslogit writes journal data there, if forwarding to syslog is configured.

  

  ok,到现在为止,我们知道了应用程序的日志是怎么转发到rsyslog,那么rsyslog怎么处理接收到的日志,秘密就在/etc/rsyslog.conf, 在打开这个配置文件之前,我们先看看rsyslog官网的简单描述:

  RSYSLOG is the rocket-fast system for log processing.

  原来R是rocket-fast的意思!火箭一般快!官网声称每秒可以处理百万级别的日志。rsyslogd在部分linux环境是默认的syslogd程序(至少在笔者的机器上),d是daemon的意思,后台进程。系统启动的时候就会启动该进程来处理日志(包括操作系统自身和用户进程的日志)。打开修改过的/etc/rsyslog.conf, 接下来就是见证奇迹的时刻

  

  原来一举一动都在监控之中。这个文件是系统提供的,直接在这个文件上做修改显然不是明智之举。如上图红色部分,可以再rysyslog.d文件夹下增加自己的配置文件,定制日志过滤规则。那么看看的rsyslog.d文件夹下新增的tmp.conf

$FileOwner USERNAME
$FileGroup USERNAME
$FileCreateMode 0644
$DirCreateMode 0755
$Umask 0022
$template serverLog,"/home/USERNAME/Server.log"
$template LogFormat,"%msg%\n"
if $syslogfacility-text == ‘local0‘ and $syslogtag contains ‘ServerLog‘ then -?serverLog;LogFormat
& stop

#if $syslogfacility-text == ‘local0‘ and $syslogtag contains ‘ServerLog‘ then @someip:port
#& stop

  再来回顾一下对应的应用代码:

 1 import logging
 2 import logging.handlers
 3
 4 logger = None
 5 def get_logger():
 6     global logger
 7     if not logger:
 8         logger = logging.getLogger(‘ServerLog‘)
 9         logger.setLevel(logging.INFO)
10
11         sys_handler = logging.handlers.SysLogHandler(‘/dev/log‘, facility=logging.handlers.SysLogHandler.LOG_LOCAL0)
12         syslog_tag = ‘ServerLog‘
13         sys_handler.setFormatter(logging.Formatter(syslog_tag + ":%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
14
15         logger.addHandler(sys_handler)
16     return logger
17
18 def some_func():
19     get_logger().info("call some_func")
20
21 if __name__ == ‘__main__‘:
22     some_func()

  注意:配置文件需要与应用代码配合,比如代码中第11行 facility=logging.handlers.SysLogHandler.LOG_LOCAL0 与 配置中 $syslogfacility-text == ‘local0‘ 相对应;代码第12行 syslog_tag = ‘ServerLog‘ 与 配置文件 $syslogtag contains ‘ServerLog‘ 对应。关于python代码中syslogtag的设置,参考了stackoverflow上的这个问答

  当我们修改了配置时候需要通过命令 /etc/init.d/rsyslog restart 来重启rsyslogd,重启之后再运行之前的python文件,就可以了。

  

总结:

  日志从应用程序到最终的日志文件(或者远程服务器)的流程如下:

  

  

references:

inter-process communication

unix domain socket

understand-logging-in-linux

时间: 2024-08-02 07:01:51

由一个简单需求到Linux环境下的syslog、unix domain socket的相关文章

java 在linux环境下写入 syslog 问题研究

1.Syslog 在Unix类操作系统上,syslog广泛应用于系统日志.syslog日志消息既可以记录在本地文件中,也可以通过网络发送到接收syslog的服务器.接收syslog的服务器可以对多个设备的syslog消息进行统一的存储,或者解析其中的内容做相应的处理.常见的应用场景是网 络管理工具.安全管理系统.日志审计系统. 2.linux下syslog系统 syslog是Linux系统默认的日志守护进,内核等一些模块的日志基本上是通过syslog存储的.新版本的ubuntu(不知道具体从哪个

Linux环境下部署完JDK后运行一个简单的Java程序

前言 前一篇文章详细讲解了如何在Windows环境下安装虚拟机+Linux系统,并且成功部署了JDK. 不过部署完JDK之后,我们判断部署是否成功的依据是看"java -version"命令是否有正确的内容打印,也许这并不具备太大的说服力.可能能够运行起一个正确的java程序来,不管从感性角度还是理性角度来说,都会有一个更好的认识. 所以本文写一段很简单的java代码,并且在Linux环境下编译运行,以证明JDK部署得确实没有问题. 代码验证JDK部署是否正确 1.级联建立两个目录 首

(1)Jenkins Linux环境下的简单搭建

(1)Jenkins Linux环境下的简单搭建 Jenkins是一个开源软件项目,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能. ----百度百科 这是一款基于Java开发的工具.种种原因,最近刚开始接触,决定研究一下.Jenkins的搭建方法不止一种,一下就是个人总结的其中一种,文章内容比较浅显,不足之处,欢迎指正. 首先,所需要准备的工具JDK.Maven.资料上显示JDK版本最好高于1.7,并没有研究1.7以下版本,所谓"没有实际调研,就没有发言权",在此就不做过多

深度分析LINUX环境下如何配置multi-path

首先介绍一下什么是多路径(multi-path)?先说说多路径功能产生的背景,在多路径功能出现之前,主机上的硬盘是直接挂接到一个总线(PCI)上,路径是一对一的关系,也就是一条路径指向一个硬盘或是存储设备,这样的一对一关系对于操作系统而言,处理相对简单,但是缺少了可靠性.当出现了光纤通道网络(Fibre Channle)也就是通常所说的SAN网络时,或者由iSCSI组成的IPSAN环境时,由于主机和存储之间通过光纤通道交换机或者多块网卡及IP来连接时,构成了多对多关系的IO通道,也就是说一台主机

Linux环境下线程消息同步的陷阱

我们程序中常常会使用到线程间的消息同步处理,比如以下一段伪码 var message = "": void func()  {   1. 启动线程Thread(该线程中填充message的内容):   2. 阻塞,直到等待到完成message填充的事件:   3. 处理message:   .... } void Thread()  {   1. 通过某种处理填充message:   2. 触发func中的阻塞事件: } 我们通常会使用条件变量来完成类似情况的线程同步处理 比如wind

mosquitto在Linux环境下的部署/安装/使用/测试

mosquitto在Linux环境下的部署 看了有三四天的的源码,(当然没怎么好好看了),突然发现对mosquitto的源码有了一点点感觉,于是在第五天决定在Linux环境下部署mosquitto. 使用传统源码安装步骤: 步骤1:http://mosquitto.org/files/source/官网下载源码,放到Linux环境中.解压后,找到主要配置文件config.mk,其中包含mosquitto的安装选项,需要注意的是,默认情况下mosquitto的安装需要OpenSSL(一个强大的安全

Linux环境下安装Tigase XMPP Server

Tigase是一种XMPP服务器,可以作为采用XMPP协议的各种IM(Instant Messeging)工具(如Pandion.Spark等)的服务器. 在Linux环境下安装Tigase的步骤如下: (1)下载安装文件 到https://projects.tigase.org/projects/tigase-server/files下载安装文件,我下载的是tigase-server-5.2.0-b3447-dist.tar.gz. (2)解压缩 创建一个文件夹,将tigase-server-

【转载】linux环境下tcpdump源代码分析

linux环境下tcpdump源代码分析 原文时间 2013-10-11 13:13:02   原文链接   主题 Tcpdump 作者:韩大卫 @ 吉林师范大学 tcpdump.c 是tcpdump 工具的main.c, 本文旨对tcpdump的框架有简单了解,只展示linux平台使用的一部分核心代码. Tcpdump 的使用目的就是打印出指定条件的报文,即使有再多的正则表达式作为过滤条件.所以只要懂得tcpdump -nXXi eth0 的实现原理即可. 进入main之前,先看一些头文件 n

linux环境下玩转截图

当习惯了使用linux环境处理事务时,总少不了截图的需求.windows环境下有各种截图工具方便大家进行截图处理,那么linux环境下都有哪些截图方式或工具呢?这里就给大家盘点一下linux环境下几种截图方式: (1)使用键盘功能键Print Screen或Alt+Print Screen组合键来抓图 这种方式当属最简单的截图方式,可以借助Print Screen截取整个屏幕或Alt+Print Screen组合键来抓取当前活动窗口界面.但当想抓取更具体界面(如terminal的某一部分)时,这