実施環境
CentOS 8-Stream
Splunk Free 8.2.2
Python 3.7.10
splunk-sdk-python 1.7.3
0. 概要
前回まで、カスタムコマンドには最低限の機能しか搭載していませんでした。
今回は最初に作成した以下のプログラムに対して、入出力に関する機能を追加していってみます。
#!/usr/bin/env python
import sys
sys.path.insert(0, "/opt/splunk/etc/apps/search/lib")
from splunklib.searchcommands import \
dispatch, GeneratingCommand, Configuration
@Configuration()
class test_class(GeneratingCommand):
def generate(self):
return [{"test_field": "Hello World !"}]
dispatch(test_class)
1. 出力の分割
上記のプログラムでは「 Hello World ! 」は1回しか出力していませんでしたが、これを3回にしてみます。
#!/usr/bin/env python
import sys
sys.path.insert(0, "/opt/splunk/etc/apps/search/lib")
from splunklib.searchcommands import \
dispatch, GeneratingCommand, Configuration
@Configuration()
class test_class(GeneratingCommand):
def generate(self):
test_result = []
for i in range(3):
test_result.append({"test_field": "Hello World !"})
return test_result
dispatch(test_class)
上記の通り結果のリストに辞書形式のイベントを追加していけば実現はできます。
ただ、上の書き方だと最後まで処理が終わってからでないと結果が返されません。
性能的には順次結果を返してほしいところです。
では、順次結果を返すにはどうすればよいか。
答えは、「 return 」の代わりに「 yield 」を使用する、です。
#!/usr/bin/env python
import sys
sys.path.insert(0, "/opt/splunk/etc/apps/search/lib")
from splunklib.searchcommands import \
dispatch, GeneratingCommand, Configuration
@Configuration()
class test_class(GeneratingCommand):
def generate(self):
for i in range(3):
yield {"test_field": "Hello World !"}
return
dispatch(test_class)
「 yield 」の場合は辞書形式のイベントをリスト化せず直接返すことに注意してください。
「 return 」は上記で残していますが、「 yield 」を使用したメソッド内では単にメソッドを終了する意味しかないので消しても問題ありません。
実際に SPL で使用してみました。
| TestCommand
| table test_field
なお、注意点が1つ。
yield で返す辞書のキーは、全て同じでなくてはなりません。
キーが異なる辞書を同じコマンド内で返すと、上手くイベントを返せません。
#!/usr/bin/env python
import sys
sys.path.insert(0, "/opt/splunk/etc/apps/search/lib")
from splunklib.searchcommands import \
dispatch, GeneratingCommand, Configuration
@Configuration()
class test_class(GeneratingCommand):
def generate(self):
for i in range(3):
yield {"test_field1": "Hello World 1 !"}
yield {"test_field2": "Hello World 2 !"}
return
dispatch(test_class)
| TestCommand
| table test_field1, test_field2
どうしても異なるキーを使いたい場合は、以下のように使用しないキーを「 None 」で埋めることで対応できます。
#!/usr/bin/env python
import sys
sys.path.insert(0, "/opt/splunk/etc/apps/search/lib")
from splunklib.searchcommands import \
dispatch, GeneratingCommand, Configuration
@Configuration()
class test_class(GeneratingCommand):
def generate(self):
for i in range(3):
yield {"test_field1": "Hello World 1 !", \
"test_field2": None}
yield {"test_field1": None, \
"test_field2": "Hello World 2 !"}
return
dispatch(test_class)
| TestCommand
| table test_field1, test_field2
ちなみに、「 generate 」以外のメソッドからイベントを返したい場合は、以下のように「 yield from 」で「 yield 」を「 generate 」まで通せば実現できます。
#!/usr/bin/env python
import sys
sys.path.insert(0, "/opt/splunk/etc/apps/search/lib")
from splunklib.searchcommands import \
dispatch, GeneratingCommand, Configuration
@Configuration()
class test_class(GeneratingCommand):
def test_func1(self):
yield from self.test_func2()
def test_func2(self):
yield {"test_field": "Hello World !"}
def generate(self):
for i in range(3):
yield from self.test_func1()
return
dispatch(test_class)
2. 引数の取得
引数を取得する場合は、「 Option 」を使用します。
ライブラリからの読み込み対象に「 Option 」を追加し、それをクラス変数に代入します。
引数名は特に指定がなければ代入先のクラス変数名と同じになります。
#!/usr/bin/env python
import sys
sys.path.insert(0, "/opt/splunk/etc/apps/search/lib")
from splunklib.searchcommands import \
dispatch, GeneratingCommand, Configuration, Option
@Configuration()
class test_class(GeneratingCommand):
testarg = Option()
def generate(self):
for i in range(int(self.testarg)):
yield {"test_field": "Hello World !"}
return
dispatch(test_class)
実際に SPL で使用してみました。
| TestCommand testarg=5
| table test_field
ちなみに、「 Option 」には引数も指定可能です。
主に使用する引数は以下のようなところでしょうか。
- name : 変数名と引数名を変える場合に使用
- require : 変数が必須な場合は True 、そうでない場合は False を指定
- default : 変数省略時のデフォルト値を指定
- validate : 変数のフォーマットを指定、使用する場合は「 validators 」の読み込みが必須
実際に使用すると以下のようになります。
#!/usr/bin/env python
import sys
sys.path.insert(0, "/opt/splunk/etc/apps/search/lib")
from splunklib.searchcommands import \
dispatch, GeneratingCommand, Configuration, Option, validators
@Configuration()
class test_class(GeneratingCommand):
test_count = \
Option(name="testarg", \
require=False, default=1, \
validate=validators.Integer())
def generate(self):
for i in range(int(self.test_count)):
yield {"test_field": "Hello World !"}
return
dispatch(test_class)
3. _time , _raw の追加
ここまでカスタムコマンドを使用する際には table コマンドを併用してきました。
では table コマンドを取り除くとどのような画面になるでしょうか。
| TestCommand testarg=5
このように、一見何も出力されていないように見えてしまいます。
これは、「イベント」画面で右下に表示されるのが「 _time 」「 _raw 」の2つの内部的なフィールドであり、上記のカスタムコマンドだとこれらが生成されないからです。
ちゃんと確認すれば、指定したフィールド自体はきちんと生成されていることがわかるはずです。
「イベント」画面で表示したい場合は、「 _time 」「 _raw 」を明示的に指定すればよいです。
なお、「 _time 」は float 型の UNIX 時間で指定する必要があります。
#!/usr/bin/env python
import sys, datetime
sys.path.insert(0, "/opt/splunk/etc/apps/search/lib")
from splunklib.searchcommands import \
dispatch, GeneratingCommand, Configuration, Option
@Configuration()
class test_class(GeneratingCommand):
testarg = Option()
def generate(self):
for i in range(int(self.testarg)):
yield {"_time": \
datetime.datetime(2000,2,29,9,30,21).timestamp(), \
"_raw": "Hello World !"}
return
dispatch(test_class)
実際に実行してみました。
| TestCommand testarg=5
「時間」と「イベント」がきちんと出力されていることがわかります。
今回は入出力について機能を追加してみました。
次回はそれ以外にも機能を追加してみます。