LoginSignup
2
3

More than 3 years have passed since last update.

Splunk のカスタムサーチコマンド作成 EventingCommand 編

Last updated at Posted at 2020-05-11

Splunk の EventingCommand 型のカスタムサーチコマンドを作成します。下の記事では、GeneratingCommand (データ生成型コマンド)を作成しました。

今回は、EventingCommand を作成します。

"EventingCommand" とは? という方は前の記事の「カスタムサーチコマンドの4つの種類」をご覧ください。
開発環境についても SDK 等の説明は省きます。

ゴール

前回の出力 "hello, world" を受け取り、すべての文字を大文字に変換するコマンド toupper を作ってみます。

SPL 例
| generatehello2 | toupper greeting     ← "greeting" はフィールド名

eval の upper() に相当するものをフィールド指定で変換するものになります。

Version 1 protocol と Version 2 protocol の違い

始める前に、サーチコマンドのプロトコル Version 1 と Version 2 について書いておきます。

Python のカスタムサーチコマンド作成の紹介記事は色々とありますが、主に以下の手法を使っています。

  1. 作成したスクリプト(コマンド)を run コマンドを用いて実行する。
  2. run を使用せず、内部で splunk.InterSplunk モジュールを利用する。

これらは、インプットとアウトプットの処理をどうするかという違いはありますが、基本的な違いはありません。
1つ目は splunk から呼び出し可能であればどのような形のものでも実行できます。
2つ目は splunk.InterSplunk を使って入力と出力を行います。

これらの利用は、protocol Version 1 あるいは、legacy protocol と呼ばれる従来の方法で呼び出されます。

commands.conf.spec
chunked = [true|false]
* If set to "true", this command supports the new "chunked" custom
  search command protocol.
* If set to "true", the only other commands.conf settings supported are
  'is_risky', 'maxwait', 'maxchunksize', 'filename', 'command.arg.<N>', and
  'run_in_preview'.
* If set to "false", this command uses the legacy custom search command
  protocol supported by Intersplunk.py.
* Default: false

"commands.conf - Splunk Documentation" より引用

Version 1 を使用する場合には、commands.conf の設定に chuncked = false を設定するか、書かずにデフォルトに任せて false のままにしておきます。

一方、Version 2 の新しいカスタムサーチコマンドを利用する場合には、splunk.InterSplunk を利用せずに EventingCommand TransformingComannd GeneratingCommand StreamingCommand のクラスを利用します。

Version 1 protocol と Version 2 protocol には次のような違いがあります。

About the protocols

Version 2 protocol

There are significant advantages to using the Version 2 of the Custom Search Command protocol.

  • With the Version 2 protocol, external programs process through an entire set of Splunk search results in one invocation. The external program is invoked once for the entire search, greatly reducing runtime overhead.
  • The Version 2 protocol requires fewer configuration attributes than the Version 1 protocol.
  • Supports non-Python custom search commands, for example C++, Go, Java and JavaScript (Node.js).
  • Support for platform-specific executable files and binaries. You can write custom search commands in compiled languages, such as C++, on multiple platforms.

Version 1 protocol

The Version 1 of the Custom Search Command protocol processes events in groups, using 50,000 events for each group. The external program is invoked and terminated multiple times for large result sets.

"About writing custom search commands - Splunk Documentation" より引用

v2 になると、Python 以外にもサポートされる言語が増えますが、一番大きな違いはイベント 5万件ごとにプログラムが呼び出されないというところでしょう。逆に言えば、v1 ではイベント 5万件ごとにカスタムサーチコマンドが起動されますので、パフォーマンスに違いが出ます。
5万件を超えないイベントを扱うのであればどちらでも差はありませんが、Splunk ではイベントがすぐに 5万件を超えますので、これから作るのであれば、splunk.InterSPlunk を使わずに、v2 を使うとよいでしょう。

この記事では、Version 2 protocol を用いたカスタムサーチコマンドを作成します。

準備

  1. 前回の環境がある場合にはそのまま継続して使用します。
  2. この記事から始める場合には、GitHub から step1-2 をダウンロードし、 HelloWorld-work ディレクトリを $SPLUNK_HOME/etc/apps/HelloWorld/ にコピーして Splunk を再起動します。
  3. $SPLUNK_HOME/etc/apps/HelloWorld/lib/ を作成し、その下に SDKsplunklib をコピーします。
    (pip3 install -t lib/ splunk-sdk でも SDK ライブラリがインストール可能です。)

ディレクトリとファイルの構造は次のようになっています。

${SPLUNK_HOME}/etc/apps/
HelloWorld/
├─ bin
│   ├─ HelloWorld.py
│   ├─ HelloWorld2.py
│   ├─ filter.py
│   ├─ generate.py
│   ├─ report.py
│   └─ stream.py
├─ default
│   ├─ app.conf
│   ├─ commands.conf
│   └─ data
│       └─ ui
│           └─ nav
│               └─ default.xml
├─ lib
│   ├─ splunk_sdk-1.6.12.dist-info
│   │   (pip でインストールした場合にはこのディレクトリが存在します。)
│   └─ splunklib
│       ├─ __init__.py
│       ... (省略)
└─ metadata
    └─ default.meta

開発

ファイルの作成

bin/ 下にある filter.py をコピーして ToUpper.py を作成します。

cd bin/
cp filter.py ToUpper.py

元にした filter.py は、EventingCommand のテンプレートなのですが、メンテナンスされていないようで、そのままでは使用できません。 (SDK v1.6.12)

ToUpper.py
#!/usr/bin/env python

import sys
import os

sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "lib"))
from splunklib.searchcommands import \
    dispatch, StreamingCommand, Configuration, Option, validators


@Configuration()
class %(command.title())Command(EventingCommand):
    """ %(synopsis)

    ##Syntax

    %(syntax)

    ##Description

    %(description)

    """
    def transform(self, events):
       # Put your event transformation code here
       pass

dispatch(%(command.title())Command, sys.argv, sys.stdin, sys.stdout, __name__)

import で StreamingCommand になっている部分を EventingCommand に修正します。

diff
--- filter.py
+++ ToUpper.py
@@ -5,8 +5,8 @@

 sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "lib"))
 from splunklib.searchcommands import \
-    dispatch, StreamingCommand, Configuration, Option, validators
+    dispatch, EventingCommand, Configuration, Option, validators
ToUpper.py 修正後
#!/usr/bin/env python

import sys
import os

sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "lib"))
from splunklib.searchcommands import \
    dispatch, EventingCommand, Configuration, Option, validators


@Configuration()
class %(command.title())Command(EventingCommand):
    """ %(synopsis)

    ##Syntax

    %(syntax)

    ##Description

    %(description)

    """
    def transform(self, events):
       # Put your event transformation code here
       pass

dispatch(%(command.title())Command, sys.argv, sys.stdin, sys.stdout, __name__)

編集

  1. %(command.title()) の部分をすべて ToUpper に修正します。
  2. transform() が必須の本体になりますので、この部分に処理を追加します。 (解説は後述)
diff
--- ToUpper.py.orig
+++ ToUpper.py
@@ -9,7 +9,7 @@


 @Configuration()
-class %(command.title())Command(EventingCommand):
+class ToUpperCommand(EventingCommand):
     """ %(synopsis)

     ##Syntax
@@ -23,6 +23,11 @@
     """
     def transform(self, events):
        # Put your event transformation code here
-       pass
+        for event in events:
+            for field in self.fieldnames:
+                if field in event:
+                    event[field] = event[field].upper()
+            yield event
+        return

-dispatch(%(command.title())Command, sys.argv, sys.stdin, sys.stdout, __name__)
+dispatch(ToUpperCommand, sys.argv, sys.stdin, sys.stdout, __name__)
ToUpper.py 更新後
#!/usr/bin/env python

import sys
import os

sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "lib"))
from splunklib.searchcommands import \
    dispatch, EventingCommand, Configuration, Option, validators


@Configuration()
class ToUpperCommand(EventingCommand):
    """ %(synopsis)

    ##Syntax

    %(syntax)

    ##Description

    %(description)

    """
    def transform(self, events):
       # Put your event transformation code here
        for event in events:
            for field in self.fieldnames:
                if field in event:
                    event[field] = event[field].upper()
            yield event
        return

dispatch(ToUpperCommand, sys.argv, sys.stdin, sys.stdout, __name__)
  • transform()EventingCommand の必須メソッドで、dispatcher によって呼び出される。
  • transform() の第2引数 (ここでは、events) は各イベントのリスト。
  • 引数に指定したフィールド (option=xxxx という形をしていない引数) は、self.fieldnames にリストとして渡される。
  • フィールドの参照および代入は、各イベントの辞書リスト (上では event[field]) 形式で行える。

というのが、EventingCommand の要素です。

上の例では、各イベントに対し (for event in events:) 指定された各フィールド (for field in self.fieldnames:) ごとに、大文字に変換して、代入し直して (event[field] = event[field].upper()) います。

動作確認

コマンドラインによる動作確認

Splunk と切り離したコマンドライン上で動作確認をします。

ローカルに実行するためには、__EXECUTE__ を指定して実行します。

python3 ToUpper.py __EXECUTE__ </dev/nul

何もエラーがなければ成功です。

入力に /dev/null を指定しているので、出力も無いのですが、実は、入力を行っても何も表示されません。

前回作成した HelloWorld2.py を使って、試してみます。

$ python3 HelloWorld2.py __EXECUTE__ count=3 < /dev/null  # HelloWorld2.py 単体での動作確認

_time,__mv__time,greeting,__mv_greeting,_raw,__mv__raw
1589160289.7283704,,"hello, world",,"hello, world",
1589160289.7284214,,"hello, world",,"hello, world",
1589160289.7284305,,"hello, world",,"hello, world",
$ python3 HelloWorld2.py __EXECUTE__ count=3 < /dev/null | python3 ToUpper.py __EXECUTE__ greeting
$                     ← 何も表示されずにコマンドプロンプトに戻ってくる。

注: コマンドラインで表示させる方法を見つけられませんでした。ご存知の方がいらっしゃいましたら、ご教授いただけると幸いです。

ひとまず、エラーの有無は確認できると思います。

サーチによる動作確認

Splunk のサーチコマンドとして動作確認を行います。

設定

提供されたものを修正する場合には、local/ ディレクトリ下にファイルを作成して設定するのですが、今回は「提供者」という立場で、default/ 下のファイルを変更します。

commands.conf

default/commands.conf を編集します。

--- commands.conf.orig
+++ commands.conf
@@ -10,3 +10,8 @@
 filename = HelloWorld2.py
 chunked = true
 python.version = python3
+
+[toupper]
+filename = ToUpper.py
+chunked = true
+python.version = python3
  • Protocol Version 2 を使用しますので、chunked = true を設定します。
  • Python2 は、2020年1月1日で EOL を迎えたので、Python3 を使用します。
  • commands.conf の詳細は「commands.conf - Splunk Documentation」を参照してください。
default/commands.conf
# [commands.conf]($SPLUNK_HOME/etc/system/README/commands.conf.spec)
# Configuration for Search Commands Protocol version 2

[generatehello]
filename = HelloWorld.py
chunked = true
python.version = python3

[generatehello2]
filename = HelloWorld2.py
chunked = true
python.version = python3

[toupper]
filename = ToUpper.py
chunked = true
python.version = python3
searchbnf.conf および default.meta

今回は、Tips として、searchbnf.confdefault.meta を設定します。

searchbnf.conf は新規に作成します。

searchbnf.conf
[toupper-command]
syntax = toupper <field-list>
description = toupper command to transfer into upper cases by the specify fields.\
        This is a long description.
shortdesc = make fields contents to upper cases.
usage = public

参照: searchbnf.conf - Splunk Documentation

  • [toupper-command] の記述のうち、toupper 部分は commands.conf で設定したサーチコマンド名、-command 部分は固定です1
  • この設定を行うと、サーチ窓に入力したときにヘルプを表示できます。 (SPL エディタで「完全」か「コンパクト」を設定している時)
  • usage = public を設定し忘れると、ヘルプは表示されません。

metadata/default.metasearchbnf について追加設定します。

--- default.meta.orig
+++ default.meta
@@ -1,2 +1,5 @@
 []
 access = read: [ * ], write : [ admin ]
+
+[searchbnf]
+export = system
metadata/default.meta
[]
access = read: [ * ], write : [ admin ]

[searchbnf]
export = system

再起動

splunkd を再起動します2

sudo ${SPLUNK_HOME}/bin/splunk restart

または、systemctl で設定している場合には

sudo systemctl restart Splunkd

動作確認

サーチ窓で動作を確認します。

1. generatehello2 の確認。
SPL文
| generatehello2 count=3
| table greeting
実行結果
greeting
hello, world
hello, world
hello, world
2. toupper の確認。
SPL文
| generatehello2 count=3
| table greeting
| toupper greeting
実行結果
greeting
HELLO, WORLD
HELLO, WORLD
HELLO, WORLD

うまく小文字が大文字に変換されました。

まとめ


  1. 余談ですが、UNIX の roff の慣例では、可変部をイタリックで記載します。MarkDown 記法や HTML の弊害は、`quote` の中に可変部を示す イタリック を書けないことでしょうか。どこが可変文字でどこが固定文字なのか区別がつかないので、毎回説明しなければなりません。逆に、説明がないとドキュメントだけではわかりづらいことがあります。 

  2. debug/refresh で有効になるかどうかは未確認。 

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3