トラック A · タスク A3
ログ検索 エクスプローラー
すべてを SQL で扱う小さな Kibana を作り、11:40 に注文処理が失敗した原因を調べます。
- 60~120 分
- 初心者向け
- 言語自由 · CLI または Web
- AI ツールの利用歓迎
困ったときは? 質問はこちら: #dev 参加先: Apache Doris Slack。メンテナーやほかの参加者がサポートします。
環境を準備
何を作るか、Doris ならどう簡単になるかを学び、クラスターを起動します。
作成するもの
ログエクスプローラーを作り、障害を解明します。
200,000 行のログ、6 つのサービス、そして問題のあった朝。BM25 の関連度順でキーワード検索し、サービス・レベル・時間で絞り込み、件数を随時計算します。すべて、1 つの Doris テーブルに対する通常の SQL で実行します。
次の機能を持つ小さなログエクスプローラー:
- 検索用の 検索ボックス (キーワード・フレーズ)と関連度による順位付け、
- フィルター (サービス、レベル、時間範囲)、
- ファセット件数 (結果の隣にサービス別・レベル別の件数を表示)、
- 任意で次も追加: タイムライン (5 分ごとのエラー数)。
最後に実際の調査を行います: 11:40 に注文処理が失敗し始めました。原因となったサービスはどれで、問題は実際にはいつ始まったのでしょうか? エクスプローラーから答えが明確にわかるようにしてください。
概略図はコードパネルの上部にあります
Doris で取り組む利点
同じテーブルで検索と分析。 転置インデックスがキーワードクエリを処理し、同じ
WHERE句を次に使います:GROUP BYによるファセット集計。別システムとの同期は不要です。関連度に基づく順位付け。
score()が BM25 で結果をソートするため、新しい行だけでなく、最も関連する行が先頭に並びます。低コストなフィルター。
serviceおよびlevelにはそれぞれ転置インデックスがあり、時間範囲はソートキーで絞り込みます。
パネル:テーブル定義のインデックス行
始める前に
docker pull apache/doris:all-in-one-4.1.3docker run -d --name doris -p 9030:9030 -p 8030:8030 -p 8040:8040 apache/doris:all-in-one-4.1.3docker exec -it doris mysql -uroot -h127.0.0.1 -P9030curl -LO https://github.com/morningman/demo-env/releases/download/for-hackathon/doris-hackathon-glasgow-2026.zip && unzip doris-hackathon-glasgow-2026.zip && cd doris-hackathon-glasgow-2026
- ホスト
- 127.0.0.1
- ポート
- 9030
- ユーザー
- rootパスワードなし
- データベース
- hackathon
データ
hackathon.app_logs :6 つのサービスを 1 つのテーブルに保存します:
CREATE TABLE app_logs ( ts DATETIME(3) NOT NULL, service VARCHAR(32) NOT NULL, -- api-gateway, auth, catalog, search, checkout, payment level VARCHAR(8) NOT NULL, -- DEBUG / INFO / WARN / ERROR host VARCHAR(32), trace_id VARCHAR(32), status INT, -- HTTP status, when relevant latency_ms INT, message STRING NOT NULL, INDEX idx_msg (message) USING INVERTED PROPERTIES ("parser" = "english", "support_phrase" = "true"), INDEX idx_service (service) USING INVERTED, INDEX idx_level (level) USING INVERTED)DUPLICATE KEY(ts)DISTRIBUTED BY HASH(trace_id) BUCKETS 1PROPERTIES ("replication_num" = "1");seed/a3_app_logs.sql が生成するデータ: 2026 年 10 月 13 日の 200,000 行 に対し、1 つの INSERT … SELECT … FROM numbers("number" = "200000") で生成します。ダウンロードは不要です。このデータのどこかに障害が潜んでいます。
パネル → テーブル:テーブル定義の全体
AI を使って作る
AI コーディングエージェントとの共同作業を推奨しています。 Doris 4.x の検索機能と AI 機能は、多くのモデルの学習データより新しいため、SQL を書かせる前に適切な情報を渡してください:
次の コンテキストプロンプト をエージェントに貼り付けます。
参照先として次を指定: このページに掲載した参考資料 および
AGENTS.md(スターターキット内)。多くのコーディングエージェントはこのファイルを自動で読み込みます。コードに組み込む前に、すべての SQL 文を実際のクラスターで実行させてください。
さらに、次を接続すると便利です: Doris MCP Server (参照: タスク A2)。エージェント自身でスキーマを確認し、クエリをテストできるようになります。
I'm building a log search explorer on Apache Doris 4.1 for a hackathon.Connection: MySQL protocol, 127.0.0.1:9030, user root, empty password, database `hackathon`.Use a plain MySQL driver and raw SQL.Table `app_logs`: ts DATETIME(3), service, level, host, trace_id, status INT, latency_ms INT,message STRING. Inverted indexes on message (english parser, phrase support), service, level.Doris SQL rules:1. Text search: `message MATCH_ANY 'a b'`, `MATCH_ALL`, `MATCH_PHRASE 'a b'`. Never LIKE.2. BM25: `score()` only with a MATCH_* predicate + `ORDER BY score() DESC LIMIT n`, selected plain (`score() AS relevance`): no ROUND() or JOIN in that SELECT. Bind the user's search text as a driver parameter.3. score() cannot be used with GROUP BY. For facet counts run a second query with the same WHERE clause and GROUP BY service, level.4. Time buckets: `minute_floor(ts, 5)`.Build: search box, service/level/time filters, facet counts, and an error timeline.Run every SQL statement against the live cluster first.AI エージェントが陥りやすい Doris の落とし穴
| Trap | What to do instead |
|---|---|
次の使用: LIKE '%term%' によるテキスト検索 | col MATCH_ANY 'a b' (OR)、 MATCH_ALL (AND)、 MATCH_PHRASE 'a b' 。これらは転置インデックスを使います |
次の呼び出し: score() を任意のクエリで使用 | score() には次が必要: MATCH_* の述語を次に指定: WHERE および ORDER BY score() DESC LIMIT n。指定しないと Doris はクエリを拒否します |
丸め、結合、順位付けの対象: score() を同じクエリ内で行う | 次はそのまま使います: score() AS relevance を単一テーブルのクエリ内でそのまま使います。丸めや結合、 ROW_NUMBER() は外側のクエリで行ってください。次の代わりにサブクエリを使います: WITH CTE |
score() を次の内側で使用: GROUP BY / 集約 | 使用できません。ファセットの件数集計には同じ次の条件を再利用します: WHERE を除外: score() |
| ORM を使う | 通常の MySQL ドライバーと生の SQL を推奨します。ORM は Doris がサポートしない文を生成する場合があります |
実装する
6 マイルストーン、 M0 → M5。パネルは読んでいるステップに追従します。
ログを生成
スターターキットのフォルダー内で実行:
M0 · ログを生成bashdocker exec -i doris mysql -uroot -h127.0.0.1 -P9030 < seed/a3_app_logs.sql確認ポイント数秒後に表示: 200,000 行のログ、開始時刻:
00:00:00終了時刻:23:59:59(10 月 13 日)。BM25 によるキーワード検索
M1 · BM25 によるキーワード検索sqlUSE hackathon;SELECT ts, service, level, message, score() AS relevanceFROM app_logsWHERE message MATCH_ANY 'timeout refused'ORDER BY relevance DESCLIMIT 20;検索ボックスに接続します。ユーザー入力はドライバーパラメーターで渡します(
MATCH_ANY %s);a3/starter.py(スターターキット内)がこの処理を実装しています。確認ポイント20 件の結果はすべて
connection refusedの行です。BM25 は出現頻度の低い語を重視します: refused は 68 行に出現し、 timeout は 347 行に出現します。検索語を次だけにすると、timeoutほかの結果も確認できます。フィルター
M2 · フィルターsqlSELECT ts, service, level, message, score() AS relevanceFROM app_logsWHERE message MATCH_ANY 'timeout' AND level = 'ERROR' AND service IN ('checkout', 'payment') AND ts >= '2026-10-13 11:00:00' AND ts < '2026-10-13 12:30:00'ORDER BY relevance DESCLIMIT 20;確認ポイントすべての結果は timeout を含む ERROR 行です。2 つのサービスのうち 1 つだけが表示されます。
ファセット件数
score()は集約できないため、ファセット集計にはこれを除いた同じフィルターを使います:M3 · ファセット件数sqlSELECT service, level, COUNT(*) AS hitsFROM app_logsWHERE message MATCH_ANY 'timeout' AND ts >= '2026-10-13 11:00:00' AND ts < '2026-10-13 12:30:00'GROUP BY service, levelORDER BY hits DESC;確認ポイントこの時間帯に一致するログは 163 行で、すべて
ERRORです。件数の合計は、次を除いた同じ検索の件数と一致する必要があります:GROUP BY。確認コマンド:SELECT COUNT(*)と同じWHERE.エラーのタイムライン
M4 · エラーのタイムラインsqlSELECT minute_floor(ts, 5) AS bucket, service, COUNT(*) AS errorsFROM app_logsWHERE level = 'ERROR' AND ts >= '2026-10-13 11:00:00' AND ts < '2026-10-13 12:30:00'GROUP BY bucket, serviceORDER BY bucket, service;確認ポイント11:35 から 12:00 の間に 2 つのサービスで急増します。それ以外の時間帯では、どのサービスも 5 分間のエラー数が 4 件を超えません。
障害を解明
エクスプローラーを使って答えます: 注文処理の失敗を引き起こしたサービスはどれで、何時何分に始まりましたか? 答えと、それを裏付けるクエリを README に記載してください。
完成させる
チェックを済ませ、成果を共有し、バッジを獲得しましょう。
完了条件
チェックリスト提出してバッジを獲得
参加 → 提出 → バッジを獲得。
成果物は次の名前のフォルダーにまとめてください: ご自身の GitHub ID。コードと README を含め、README に内容、実行方法、スクリーンショットまたは GIF 1 点、使用した Doris の機能を記載します。
プルリクエストを作成 次の場所に追加してください: morningman/demo-env 配置先:
doris-hackathon-glasgow-2026/<your-github-id>/。フォークして push するか、GitHub の次の操作を使ってください: Add file → Upload files。ほかに入力する項目はありません。共有する 共有先:
#dev参加先: Apache Doris Slack:プルリクエストまたはリポジトリへのリンクと、スクリーンショットまたは短いデモを共有します。質問、提出、バッジの案内はすべてこちらで行います。
タスクに参加した方全員に次のバッジを贈ります: Apache Doris Contributor バッジ。Doris 本体へのプルリクエストがマージされている必要はありません。締め切りはないので、ご自身のペースで進めてください。チーム参加も可能ですが必須ではありません。全員の GitHub ID を次に記載してください: README.
追加の挑戦
フレーズ検索と入力補完:
MATCH_PHRASE 'connection refused',MATCH_PHRASE_PREFIXを使って入力中に検索します。トレース表示:行をクリックすると、同じ次の値を持つログをすべて表示します:
trace_id。時刻順で表示します。クエリ文字列検索:次を試してください:
SEARCH()DSL(Lucene 形式のfield:term AND …)。参照: SEARCH 関数.ログのリアルタイム追跡:毎秒数行を追加し、自動更新します。
インデックスの有無を比較:比較対象:
MATCH_ANYともに歩むLIKE '%timeout%'を全テーブルで実行し、所要時間を表示します。
トラブルシューティングと参考資料
| Symptom | Fix |
|---|---|
docker: command not found (macOS) | Docker Desktop を起動してください。起動済みなら CLI へのリンクを作成します: sudo ln -s /Applications/Docker.app/Contents/Resources/bin/docker /usr/local/bin/docker |
error getting credentials (イメージ取得時) | 削除する項目: credsStore のフィールドを次から削除: ~/.docker/config.json (ローカル開発環境のみ) |
port is already allocated | 別のプロセス(多くは別の Doris)が 9030、8030、8040 のいずれかを使っています。実行中のコンテナーを確認してください(docker ps)。または別のホストポートを公開します。例: -p 19030:9030。その場合は 19030 に接続してください。 |
The container name "/doris" is already in use | 以前に起動したことがあります: docker start doris。最初からやり直すには、 docker rm -f doris (データが削除されます) |
コンテナーが終了する、または状態が変わらない: (healthy) | 確認: docker logs doris。多くはメモリが原因です。Docker Desktop に 6 GB 以上を割り当ててください(Settings → Resources)。Apple Silicon では次を追加しないでください: --platform linux/amd64 |
permission denied: ./doris.sh | 解凍ツールによって実行権限が失われています: chmod +x doris.sh、または次を実行: bash doris.sh start |
Current database is not set | 実行: USE hackathon; を先に実行するか、次で接続します: -Dhackathon |
score() function requires WHERE clause with MATCH function, ORDER BY and LIMIT | 次を追加: MATCH_* の述語、 ORDER BY score() DESC および LIMIT、さらに次は score() を式で包まないでください(上記の落とし穴を参照) |
| 10 分以上解決できない場合 | 投稿先: #dev 参加先: Apache Doris Slack |
参考資料
Apache Doris Hackathon
