Splunk | SPL
SPL(Search Processing Language)完全ガイド — Splunkによるデータ分析の全容
はじめに
SPL(Search Processing Language)は、Splunkプラットフォームにおけるデータの検索、変換、分析、可視化を行うための専用クエリ言語である。マシンデータやログデータから価値あるインサイトを抽出するための強力かつ柔軟な構文を提供し、SRE・セキュリティ・ビジネスインテリジェンスなど、幅広い分野で活用されている。
SPLはUnixのパイプライン概念を採用しており、検索コマンドをパイプ(|)で連結して、データを段階的にフィルタリング、変換、集計する。SQLに精通したユーザーにとっては直感的に理解しやすい構造だが、時系列データの分析に特化した独自の機能群を備えている。
本記事では、SPLの基本構文からデータモデル、主要コマンド、統計関数、サブサーチ、ルックアップ、マクロ、ダッシュボード連携、パフォーマンス最適化、そして実践的なSREユースケースまで、SPLの全容を体系的に解説する。
第1章: Splunkアーキテクチャの基礎
1.1 Splunkのデータパイプライン
データソース → [入力] → [パーシング] → [インデキシング] → [サーチ]
↓ ↓ ↓ ↓
フォワーダー 行分割 インデックス SPLクエリ
(UF/HF) タイムスタンプ 圧縮・保存 結果表示
フィールド抽出
| コンポーネント | 役割 |
|---|---|
| Universal Forwarder (UF) | データ収集・転送(軽量) |
| Heavy Forwarder (HF) | データ収集・パーシング・転送 |
| Indexer | データのインデキシング・保存・検索実行 |
| Search Head | ユーザーインターフェース・SPL実行・結果統合 |
| Cluster Master / Manager | インデクサークラスタの管理 |
| Deployment Server | フォワーダーの設定配布 |
| License Server | ライセンス管理 |
1.2 インデックス構造
インデックス(例: main, web_logs, security)
└── バケット
├── Hot Bucket — 書き込み中(高速SSD推奨)
├── Warm Bucket — 読み取り専用(SSD/HDD)
├── Cold Bucket — アーカイブ(HDD/大容量ストレージ)
└── Frozen Bucket — 削除またはアーカイブ
# indexes.conf の設定例
[web_logs]
homePath = $SPLUNK_DB/web_logs/db
coldPath = $SPLUNK_DB/web_logs/colddb
thawedPath = $SPLUNK_DB/web_logs/thaweddb
maxDataSize = auto_high_volume
maxHotBuckets = 10
maxWarmDBCount = 300
frozenTimePeriodInSecs = 7776000 # 90日
1.3 フィールドとイベント
Splunkのデータはイベントとして保存され、各イベントは複数のフィールドを持つ。
デフォルトフィールド:
| フィールド | 説明 |
|---|---|
_time | イベントのタイムスタンプ(エポック秒) |
_raw | イベントの生データ(元のログ行) |
host | データソースのホスト名 |
source | データソースのパス/名前 |
sourcetype | データの形式(例: access_combined, syslog) |
index | イベントが保存されているインデックス |
_indextime | インデックスに格納された時刻 |
splunk_server | 検索を実行したサーバー |
linecount | イベント内の行数 |
第2章: SPLの基本構文
2.1 基本的な検索
# キーワード検索
error
# 複数キーワード(AND条件 — 暗黙的)
error login failed
# 明示的なブール演算
error AND (login OR authentication) NOT timeout
# フィールド値での検索
status=500
host="web-server-01"
sourcetype=access_combined
# ワイルドカード
source="/var/log/*.log"
host=web-*
status=4*
# 時間範囲の指定
index=web_logs earliest=-24h latest=now
index=web_logs earliest="2024-01-01T00:00:00" latest="2024-01-02T00:00:00"
index=web_logs earliest=-7d@d latest=@d # 過去7日間(日単位でスナップ)
2.2 時間修飾子
| 修飾子 | 説明 | 例 |
|---|---|---|
s | 秒 | -30s |
m | 分 | -15m |
h | 時間 | -4h |
d | 日 | -7d |
w | 週 | -1w |
mon | 月 | -3mon |
y | 年 | -1y |
@ | スナップ(切り捨て) | -1d@d(昨日の0:00) |
# 今日の0:00から現在まで
earliest=@d latest=now
# 先週月曜から先週日曜まで
earliest=-1w@w latest=@w
# 過去1時間(分単位でスナップ)
earliest=-1h@m latest=now
2.3 パイプライン構文
SPLの核心は、コマンドをパイプ(|)で連結するパイプライン構造にある。
検索文 | コマンド1 | コマンド2 | コマンド3
# 例: Webサーバーの500エラーを時間帯別に集計
index=web_logs status=500
| timechart span=1h count AS error_count
| where error_count > 100
| sort -error_count
2.4 コマンドのカテゴリ
| カテゴリ | 説明 | 代表的なコマンド |
|---|---|---|
| 検索 / フィルタ | イベントの取得とフィルタリング | search, where, dedup, regex, head, tail, uniq, multisearch, map, localop, noop |
| 変換 | テーブルへのデータ構造変換 | stats, timechart, chart, top, rare, bin (別名: bucket), xyseries, untable, gauge, trendline, addtotals |
| レポート / 集約 | 統計的な集約 | stats, eventstats, streamstats, mstats, tstats, geostats, sistats, sichart, sitimechart |
| フィールド操作 | フィールドの作成・変更・抽出 | eval, rename, fields, rex, spath, convert, fillnull, replace, fieldformat, multikv, xmlkv, kvform, extract (別名: kv), reltime, accum |
| ルックアップ | 外部データによるエンリッチメント | lookup, inputlookup, outputlookup, geom |
| サブサーチ / 結合 | ネスト検索と結果の統合 | [search ...] (サブサーチ), append, appendcols, appendpipe, join, selfjoin, set (union/intersect/diff), multisearch |
| トランザクション | セッション/フロー単位のイベントグループ化 | transaction, concurrency |
| 並び替え / 制限 | 結果のソートと件数制限 | sort, reverse, head, tail, sample, dedup |
| 出力 / 表示 | 結果のフォーマットとエクスポート | table, fields, rename, fieldformat, addtotals, addcoltotals, outputcsv, outputlookup, collect, sendemail, tojson, highlight, iconify |
| 文字列関数 | eval内の文字列操作 | lower(), upper(), trim(), ltrim(), rtrim(), substr(), len(), replace(), split(), urldecode(), printf(), tostring() |
| マルチバリュー | 多値フィールド操作 | mvexpand, mvcombine, mvindex(), mvcount(), mvdedup(), mvfind(), mvappend(), mvzip(), mvjoin(), mvrange(), mvsort(), nomv, makemv |
| 時間関数 | eval内の時間操作 | now(), time(), strftime(), strptime(), relative_time() |
| 数学 / 比較 | eval内の数値演算 | round(), ceil(), floor(), abs(), sqrt(), pow(), log(), exp(), min(), max(), exact(), pi(), sigfig(), tonumber() |
| 条件分岐 | eval内の条件ロジック | if(), case(), coalesce(), nullif(), validate(), match(), like(), cidrmatch(), in(), true(), false(), null() |
| 型 / 検査 | 型チェックと変換 | isnull(), isnotnull(), isint(), isstr(), isnum(), typeof(), tostring(), tonumber() |
| 統計関数 | statsで使用可能な関数 | count, dc(), sum(), avg(), median(), min(), max(), stdev(), var(), range(), mode(), perc<N>(), exactperc<N>(), upperperc<N>(), values(), list(), first(), last(), earliest(), latest(), rate() |
| 予測 / ML | 異常検知と予測 | predict, anomalydetection, anomalousvalue, cluster, kmeans, x11, arules |
| メタデータ / 検査 | インデックスとフィールドのメタデータ | metadata, fieldsummary, typelearner, eventcount, dbinspect, datamodel, rest, audit |
第3章: 主要コマンド詳解
3.1 検索・フィルタリングコマンド
search / where
# search — キーワード・フィールドベースの検索
index=web_logs status=500 | search uri="/api/*"
# where — eval式ベースのフィルタリング(大文字小文字区別)
index=web_logs | where status >= 400 AND status < 500
index=web_logs | where like(uri, "/api/%")
index=web_logs | where match(user_agent, "(?i)bot|crawler|spider")
index=web_logs | where response_time > avg_response_time * 2
# searchとwhereの違い:
# search: フィールド値の比較は大文字小文字を区別しない、ワイルドカード使用可
# where: eval式を使用、大文字小文字を区別、関数が使用可能
dedup — 重複排除
# 各ホストの最新イベントのみ
index=web_logs | dedup host
# 複数フィールドでの重複排除
index=web_logs | dedup host, status
# 各グループの上位N件を保持
index=web_logs | dedup 5 host sortby -_time
head / tail
# 最初の10件
index=web_logs | head 10
# 最後の10件
index=web_logs | tail 10
3.2 統計コマンド
stats — 統計集計
# 基本的な統計
index=web_logs | stats count
# グループ別集計
index=web_logs | stats count BY status
index=web_logs | stats count BY host, status
# 複数の統計関数
index=web_logs
| stats count AS total_requests,
avg(response_time) AS avg_rt,
median(response_time) AS median_rt,
perc95(response_time) AS p95_rt,
perc99(response_time) AS p99_rt,
max(response_time) AS max_rt,
sum(bytes) AS total_bytes,
dc(client_ip) AS unique_clients
BY host
# 条件付き統計
index=web_logs
| stats count(eval(status>=500)) AS server_errors,
count(eval(status>=400 AND status<500)) AS client_errors,
count(eval(status>=200 AND status<300)) AS success,
count AS total
BY host
| eval error_rate = round(server_errors / total * 100, 2)
主要な統計関数:
| 関数 | 説明 |
|---|---|
count | イベント数 |
dc(field) | ユニーク値の数(Distinct Count) |
sum(field) | 合計 |
avg(field) | 平均 |
median(field) | 中央値 |
mode(field) | 最頻値 |
min(field) / max(field) | 最小値 / 最大値 |
range(field) | 範囲(max - min) |
stdev(field) | 標準偏差 |
var(field) | 分散 |
perc<N>(field) | パーセンタイル(例: perc95, perc99) |
earliest(field) / latest(field) | 時間的に最初/最後の値 |
first(field) / last(field) | 最初/最後の値 |
list(field) | 値のリスト |
values(field) | ユニーク値のリスト |
timechart — 時系列集計
# 時間帯別のリクエスト数
index=web_logs | timechart span=5m count
# ステータスコード別の時系列
index=web_logs | timechart span=1h count BY status
# レスポンスタイムの時系列
index=web_logs
| timechart span=5m avg(response_time) AS avg_rt,
perc95(response_time) AS p95_rt,
perc99(response_time) AS p99_rt
# 固定バケット数
index=web_logs | timechart bins=24 count
# NULL値の処理
index=web_logs | timechart span=1h count BY host useother=false usenull=false
chart — 多次元集計
# ホスト別・ステータス別のリクエスト数
index=web_logs | chart count BY host, status
# X軸をフィールド値に
index=web_logs | chart avg(response_time) OVER uri BY host
# 上位N件のみ表示
index=web_logs | chart count BY status limit=5 useother=true
top / rare
# 上位10件のURI
index=web_logs | top limit=10 uri
# ホスト別の上位ステータスコード
index=web_logs | top status BY host limit=5
# 最も稀なユーザーエージェント
index=web_logs | rare user_agent limit=20
# カスタム出力
index=web_logs | top limit=10 uri showperc=true showcount=true
eventstats — イベントレベルの統計
# 各イベントにグローバル平均を追加
index=web_logs
| eventstats avg(response_time) AS global_avg_rt
| where response_time > global_avg_rt * 3
| table _time, host, uri, response_time, global_avg_rt
# グループ別の統計を各イベントに追加
index=web_logs
| eventstats avg(response_time) AS host_avg_rt BY host
| eval deviation = response_time - host_avg_rt
| where deviation > 1000
streamstats — ストリーミング統計
# 移動平均(過去10件)
index=web_logs
| streamstats avg(response_time) AS moving_avg window=10
# 累積合計
index=web_logs
| streamstats sum(bytes) AS cumulative_bytes
# ランキング
index=web_logs
| sort -response_time
| streamstats count AS rank
| where rank <= 10
# 前のイベントとの差分
index=web_logs host="web-01" | sort _time
| streamstats current=false last(_time) AS prev_time window=1
| eval time_gap = _time - prev_time
| where time_gap > 300
3.3 フィールド操作コマンド
eval — フィールドの計算・作成
# 基本的な計算
| eval response_time_sec = response_time / 1000
| eval error_rate = round(errors / total * 100, 2)
# 条件分岐(if)
| eval severity = if(status >= 500, "critical", if(status >= 400, "warning", "ok"))
# 条件分岐(case)
| eval category = case(
status >= 500, "Server Error",
status >= 400, "Client Error",
status >= 300, "Redirection",
status >= 200, "Success",
true(), "Unknown"
)
# 文字列操作
| eval domain = lower(host)
| eval short_uri = substr(uri, 1, 50)
| eval log_msg = host . " - " . status . " - " . uri
| eval uri_parts = split(uri, "/")
| eval api_version = mvindex(uri_parts, 2)
# 正規表現
| eval is_api = if(match(uri, "^/api/v\d+/"), "yes", "no")
| eval user_id = replace(uri, ".*/users/(\d+).*", "\1")
# 時間操作
| eval event_hour = strftime(_time, "%H")
| eval event_date = strftime(_time, "%Y-%m-%d")
| eval epoch_time = strptime(date_str, "%Y-%m-%d %H:%M:%S")
| eval time_diff = now() - _time
| eval age_hours = round(time_diff / 3600, 1)
# 型変換
| eval status_str = tostring(status)
| eval bytes_num = tonumber(bytes)
# NULL / 空値の処理
| eval value = coalesce(field1, field2, "default")
| eval has_error = if(isnotnull(error_msg), "yes", "no")
# マルチバリューフィールド
| eval tags = mvappend(tag1, tag2, tag3)
| eval unique_tags = mvdedup(tags)
| eval tag_count = mvcount(tags)
| eval has_critical = if(mvfind(tags, "critical") >= 0, "yes", "no")
rex — 正規表現によるフィールド抽出
# 名前付きグループで抽出
index=web_logs
| rex field=_raw "(?<client_ip>\d+\.\d+\.\d+\.\d+)\s+.*?\"(?<method>\w+)\s+(?<uri>\S+)"
# 複数フィールドの抽出
index=web_logs sourcetype=syslog
| rex "(?<log_level>\w+)\s+\[(?<thread_name>[^\]]+)\]\s+(?<class_name>\S+)\s+-\s+(?<message>.*)"
# sedモード(置換)
| rex field=uri mode=sed "s/\/users\/\d+/\/users\/{id}/g"
| rex field=email mode=sed "s/(.{3}).*@/\1***@/"
rename / fields / table
# フィールド名の変更
| rename response_time AS "Response Time (ms)", client_ip AS "Client IP"
# フィールドの選択(不要なフィールドを除外)
| fields host, status, response_time, uri
| fields - _raw, _time, linecount # 除外
# テーブル表示
| table _time, host, status, uri, response_time
spath — 構造化データ(JSON/XML)のパース
# JSONの自動パース
index=app_logs sourcetype=json
| spath
# 特定のフィールドを抽出
| spath input=_raw path=response.status output=api_status
| spath input=_raw path=request.headers{}.name output=header_names
# ネストされたJSONの操作
| spath path=metadata.tags{}
| mvexpand tags
第4章: 高度なSPL機能
4.1 サブサーチ
サブサーチは角括弧 [...] で囲まれたネストされた検索で、外部検索の条件として使用される。
# 過去1時間にエラーが多いホストのログを検索
index=web_logs
[search index=web_logs status=500 earliest=-1h
| stats count BY host
| where count > 100
| fields host]
# 最もレスポンスタイムが長いURIの詳細
index=web_logs
[search index=web_logs earliest=-1h
| stats avg(response_time) AS avg_rt BY uri
| sort -avg_rt
| head 5
| fields uri]
# サブサーチの結果をフォーマット
index=web_logs
[search index=alerts severity="critical"
| fields host
| format]
サブサーチの制限事項:
- デフォルトのタイムアウト: 60秒
- デフォルトの最大結果数: 10,000件
- ネストは最大で数レベルまで(パフォーマンスに注意)
4.2 join — テーブル結合
# 内部結合
index=web_logs
| stats count AS request_count BY host
| join host [search index=server_metrics | stats avg(cpu_usage) AS avg_cpu BY host]
# 左外部結合
index=web_logs
| stats count BY client_ip
| join type=left client_ip
[search index=threat_intel | fields client_ip, threat_level, category]
# 注意: joinは大規模データでパフォーマンスが低下する
# 代替手段: lookup, stats + append + stats, eventstats
4.3 lookup — 外部データとの結合
# CSVルックアップの使用
| lookup geo_lookup client_ip AS src_ip OUTPUT country, city, latitude, longitude
# ルックアップファイルの入出力
| inputlookup server_inventory.csv
| inputlookup server_inventory.csv WHERE role="web"
| outputlookup threat_indicators.csv
# 自動ルックアップ(transforms.confで設定済み)
# props.conf:
# [access_combined]
# LOOKUP-geo = geo_lookup client_ip AS clientip OUTPUT country city
# KVストアルックアップ
| inputlookup kvstore_collection
| outputlookup kvstore_collection append=true
# transforms.conf — ルックアップ定義
[geo_lookup]
filename = geo_database.csv
match_type = CIDR(client_ip)
max_matches = 1
[server_inventory]
filename = server_inventory.csv
default_match = unknown
case_sensitive_match = false
# KVストア定義(collections.conf)
[kvstore_alerts]
field.alert_id = string
field.host = string
field.severity = number
field.timestamp = time
4.4 マクロ
再利用可能な検索スニペット。macros.confで定義する。
# macros.conf
[web_errors]
definition = index=web_logs (status>=400)
[web_errors(1)]
definition = index=web_logs (status>=$min_status$)
args = min_status
[error_rate(2)]
definition = stats count(eval(status>=$error_threshold$)) AS errors, count AS total BY $group_by$ | eval error_rate=round(errors/total*100, 2)
args = error_threshold, group_by
[sre_slo_compliance]
definition = stats count(eval(response_time<=$threshold$)) AS good, count AS total | eval slo_compliance=round(good/total*100, 4)
args = threshold
# マクロの使用(バッククォートで囲む)
`web_errors`
| stats count BY host
`web_errors(500)`
| timechart span=1h count
`error_rate(500, host)`
# SLO計算
index=web_logs
| `sre_slo_compliance(2000)`
4.5 トランザクション
関連するイベントをグループ化して1つのトランザクションとしてまとめる。
# セッション単位のトランザクション
index=web_logs
| transaction session_id maxspan=30m maxpause=5m
| stats avg(duration) AS avg_session_duration,
avg(eventcount) AS avg_pages_per_session
# リクエスト-レスポンスのペアリング
index=app_logs
| transaction request_id startswith="request_start" endswith="request_end"
| eval duration = duration
| where duration > 5
# 注意: transactionはメモリ集約型 — 大規模データにはstatsを推奨
# 代替:
index=app_logs
| stats min(_time) AS start_time, max(_time) AS end_time,
values(event_type) AS events, count BY request_id
| eval duration = end_time - start_time
4.6 predict / anomalydetection — 予測と異常検知
# 時系列予測
index=web_logs
| timechart span=1h count AS requests
| predict requests AS predicted_requests algorithm=LLP5 future_timespan=24
| eval lower_bound = predicted_requests - 2*stdev
| eval upper_bound = predicted_requests + 2*stdev
# 異常検知
index=web_logs
| timechart span=5m count AS requests
| anomalydetection requests action=annotate pthresh=0.01
# 外れ値検出
index=web_logs
| stats avg(response_time) AS avg_rt, stdev(response_time) AS stdev_rt BY host
| join host [search index=web_logs | fields host, response_time, _time, uri]
| where response_time > avg_rt + 3 * stdev_rt
| table _time, host, uri, response_time, avg_rt
4.7 map — イテレーティブサーチ
# 各ホストに対して個別の検索を実行
index=web_logs | stats count BY host | head 5
| map search="search index=web_logs host=$host$ status=500 | stats count AS error_count"
maxsearches=10
# パラメータ化された検索
| inputlookup critical_servers.csv
| map search="search index=server_metrics host=$hostname$ earliest=-1h | stats avg(cpu_usage) AS avg_cpu, avg(memory_usage) AS avg_mem"
第5章: SRE実践ユースケース
5.1 SLI/SLO モニタリング
# 可用性SLO(99.9%)の計算
index=web_logs earliest=-30d
| stats count(eval(status<500)) AS good_requests,
count AS total_requests
| eval availability = round(good_requests / total_requests * 100, 4)
| eval slo_target = 99.9
| eval error_budget_total = round(total_requests * (1 - slo_target/100), 0)
| eval error_budget_used = total_requests - good_requests
| eval error_budget_remaining = error_budget_total - error_budget_used
| eval error_budget_pct = round(error_budget_used / error_budget_total * 100, 2)
# レイテンシSLO(P95 < 2秒)
index=web_logs earliest=-7d
| bin _time span=1h
| stats perc95(response_time) AS p95_latency,
count(eval(response_time<=2000)) AS good,
count AS total
BY _time
| eval slo_met = if(p95_latency <= 2000, 1, 0)
| stats sum(slo_met) AS hours_met, count AS total_hours
| eval compliance = round(hours_met / total_hours * 100, 2)
# エラーバジェットの時系列推移
index=web_logs earliest=-30d
| timechart span=1d count(eval(status>=500)) AS daily_errors, count AS daily_total
| streamstats sum(daily_errors) AS cumulative_errors, sum(daily_total) AS cumulative_total
| eval error_budget_total = cumulative_total * 0.001
| eval error_budget_remaining = error_budget_total - cumulative_errors
| eval budget_pct_remaining = round(error_budget_remaining / error_budget_total * 100, 2)
5.2 インシデント検知と分析
# エラースパイクの検出
index=web_logs
| timechart span=5m count(eval(status>=500)) AS errors
| eventstats avg(errors) AS avg_errors, stdev(errors) AS stdev_errors
| eval threshold = avg_errors + 3 * stdev_errors
| where errors > threshold
| eval spike_severity = case(
errors > avg_errors + 5 * stdev_errors, "critical",
errors > avg_errors + 3 * stdev_errors, "warning",
true(), "info"
)
# レイテンシ劣化の検出
index=web_logs
| timechart span=5m perc95(response_time) AS p95
| predict p95 AS predicted algorithm=LLP5
| eval anomaly = if(p95 > predicted * 1.5, "degraded", "normal")
| where anomaly="degraded"
# 依存サービスの障害影響分析
index=app_logs error
| rex "(?<dependency>\w+)_(timeout|connection_refused|unavailable)"
| stats count BY dependency
| sort -count
| head 10
# 変更とインシデントの相関分析
index=deploy_logs "deployment completed"
| eval deploy_time = _time
| join type=left host
[search index=web_logs status=500 | timechart span=5m count BY host]
5.3 キャパシティプランニング
# CPU使用率のトレンド予測
index=server_metrics metric_name=cpu.usage
| timechart span=1d avg(value) AS avg_cpu BY host
| predict avg_cpu AS predicted_cpu future_timespan=30
# ディスク使用量の予測
index=server_metrics metric_name=disk.usage_pct
| timechart span=1d max(value) AS disk_usage BY host
| predict disk_usage AS predicted future_timespan=90
| where predicted > 80
| eval days_until_80 = "check trend"
# リクエスト増加率
index=web_logs
| timechart span=1d count AS daily_requests
| trendline sma5(daily_requests) AS trend
| eval growth_rate = round((daily_requests - trend) / trend * 100, 2)
5.4 アラート設定
# エラーレートが閾値を超えた場合のアラート
index=web_logs earliest=-5m
| stats count(eval(status>=500)) AS errors, count AS total
| eval error_rate = errors / total * 100
| where error_rate > 1
# P99レイテンシが閾値を超えた場合
index=web_logs earliest=-5m
| stats perc99(response_time) AS p99
| where p99 > 5000
# トラフィック急減の検出(サービスダウンの可能性)
index=web_logs earliest=-10m latest=-5m
| stats count AS recent_count
| appendcols
[search index=web_logs earliest=-15m latest=-10m | stats count AS previous_count]
| eval drop_pct = round((previous_count - recent_count) / previous_count * 100, 2)
| where drop_pct > 50
5.5 セキュリティ分析
# ブルートフォース攻撃の検出
index=auth_logs action=failed
| stats count AS failed_attempts, dc(user) AS unique_users BY src_ip
| where failed_attempts > 50
| sort -failed_attempts
# 異常なアクセスパターン
index=web_logs
| stats count BY client_ip, uri
| eventstats avg(count) AS avg_count, stdev(count) AS stdev_count BY uri
| where count > avg_count + 3 * stdev_count
| table client_ip, uri, count, avg_count
# データ流出の検出(大量ダウンロード)
index=web_logs method=GET status=200
| stats sum(bytes) AS total_bytes BY client_ip
| eval total_gb = round(total_bytes / 1073741824, 2)
| where total_gb > 10
| sort -total_gb
第6章: パフォーマンス最適化
6.1 検索の最適化原則
1. 可能な限り早い段階でデータを絞り込む
2. インデックス時のフィールドを活用する
3. ワイルドカードの先頭使用を避ける
4. 時間範囲を最小限にする
5. 不要なフィールドを早期に除外する
# 悪い例 ❌
index=* error | search host="web-*" | stats count BY source
# 良い例 ✅
index=web_logs host="web-*" error | stats count BY source
# 悪い例 ❌(先頭ワイルドカード)
index=web_logs uri="*login"
# 良い例 ✅
index=web_logs uri="*/login" OR uri="/login"
# 悪い例 ❌(不要なフィールドを保持)
index=web_logs | stats count BY host
# 良い例 ✅
index=web_logs | fields host | stats count BY host
6.2 tstats — 高速統計(インデックス時フィールド)
# tstatsは通常のstatsより最大100倍高速
| tstats count WHERE index=web_logs BY host, sourcetype
# 時系列集計(timechartの代替)
| tstats count WHERE index=web_logs BY _time span=1h, host
| xyseries _time host count
# データモデルとの連携
| tstats count FROM datamodel=Web WHERE Web.status>=500 BY Web.src, Web.dest
6.3 statsとjoinの代替パターン
# joinの代わりにstatsを使用
# 悪い例 ❌
index=web_logs | stats count BY host
| join host [search index=server_metrics | stats avg(cpu) BY host]
# 良い例 ✅(append + stats)
index=web_logs | stats count AS request_count BY host
| append [search index=server_metrics | stats avg(cpu) AS avg_cpu BY host]
| stats values(request_count) AS request_count, values(avg_cpu) AS avg_cpu BY host
# 良い例 ✅(lookup使用)
index=web_logs | stats count AS request_count BY host
| lookup server_inventory.csv hostname AS host OUTPUT role, environment
6.4 サマリーインデックス
頻繁に実行する集計を事前計算して保存する。
# サマリーインデックスへの書き込み(スケジュール検索で定期実行)
index=web_logs earliest=-1h latest=now
| stats count AS total_requests,
count(eval(status>=500)) AS server_errors,
avg(response_time) AS avg_response_time,
perc95(response_time) AS p95_response_time
BY host
| eval _time = now()
| collect index=summary_web_metrics marker="report_type=hourly_metrics"
# サマリーインデックスからの読み取り
index=summary_web_metrics report_type=hourly_metrics earliest=-30d
| timechart span=1d avg(avg_response_time) AS avg_rt BY host
6.5 アクセラレーション
# savedsearches.conf — レポートアクセラレーション
[Daily Error Report]
search = index=web_logs status>=500 | stats count BY host, status, uri
dispatch.earliest_time = -24h
dispatch.latest_time = now
auto_summarize = true
auto_summarize.dispatch.earliest_time = -30d
auto_summarize.cron_schedule = */10 * * * *
# datamodels.conf — データモデルアクセラレーション
[Web]
acceleration = true
acceleration.earliest_time = -3mon
acceleration.cron_schedule = */5 * * * *
第7章: ダッシュボードとアラート
7.1 SimpleXML / Dashboard Studio
<!-- SREダッシュボードの例 -->
<dashboard>
<label>SRE - Service Health Dashboard</label>
<row>
<panel>
<title>Error Rate (Last 24h)</title>
<single>
<search>
<query>
index=web_logs earliest=-24h
| stats count(eval(status>=500)) AS errors, count AS total
| eval error_rate = round(errors/total*100, 3)
| fields error_rate
</query>
</search>
<option name="colorBy">value</option>
<option name="rangeColors">["0x53a051","0xf8be34","0xdc4e41"]</option>
<option name="rangeValues">[0.1,1]</option>
<option name="unit">%</option>
</single>
</panel>
<panel>
<title>P95 Latency (Last 24h)</title>
<chart>
<search>
<query>
index=web_logs earliest=-24h
| timechart span=5m perc95(response_time) AS p95_ms
</query>
</search>
<option name="charting.chart">line</option>
<option name="charting.chart.overlayFields">threshold</option>
</chart>
</panel>
</row>
<row>
<panel>
<title>Error Budget Burn Rate</title>
<chart>
<search>
<query>
index=web_logs earliest=-30d
| timechart span=1d
count(eval(status>=500)) AS daily_errors,
count AS daily_total
| streamstats sum(daily_errors) AS cum_errors,
sum(daily_total) AS cum_total
| eval budget_total = cum_total * 0.001
| eval budget_remaining_pct = round((budget_total - cum_errors) / budget_total * 100, 2)
| fields _time, budget_remaining_pct
</query>
</search>
<option name="charting.chart">area</option>
</chart>
</panel>
</row>
</dashboard>
7.2 アラート設定
# savedsearches.conf — アラート定義
[High Error Rate Alert]
search = index=web_logs earliest=-5m | stats count(eval(status>=500)) AS errors, count AS total | eval error_rate=errors/total*100 | where error_rate > 1
cron_schedule = */5 * * * *
alert.severity = 4
alert.suppress = true
alert.suppress.period = 30m
alert_condition = search error_rate > 1
action.webhook = 1
action.webhook.param.url = https://hooks.slack.com/services/YOUR/WEBHOOK/URL
action.email = 1
action.email.to = sre-team@example.com
action.email.subject = [ALERT] High Error Rate Detected
第8章: SPLのベストプラクティス
8.1 命名規則
# マクロの命名
`sre_error_rate(2)` — プレフィックスにチーム名/目的
`security_threat_check` — 機能を表す名前
# サマリーインデックスのマーカー
marker="report_type=hourly_metrics, team=sre"
# ルックアップファイル
server_inventory.csv — 名詞で説明的
geo_ip_database.csv — 内容を反映
8.2 よくある間違いと回避策
# ❌ NOT演算子の誤用(フィールドが存在しないイベントにもマッチ)
index=web_logs NOT status=200
# ✅ 正しくは
index=web_logs status!=200
# ❌ 大文字小文字の混同
index=web_logs | where host="Web-Server-01" # 大文字小文字を区別
# ✅ searchは区別しない
index=web_logs host="Web-Server-01" # 大文字小文字を区別しない
# ❌ 過剰なサブサーチ
# ✅ 代わりにlookup, eventstats, statsを使用
# ❌ joinの乱用
# ✅ 代わりにstats + append + statsパターンを使用
まとめ
SPLはSplunkの強力なクエリ言語であり、以下の要点を押さえることで効果的に活用できる:
- 基本構文: パイプライン構造、キーワード検索、フィールドフィルタリング
- 統計コマンド:
stats,timechart,chart,eventstats,streamstats - フィールド操作:
eval(計算・条件分岐)、rex(正規表現抽出)、spath(JSON/XML) - 高度な機能: サブサーチ、ルックアップ、マクロ、トランザクション
- SREユースケース: SLI/SLOモニタリング、エラーバジェット、インシデント分析、キャパシティプランニング
- パフォーマンス最適化: 早期フィルタリング、
tstats、サマリーインデックス - 運用: ダッシュボード、アラート、命名規則、ベストプラクティス
参考文献
- Splunk SPL Reference Manual: https://docs.splunk.com/Documentation/Splunk/latest/SearchReference
- Splunk Search Tutorial: https://docs.splunk.com/Documentation/Splunk/latest/SearchTutorial
- Splunk Quick Reference Guide (PDF): https://www.splunk.com/pdfs/solution-guides/splunk-quick-reference-guide.pdf
- "Splunk Operational Intelligence Cookbook" - Josh Diakun, Paul R Johnson, Derek Mock
- "Exploring Splunk" - David Carasso
- Splunk Community: https://community.splunk.com/
- SRE with Splunk: https://www.splunk.com/en_us/solutions/sre.html