rsyslogのアウトプットプラグイン機能(omprog)を使ってみる

概要

rsyslog のアウトプットモジュールには omprog というのがあり、好きな言語でsyslog データを標準入力で受け取り、データを処理するプラグイン機構がある。
この機能は rsyslog 4.3.0 と結構昔から存在するにもかかわらず、自分は知らなかったのだけど、rsyslog のブログでこの機能を知ったので、ミニマムで動かすところまでを試してみた。

http://www.rsyslog.com/writing-external-output-plugins-for-rsyslog/

# omprog は v4 のころからあるのに with v8, we officially support external plugins とコメントされているのは突っ込まないでおく。

検証環境

  • OS : CentOS 6
  • rsyslogd 8.2.2

rsyslog external plugin

通常のモジュールは、アウトプットを例にすると
ローカルのファイルシステムに出力に出力する omfile や MySQL に保存する ommysql など

  • プログラムは C で記述され
  • rsyslog のプロセスの一部として処理されていた

一方で、external plugin は

  • 任意の言語で記述でき
  • rsyslog とは別プロセスで起動して処理される。

rsyslog 設定

アウトプット設定をいじる。

rsyslog の action で onprog モジュールを指定し、binary オプションにプラグインプログラムを指定する。
データ加工しやすいように、JSON 形式で渡す。

rsyslog8 だと RainerScript を使って以下のように書ける。

# /etc/rsyslog.conf
...

#### MODULES ####

## JSON 出力形式を "json-style" という名前で定義
# http://www.rsyslog.com/output-to-elasticsearch-in-logstash-format-kibana-friendly/
template(name="json-style"
  type="list") {
    constant(value="{")
      constant(value="\"timestamp\":\"")      property(name="timereported" dateFormat="rfc3339")
      constant(value="\",\"host\":\"")        property(name="hostname")
      constant(value="\",\"severity\":\"")    property(name="syslogseverity-text")
      constant(value="\",\"facility\":\"")    property(name="syslogfacility-text")
      constant(value="\",\"tag\":\"")         property(name="syslogtag" format="json")
      constant(value="\",\"message\":\"")     property(name="msg" format="json")
    constant(value="\"}\n")
}

# onprog モジュールで external plugin(/tmp/plugin.py) を指定。
module(load="omprog")
action(type="omprog"
       binary="/tmp/plugin.py"
       template="json-style")

インターフェースドキュメントによると

Rsyslog pushes messages via stdin. Each message is terminated by a LF. So a plugin can obtain a full messages simply by reading a line.

だそうなので、JSON 形式の末尾に明示的に LF("\n") を突っ込んでいる。

プラグインプログラム

次にプラグインプログラムを用意する。

rsyslog ソースコードの rsyslog/plugins/external/skeletons 以下に

  • C
  • java
  • perl
  • python

のひな形が転がっているので、活用させて頂く。

今回は python/mm-python.py を一部変更し、json 形式の syslog をローカルサーバにファイル出力させたのが以下。

#! /usr/bin/python

"""A skeleton for a python rsyslog message modification plugin
   Copyright (C) 2014 by Adiscon GmbH

   This file is part of rsyslog.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

         http://www.apache.org/licenses/LICENSE-2.0
         -or-
         see COPYING.ASL20 in the source distribution

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
"""

import sys
import json

# skeleton config parameters
# currently none

# App logic global variables
outfile = "" # "define" global var that the app code needs

def onInit():
    """ Do everything that is needed to initialize processing (e.g.
        open files, create handles, connect to systems...)
    """
    # most often, nothing to do here
    global outfile
    outfile = open("/tmp/logfile", "a")

def onReceive(msg):
    """This is the entry point where actual work needs to be done. It receives
       the messge from rsyslog and now needs to examine it, do any processing
       necessary. The to-be-modified properties (one or many) need to be pushed
       back to stdout, in JSON format, with no interim line breaks and a line
       break at the end of the JSON. If no field is to be modified, empty
       json ("{}") needs to be emitted.
       Note that no batching takes place (contrary to the output module skeleton)
       and so each message needs to be fully processed (rsyslog will wait for the
       reply before the next message is pushed to this module).
    """
    msg = json.loads(msg)
    # manipulate data
    outfile.write(str(msg) + "\n")
    outfile.flush()

def onExit():
    """ Do everything that is needed to finish processing (e.g.
        close files, handles, disconnect from systems...). This is
        being called immediately before exiting.
    """
    # most often, nothing to do here
    outfile.close()


"""
-------------------------------------------------------
This is plumbing that DOES NOT need to be CHANGED
-------------------------------------------------------
Implementor's note: Python seems to very agressively
buffer stdouot. The end result was that rsyslog does not
receive the script's messages in a timely manner (sometimes
even never, probably due to races). To prevent this, we
flush stdout after we have done processing. This is especially
important once we get to the point where the plugin does
two-way conversations with rsyslog. Do NOT change this!
See also: https://github.com/rsyslog/rsyslog/issues/22
"""
onInit()
keepRunning = 1
while keepRunning == 1:
    msg = sys.stdin.readline()
    if msg:
        onReceive(msg)
    else: # an empty line means stdin has been closed
        keepRunning = 0
onExit()
sys.stdout.flush() # very important, Python buffers far too much!

関数は3つほど用意されていて、それぞれの意味は

  • onInit : プラグイン起動時にのみ呼び出される
  • onReceive : メッセージを受け取るたびに呼び出される
  • onExit : プラグイン停止時にのみ呼び出される

実際のデータ加工は onReceive 関数で行っている。
本来であれば、デコードした後、 データ処理して別システムと連携すべきなのだけれども、今回はプラグイン連携の動作確認だけが目的なので、データを文字列としてファイル出力する。

このファイルを rsyslog.conf の設定通り、 /tmp/plugin.py として保存。

プラグインの動作確認

rsyslog をリスタート

$ sudo service rsyslog restart
Shutting down system logger:                               [  OK  ]
Starting system logger:                                    [  OK  ]

プロセスを確認すると、 rsyslogd の子プロセスとして plugin.py が起動している。

$ pstree
init-+-auditd---{auditd}
     ...
     |-rsyslogd-+-plugin.py
     |          `-3*[{rsyslogd}]
     |-sshd-+-sshd---sshd---bash---sudo---bash---tail
     |      |-sshd---sshd---bash---pstree
     |      `-sshd---sshd---bash---sudo---bash
     `-udevd

logger 経由で syslog 出力

$ logger test
$ echo 'hello
> > world' | logger

ログを確認

$ tail -f /tmp/logfile でログを確認。

{u'severity': u'info', u'facility': u'syslog', u'timestamp': u'2014-06-29T22:33:11.401791+09:00', u'host': u'cent64a', u'tag': u'rsyslogd:', u'message': u' [origin software="rsyslogd" swVersion="8.2.2" x-pid="27542" x-info="http://www.rsyslog.com"] start'}
{u'severity': u'notice', u'facility': u'user', u'timestamp': u'2014-06-29T22:38:08.160517+09:00', u'host': u'cent64a', u'tag': u'jsmith:', u'message': u' hello'}
{u'severity': u'notice', u'facility': u'user', u'timestamp': u'2014-06-29T22:38:08.161286+09:00', u'host': u'cent64a', u'tag': u'jsmith:', u'message': u' > world'}

とそれっぽく出力されている。

$ yes | logger などとして syslog に負荷をかけると

$ pstree
init-+-auditd---{auditd}
     ...
     |-rsyslogd-+-2*[plugin.py]
     |          `-4*[{rsyslogd}]
...

というように、プラグインが複数プロセスに増えていたので、スケールアウトする何らかの仕組みがある模様。

References

Advertisements
Tagged with: ,
Posted in linux, middleware, python

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Archives
  • RT @__apf__: How to write a research paper: a guide for software engineers & practitioners. docs.google.com/presentation/d… /cc @inwyrd 6 months ago
  • RT @HayatoChiba: 昔、自然と対話しながら数学に打ち込んだら何かを悟れるのではと思いたち、専門書1つだけ持ってパワースポットで名高い奈良の山奥に1週間籠ったことがある。しかし泊まった民宿にドカベンが全巻揃っていたため、水島新司と対話しただけで1週間過ぎた。 それ… 6 months ago
  • RT @googlecloud: Ever wonder what underwater fiber optic internet cables look like? Look no further than this deep dive w/ @NatAndLo: https… 6 months ago
  • @ijin UTC+01:00 な時間帯で生活しています、、、 1 year ago
  • RT @mattcutts: Google's world-class Site Reliability Engineering team wrote a new book: amazon.com/Site-Reliabili… It's about managing produc… 1 year ago
%d bloggers like this: