DuckDB で AWS ALB の S3 ログをクエリするツール作成 with Windsurf

Windsurf + Claude 3.7 sonnet でツールを実装してみた感想。
S3にあるALBログの調査はAthenaよりDuckDBのほうが簡単 - これをコマンドでシュッと呼び出したい!がモチベーション
今回は Windsurf にやり切ってもらう!を意識して 95% くらいは Windsurf が実装 & コマンドライン操作(したと思う)。実際仕事で使うよ、、、荒削りなところとか、セキュリティ的な部分はあとで見る...
- やりたいことを実装 -> リリースのための GitHub Actions workflow 実装 -> リリース!までのトータルで考えると圧倒的に早い
- プログラム実装だけであれば自分でやった方がはやかったかも...が、とりあえずやりたいことを実装してもらってそれをベースに修正してくのが一番早そう
- GitHub Actions workflow 周辺は、空で yaml 書ける変態さん以外はちょー便利、Makefile もとても助かる
- エラー解消で無限ループに入ることがある
- Windsurf でやり切ることに拘らず自分で実装した方が早い(良い) / それはそう
- テスト書いてくれるのめちゃ良い
- Windsurf に追加して指示したことは、その度に markdown ファイルに書き出しておくと良さそう
- 消費リクエスト数

Windsurf と一緒にツールを作成するシリーズ? 今回は新規ツール作成するよ。

こんな README.md を作成
dalv
Querying AWS ALB S3 logs with duckdb
DuckDB で AWS ALB の S3 ログを読み込むための準備
DuckDB で AWS ALB S3 logs をクエリするためのセットアップと、必要なテーブル作成
INSTALL aws;
LOAD aws;
INSTALL httpfs;
LOAD httpfs;
CREATE SECRET (
TYPE S3,
PROVIDER CREDENTIAL_CHAIN
);
ログを読み込みためのテーブル作成
CREATE TABLE alb_log_20250303 AS
SELECT *
FROM read_csv(
's3://{S3_BUCKET_NAME}/xxxxxx/AWSLogs/{ACCOUNT_ID}/elasticloadbalancing/{REGION}/2025/03/03/*.log.gz',
columns={
'type': 'VARCHAR',
'timestamp': 'TIMESTAMP',
'elb': 'VARCHAR',
'client_ip_port': 'VARCHAR',
'target_ip_port': 'VARCHAR',
'request_processing_time': 'DOUBLE',
'target_processing_time': 'DOUBLE',
'response_processing_time': 'DOUBLE',
'elb_status_code': 'INTEGER',
'target_status_code': 'VARCHAR',
'received_bytes': 'BIGINT',
'sent_bytes': 'BIGINT',
'request': 'VARCHAR',
'user_agent': 'VARCHAR',
'ssl_cipher': 'VARCHAR',
'ssl_protocol': 'VARCHAR',
'target_group_arn': 'VARCHAR',
'trace_id': 'VARCHAR',
'domain_name': 'VARCHAR',
'chosen_cert_arn': 'VARCHAR',
'matched_rule_priority': 'VARCHAR',
'request_creation_time': 'TIMESTAMP',
'actions_executed': 'VARCHAR',
'redirect_url': 'VARCHAR',
'error_reason': 'VARCHAR',
'target_port_list': 'VARCHAR',
'target_status_code_list': 'VARCHAR',
'classification': 'VARCHAR',
'classification_reason': 'VARCHAR',
'conn_trace_id': 'VARCHAR'
},
delim=' ',
quote='"',
escape='"',
header=False,
auto_detect=False
);
DuckDB で AWS ALB の S3 ログをクエリする
select * from alb_log_20250303 where elb_status_code != 431;

Windsurf に README.md をみてツールの仕様を考えてもらう
README.md に書かれていることを
dalv s3://{S3_BUCKET_NAME}/xxxxxx/AWSLogs/{ACCOUNT_ID}/elasticloadbalancing/{REGION}/2025/03/03/*.log.gz
のように S3 バケットを指定して起動する。
起動後は DuckDB のコンソール操作をしてクエリできるツールを作りたいと思います。
開発言語は Go言語 です。
docs/too-spec.md としてツールの仕様を書き出してください。

なんかええ感じの仕様が作成されたねー、ほんまに作成できるの?
dalv - DuckDB AWS ALB Log Viewer
dalv
is a Go-based command-line tool for querying AWS Application Load Balancer (ALB) logs stored in S3 buckets using DuckDB.
機能概要
- S3 バケットから AWS ALB ログを直接クエリ
- DuckDB を使用した強力なSQL機能を提供
- インタラクティブなコンソールモードをサポート
- 簡単なコマンドライン操作
コマンド仕様
dalv [options] <s3-path>
引数
-
<s3-path>
: 必須。ALB ログが保存されている S3 パスを指定。ワイルドカードの使用可能 (例:s3://bucket-name/path/to/logs/*.log.gz
)
オプション
-
-i, --interactive
: インタラクティブモードで起動 (デフォルト: true) -
-q, --query <sql>
: 指定された SQL クエリを実行して結果を表示し、終了 -
-o, --output <file>
: 結果を指定されたファイルに出力 -
-f, --format <format>
: 出力フォーマットを指定 (csv, json, table) (デフォルト: table) -
--aws-profile <profile>
: 使用する AWS プロファイルを指定 -
--region <region>
: AWS リージョンを指定 -
--debug
: デバッグ情報を表示
内部処理フロー
- コマンドライン引数の解析
- AWS 認証情報の取得と検証
- 指定された S3 パスからログファイルのメタデータ取得
- DuckDB インスタンスの初期化
- AWS 拡張と httpfs 拡張のインストールと読み込み
- S3 アクセスのための Secret 設定
- ログデータをクエリするための一時テーブル作成
- インタラクティブモードまたは指定クエリの実行
- 結果の表示または出力
インタラクティブモード仕様
インタラクティブモードでは、以下の機能を提供:
- SQL クエリの入力と実行
- コマンド履歴機能 (上下キーで過去のクエリ呼び出し)
- クエリ補完機能 (TABキーで補完)
- ヘルプコマンド (
.help
) - 終了コマンド (
.quit
または.exit
) - テーブル一覧表示 (
.tables
) - テーブル構造表示 (
.schema <table_name>
) - キーワード色分け表示
実装要件
技術スタック
- Go言語 (1.18以上)
- DuckDB Go ドライバー (github.com/marcboeker/go-duckdb)
- AWS SDK for Go v2
- Cobra (コマンドライン引数解析)
- Prompter (インタラクティブモード用)
モジュール構成
dalv/
├── cmd/ # コマンドライン処理
│ └── root.go # メインのコマンド定義
├── internal/ # 内部パッケージ
│ ├── aws/ # AWS 関連処理
│ ├── console/ # インタラクティブコンソール処理
│ ├── duckdb/ # DuckDB 連携処理
│ └── query/ # クエリ実行・結果整形処理
├── pkg/ # 外部公開可能なパッケージ
├── main.go # エントリポイント
└── go.mod # Go モジュール定義
ユースケース例
基本的な使用方法
# インタラクティブモードで起動
dalv s3://chatgai-prod-211125472860/chatgai-prod/AWSLogs/211125472860/elasticloadbalancing/ap-northeast-1/2025/03/03/*.log.gz
# 特定のクエリを実行して終了
dalv -q "SELECT COUNT(*) FROM alb_log WHERE elb_status_code = 200" s3://bucket/path/*.log.gz
# 結果をCSVファイルに出力
dalv -q "SELECT * FROM alb_log LIMIT 100" -o results.csv -f csv s3://bucket/path/*.log.gz
# 特定のAWSプロファイルを使用
dalv --aws-profile production s3://bucket/path/*.log.gz
インタラクティブモードでの操作例
dalv> SELECT COUNT(*) FROM alb_log;
┌───────────┐
│ count_star │
├───────────┤
│ 1234 │
└───────────┘
dalv> SELECT elb_status_code, COUNT(*) as count FROM alb_log GROUP BY elb_status_code ORDER BY count DESC;
┌───────────────┬───────┐
│ elb_status_code│ count │
├───────────────┼───────┤
│ 200 │ 980 │
│ 404 │ 120 │
│ 500 │ 80 │
│ 403 │ 54 │
└───────────────┴───────┘
dalv> .tables
┌────────┐
│ name │
├────────┤
│ alb_log │
└────────┘
dalv> .exit
今後の拡張性
- クエリ結果のグラフ表示機能
- ログパターンの自動検出と分析
- 一般的なクエリテンプレートの提供
- 複数バケット・パスの同時クエリ対応
- クエリ結果のキャッシュ機能
- カスタムUDF(ユーザー定義関数)のサポート
実装計画
- プロジェクト基本構造とコマンドライン引数処理の実装
- AWS 認証と S3 パス解析機能の実装
- DuckDB 連携処理の実装
- インタラクティブコンソールの実装
- クエリ実行と結果表示処理の実装
- テスト・ドキュメント作成
- リリース準備

作ってもらった仕様書にもとづいて実装してもらおう
できたの?
README.md をええ感じにするのとビルドスクリプト(何で書いてくれるんだろう)をお願いしたよ
シェルスクリプト(確かに)だったので
Makefile を用意してもらう

ソースコードできた!
それでビルド&実行できるんですか?
この後いろいろ試した後、 running
だったかな?の表示のまま で返答が返ってこなくなったのでキャンセル
どれどれ
ふむ Explan してお話し開始
いやだ、DuckDB 公式ページにあるライブラリがいい。公式ドライバ使用するかい? go get github.com/duckdb/duckdb-go
ってそんなライブラリ存在しないぞ!
go get github.com/marcboeker/go-duckdb@v1.4.0
してバージョンダウンを勧められるんだけど、その理由はなんでなんだろう?
あーね、やばい、そろそろ気持ちが切れてきた。
検索してみるとなんか出てくるなー。最後まで Windsurf さんで実装しきりたいんだよな。全部捨てて Deno で実装するか!

これが生成 AI 時代のプログラミングなんじゃね?🙃
ほんまに?俺 Deno 全くわからんけど、よし!
ビルドして
僕は mise でツール管理したいんだよ
なるほど? deno の最新版は mise 環境にインストールされている。
が、mise って ~/.zshrc で active にしてるけどこれが効かないのか?
eval "$(~/.local/bin/mise activate zsh)"
deno のパスを教えちゃお
ビルド走ったねー、いや、僕の力が入ってしまった、ダメ、我慢

いや、Done じゃねーじゃん、面白い
頑張って、頑張ると、全てを忘れて、ビルドも通ってないし、deno のパスも忘れちゃった

わーん、これは仕様書がよくばりすぎるのか?僕はもっとシンプルなものでよかったんだよ。

Windsurf のルールファイルを書いて再挑戦!

DuckDB + Deno での実装....ギブアップ! Deno でのビルド成功せず.... これ以上 Windsurf のクレジット消費はもったいないな。
最初 README.md に書かれていたことを実現するために、僕なら
- DuckDB は brew なりなんなりで OS にインストールしてもらう前提とする
- aws, httpfs のロードと S3 SECETS 作成 + AWS ALB S3 (.tar.gz) ログをテーブル化するための SQL? を組み立てる
- ロードする S3 バケット(.tar.gz) だけ外から与えられるようにする
で実現できるよなー、そうするか!と思うよね。これって今の AI エディタでの開発だと知識として与えることになるだろうな、Devin でも同様かな?

こんな曖昧な指示はダメ
うりゃ

やり直し!
docs/tool-spec.md の仕様を変更します。
やりたいことは
dalv s3://{ALBログファイル.targzパス}
なので同じ。
プログラミング言語で全て実装するのではなく、duckdb は事前にインストールされていることを前提にする。
- aws, httpfs のロードと S3 SECETS 作成 + AWS ALB S3 (.tar.gz) ログをテーブル化するための SQL? を組み立てる
- ロードする S3 バケット(.tar.gz) だけ外から与えられるようにする
という戦略に変更したいと思います。duckdb を適切な呼び出しをすることで、やりたいことを実現してください。
箇条書きしたやりたいことに必要な情報は READMD.md を参照して!
ツールは Go 言語を使って実現してください。

ごめん!ちゃんと docs/tool-spec.md を書いて!ってお願いしなかったね
duckdb コマンド実行前提なのか?確認してみたけど自信あるみたいだね

できたらしいぞ!
Go 言語で開発するは Makefile 書いて!ってのが僕のルールなのか、そうだね
.gitignore, .tool-versions も修正してもらう

おぉぉぉぉ ビルドできるよ
もう記録できてないけど duckdb を呼び出して AWS ALB の S3 (.tar.gz) に保存されたログをクエリできるようになった......いえい!
- table 指定オプション実装してもらった
- README.md 整えてもらった
- Go のテスト書いてもらった

CI 作成しよう
pull request 時の CI一式と、Upload Artifacts の 2つの workflow を作成してもらった。
初めての git 操作依頼! この PR. のブランチを作成
エラーが発生したので修正依頼
CI 通ったね
PR#1 をマージ! もう一つの workflow が走るよ
成功してるね!
簡単に利用できるようにしたいぞ!

リリース失敗
修正してもらおう、正しそう?ただこのパーミッションで良いのか?は調べてない
リリース成功してるじゃん
v0.1.0 🎉

Windsurf が実施したことに対して、追加で指示したことを Rules として書いておきたいよなーと思ったけど、途中で死んだり、Windsurf 再起動してるからダメだった、都度都度書き出さないとダメだね。

Go 実装
-
テストに関すること
- 常にテストが通る状態を保って
- ファイルを追加したらテストを実装して
- 関数を変更したらテストが通るか?確認してテストを修正して、また、追加機能があればテストを追加して
- テストは1ファイルずつテスト実行しながら確認すること
-
機能実装時
- 機能追加・変更・削除で README.md に変更が必要ないか?確認して必要があれば修正して
このくらいか?優秀