ダッシュボードでタイムレンジピッカーを使う時に_time以外のフィールドと比較できるようにする方法

# はじめに
Splunkのダッシュボードの入力エレメントの一つに"タイムレンジピッカー"があります。
これはSplunkに取込んだイベントをサーチしたいときに、日付や時間などを指定してサーチするときに使うことができる入力で、特別な設定を入れなくても様々な条件を設定することができます。

![timerangepicker.jpg](https://qiita-image-store.s3.amazonaws.com/0/170600/c1bf7d9d-a643-e847-7265-5e9d4699cd44.jpeg)

このタイムレンジピッカーは、たとえば期間指定してイベントを表示するようなダッシュボードを作る際に役だつエレメントなのですが、イベントの_timeフィールドを使ってサーチするという仕様があるようです。

よって、**イベントに_timeフィールドと別の時間軸を持つフィールドが存在する場合において、タイムレンジピッカーで、そのフィールドを使ったサーチをする**には、ちょっとした工夫が必要になります。たとえば以下のようなケースです。

## イベントに複数の時間軸を持つケース
- ケース1
syslogサーバ経由で取込んでいるアプライアンスのログについて、イベントに"syslogサーバに記録された日時"と"アプライアンス上で記録された日時"が記録されており、_timeフィールドは"syslogサーバに記録された日時"として取り込まれている。
→アプライアンス上で記録された時間でサーチしたい。

- ケース2
あるイベント1件に、"ログイン日時"と"ログアウト日時"がまとめて記録されており、"ログアウト日時"が_timeフィールドとして取込まれている。
→ ログイン日時を指定してサーチしたい。

要するに、1つのイベントに複数の時間軸を持ったフィールドが存在するケースです。
イベントの_timeフィールドの取り方を変えるという方法もありますが、
今回は、そのような既存の構成を変えるのが難しい場合の対応案として、タイムレンジピッカーを用いて_timeフィールド以外のフィールドの時間でサーチする方法について説明します。

## 前提条件
この方法ではタイムレンジピッカーで比較するフィールドの時刻書式がsplunkで認識できる形式(strftime形式)であることが必要です。

#実装方式概要
1. タイムレンジピッカー作成
2. タイムレンジピッカー詳細設定
3. ダッシュボードパネル作成
4. ダッシュボード初期化処理設定
5. 結果確認

# 実装詳細
## タイムレンジピッカー作成
ダッシュボードにタイムレンジピッカーを作ります。
トークン名はなんでもいいですが、今回は"t_time"というトークン名にします。

![sample01.jpg](https://qiita-image-store.s3.amazonaws.com/0/170600/225536bc-2ced-bcc6-ab26-fe0fb8dd9d0f.jpeg)

```python:タイムレンジピッカーのソースコード
    <input type="time" token="t_time">
      <label></label>
      <default>
        <earliest>-24h@h</earliest>
        <latest>now</latest>
      </default>
    </input>
```


## タイムレンジピッカー詳細設定
Splunkのソースコードエディタなどを使ってタイムレンジピッカートークンの詳細設定をします。
具体的には、**タイムレンジピッカーの値が変化したときに、タイムレンジピッカーのearliest,latest値から時間を設定するevalトークンを設定するようなソースコードを記述します。**

ここで一つポイントがあります。
それは、タイムレンジピッカーで全時間や相対時間を設定すると、タイムレンジピッカーのトークンのearliest,latestの値が文字列や空白になることです。よって、タイムレンジピッカーでそれらが選ばれたときに、evalトークンに想定通りの時間が設定されるようif文を記述します。

また、タイムレンジピッカーはプリセットで「今週(平日)」「先週の平日」という選択肢があるため、それが選択されたときに、サーチ対象の日付が平日であるか考慮する必要があります。

これらをふまえたソースコードの例が以下になります。ここではタイムレンジピッカーのトークン名を"t_time"、evalトークンをそれぞれt_earliest,t_latest,t_earliest_weekdayとして定義しています。

※**ソースコードを見やすくするため改行＋インデントを行っておりますが、実際のソースでは改行を削除してください。**

```python:change,condition,evalタグを追加したタイムレンジピッカーのソースコード
<input type="time" token="t_time">
  <label></label>
  <change>
    <condition>
      <eval token="t_earliest">
      if($t_time.earliest$="",
         strptime("1971/01/01 00:00:00.000","%Y/%m/%d %H:%M:%S.%3N"),
         if(isnum($t_time.earliest$),
            $t_time.earliest$,
            if($t_time.earliest$="-6d@w1",
               relative_time(now(),"-7d@w1"),
               relative_time(now(),$t_time.earliest$)
            )
         )
      )
      </eval>
      <eval token="t_latest">
      if($t_time.latest$="",
         strptime("2100/12/31 23:59:59.000","%Y/%m/%d %H:%M:%S.%3N"),
         if(isnum($t_time.latest$),
            $t_time.latest$,
            if($t_time.latest$="-1d@w6",
               relative_time(now(),"-7d@w6"),
               relative_time(now(),$t_time.latest$)
           )
        )
      )
      </eval>
      <eval token="t_earliest_weekday">if(like($t_time.earliest$,"%@w1"),1,0)</eval>
    </condition>
  </change>
  <default>
     <earliest>-24h@h</earliest>
     <latest>now</latest>
   </default>
</input>
```

## ダッシュボードパネル作成
ダッシュボードのパネルに上記のトークンを参照するサーチを作ります。
この時、タイムレンジピッカーで比較したいフィールドが、タイムレンジピッカーで選択した期間に収まっているかチェックするようeval式を作ります。
また、タイムレンジピッカーの時間範囲は_timeフィールドを参照しないため「グローバル」にします。

これらをふまえたサーチ文の例が以下になります。ここでは、サーチするインデックス名を"index_hoge",タイムレンジピッカーと比較したいフィールドをTARGET_FIELDとして定義しています。
※TARGET_FIELDの時刻書式は"%Y/%m/%d %H:%M:%S.%3N"としています。

```python:サーチ文字列の例
index="index_hoge"
| eval target_field_time=strptime('TARGET_FIELD',"%Y/%m/%d %H:%M:%S.%3N")
| eval target_field_time_weekday=strftime(target_field_time,"%w")
| eval flag_term=if(target_field_time > $t_earliest$ AND target_field_time <= $t_latest$,1,0)
| eval flag_term=if($t_earliest_weekday$ =1 AND (target_field_time_weekday = 0 OR target_field_time_weekday = 6),0,flag_term)
| search flag_term=1
```
※ ここではソースの見易さを考慮してeval式を複数行書いていますが、1行にまとめても構いません。

## ダッシュボード初期化処理(initタグによる設定)
今回の方法はタイムレンジピッカーの値が変化したときにevalトークンを設定し、そのevalトークンを使ってイベントをサーチするようにしています。つまり、ダッシュボードを開いた時点ではタイムレンジピッカーの値が変化していないためevalトークンが設定されていない状態です。

よって、タイムレンジピッカーのデフォルトに合わせて<init>タグを用いて
evalトークンの初期値を設定します。以下は、タイムレンジピッカーのデフォルトが"過去24h"の場合のevalトークンの指定例になります。
※t_earliest_weekdayは固定値なのでsetトークンにしています。デフォルト値が平日の場合は1にします。

```python:initトークン
<init>
   <eval token="t_earliest">relative_time(now(),"-24h@h")</eval>
   <eval token="t_latest">now()</eval>
   <set token="t_earliest_weekday">0</set>
</init>
```

## 結果確認
ダッシュボードを保存し、想定通りの結果になっているか確認します。
(1)全時間を選択した状態のサーチ結果
![sample02.jpg](https://qiita-image-store.s3.amazonaws.com/0/170600/e06d5123-61a9-26a2-27e5-9d10b30af2e2.jpeg)

(2)(1)から期間を変更した時のサーチ結果(期間を相対時間で指定)
![sample03.jpg](https://qiita-image-store.s3.amazonaws.com/0/170600/6bd0ae65-35f5-176f-f845-8202a5296295.jpeg)

(3)(1)から期間を変更した時のサーチ結果(期間を絶対時間で指定)
![sample04.jpg](https://qiita-image-store.s3.amazonaws.com/0/170600/399ca9f3-3370-479c-61dc-1f464e9c54bb.jpeg)

## 制約

- 祝日を指定した検索はさらに工夫が必要

Splunk標準の機能でもそうなのですが、タイムレンジピッカーのプリセット項目にある「今週(平日)」または「先週の平日」を指定した場合にイベント日付が祝日であってもサーチ対象になります。
祝日も考慮する場合は、祝日のルックアップテーブルを別途用意して、そのテーブルに存在しないことを突合するような実装が必要になります。

## 終わりに

以上より、_time以外に時間情報を持つイベントをタイムレンジピッカーで抽出できるダッシュボードが作れるようになります。

しかし、当然ながら通常のサーチバーのタイムレンジピッカーで指定する時間範囲は_timeフィールドを判定するため、もし、Splunkのユーザーが上記の実装を行ったダッシュボードを使ってサーチする以外にサーチバーを使ってもサーチするような運用がある場合は、サーチ条件が異なることを理解して頂く必要があると思います。