Splunk の EventingCommand 型のカスタムサーチコマンドを作成します。下の記事では、GeneratingCommand (データ生成型コマンド)を作成しました。
- Splunk カスタムサーチコマンド (custom search command) 作成 ― はじめの一歩 ― - Qiita
- Splunk カスタムサーチコマンド (custom search command) 作成 その2 - Qiita
今回は、EventingCommand を作成します。
"EventingCommand" とは? という方は前の記事の「カスタムサーチコマンドの4つの種類」をご覧ください。
開発環境についても SDK 等の説明は省きます。
ゴール
前回の出力 "hello, world
" を受け取り、すべての文字を大文字に変換するコマンド toupper
を作ってみます。
| generatehello2 | toupper greeting ← "greeting" はフィールド名
eval の upper() に相当するものをフィールド指定で変換するものになります。
Version 1 protocol と Version 2 protocol の違い
始める前に、サーチコマンドのプロトコル Version 1 と Version 2 について書いておきます。
Python のカスタムサーチコマンド作成の紹介記事は色々とありますが、主に以下の手法を使っています。
- 作成したスクリプト(コマンド)を
run
コマンドを用いて実行する。 -
run
を使用せず、内部でsplunk.InterSplunk
モジュールを利用する。
これらは、インプットとアウトプットの処理をどうするかという違いはありますが、基本的な違いはありません。
1つ目は splunk から呼び出し可能であればどのような形のものでも実行できます。
2つ目は splunk.InterSplunk
を使って入力と出力を行います。
これらの利用は、protocol Version 1 あるいは、legacy protocol と呼ばれる従来の方法で呼び出されます。
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 writing custom search commands - Splunk Documentation](https://docs.splunk.com/Documentation/Splunk/latest/Search/Aboutcustomsearchcommands)" より引用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.
v2 になると、Python 以外にもサポートされる言語が増えますが、一番大きな違いはイベント 5万件ごとにプログラムが呼び出されないというところでしょう。逆に言えば、v1 ではイベント 5万件ごとにカスタムサーチコマンドが起動されますので、パフォーマンスに違いが出ます。
5万件を超えないイベントを扱うのであればどちらでも差はありませんが、Splunk ではイベントがすぐに 5万件を超えますので、これから作るのであれば、splunk.InterSPlunk
を使わずに、v2 を使うとよいでしょう。
この記事では、Version 2 protocol を用いたカスタムサーチコマンドを作成します。
準備
- 前回の環境がある場合にはそのまま継続して使用します。
- この記事から始める場合には、GitHub から step1-2 をダウンロードし、
HelloWorld-work
ディレクトリを$SPLUNK_HOME/etc/apps/HelloWorld/
にコピーして Splunk を再起動します。 -
$SPLUNK_HOME/etc/apps/HelloWorld/lib/
を作成し、その下に SDK のsplunklib
をコピーします。
(pip3 install -t lib/ splunk-sdk
でも SDK ライブラリがインストール可能です。)
ディレクトリとファイルの構造は次のようになっています。
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)
#!/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
に修正します。
--- 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
#!/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__)
編集
-
%(command.title())
の部分をすべてToUpper
に修正します。 -
transform()
が必須の本体になりますので、この部分に処理を追加します。 (解説は後述)
--- 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__)
#!/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」を参照してください。
# [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.conf
と default.meta
を設定します。
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.meta
へ searchbnf
について追加設定します。
--- default.meta.orig
+++ default.meta
@@ -1,2 +1,5 @@
[]
access = read: [ * ], write : [ admin ]
+
+[searchbnf]
+export = system
[]
access = read: [ * ], write : [ admin ]
[searchbnf]
export = system
再起動
splunkd
を再起動します2。
sudo ${SPLUNK_HOME}/bin/splunk restart
または、systemctl で設定している場合には
sudo systemctl restart Splunkd
動作確認
サーチ窓で動作を確認します。
1. generatehello2 の確認。
| generatehello2 count=3
| table greeting
greeting
hello, world
hello, world
hello, world
2. toupper の確認。
| generatehello2 count=3
| table greeting
| toupper greeting
greeting
HELLO, WORLD
HELLO, WORLD
HELLO, WORLD
うまく小文字が大文字に変換されました。
まとめ
- 大文字に変換する toupper コマンドを作成しました。
- Protocol Version 2 の利用を推奨します。 (splunk.InterSplunk は使用しない)
- イベントを変換するコマンドは
EventingCommand
クラスを使用します。 - searchbnf.conf を設定することで、ヘルプを表示できるようになります。
- Python2 は 2020年1月1日で EOL を迎えたので、Python3 で動作することを確認しました。
- ソースコードは "Splunk-CustomSearchCommand-FirstStep: Splunk カスタムサーチコマンド (custom search command) 作成 ― はじめの一歩 ― - Qiita のサンプル" にて公開しています。