ElasticsearchとKibanaを動かせる環境を作ったので、今度は何かしらグラフ表示してみたいと思います。
Docker Composeで、ElasticsearchとKibanaを試す環境を作る - CLOVER
ネタはどうしようかなぁと思ったのですが…自分のブログ(http://d.hatena.ne.jp/Kazuhira/)の月ごとの投稿回数をお題にしてみました。
※一応、このブログは月1回は投稿しようという目標を自分で設けています
以下の流れでやります。
- Apacheでフォワードプロキシサーバーを立てる(ここは省略)
- wgetでフォワードプロキシごしにブログをクロール
- フォワードプロキシからアクセスログを取得して、Elasticsearchに登録
- Kibanaで見る
では、やってみます。
ブログのクロール
まずは、ブログをwgetでクロールします。
ローカルにApacheによるフォワードプロキシを、ポート8080で起動しているとして、以下のコマンドでこのブログのみをクロールします。
$ export http_proxy=http://localhost:8080/ $ wget -r --no-parent http://d.hatena.ne.jp/Kazuhira/
けっこう時間がかかりましたが、こんな感じのアクセスログが取得できました。
$ head -n 100 logs/access.log 172.17.0.1 - - [16/Feb/2016:14:30:00 +0000] "GET http://d.hatena.ne.jp/Kazuhira/ HTTP/1.1" 200 276760 "-" "Wget/1.15 (linux-gnu)" 172.17.0.1 - - [16/Feb/2016:14:30:00 +0000] "GET http://d.hatena.ne.jp/robots.txt HTTP/1.1" 200 359 "-" "Wget/1.15 (linux-gnu)" 172.17.0.1 - - [16/Feb/2016:14:30:00 +0000] "GET http://d.hatena.ne.jp/Kazuhira/?of=5 HTTP/1.1" 200 403769 "http://d.hatena.ne.jp/Kazuhira/" "Wget/1.15 (linux-gnu)" 172.17.0.1 - - [16/Feb/2016:14:30:01 +0000] "GET http://d.hatena.ne.jp/Kazuhira/rss HTTP/1.1" 200 248281 "http://d.hatena.ne.jp/Kazuhira/" "Wget/1.15 (linux-gnu)" 172.17.0.1 - - [16/Feb/2016:14:30:01 +0000] "GET http://d.hatena.ne.jp/Kazuhira/rss2 HTTP/1.1" 200 244453 "http://d.hatena.ne.jp/Kazuhira/" "Wget/1.15 (linux-gnu)" 172.17.0.1 - - [16/Feb/2016:14:30:02 +0000] "GET http://d.hatena.ne.jp/Kazuhira/foaf HTTP/1.1" 200 1183 "http://d.hatena.ne.jp/Kazuhira/" "Wget/1.15 (linux-gnu)" 172.17.0.1 - - [16/Feb/2016:14:30:02 +0000] "GET http://d.hatena.ne.jp/Kazuhira/opensearch/diary.xml HTTP/1.1" 200 1030 "http://d.hatena.ne.jp/Kazuhira/" "Wget/1.15 (linux-gnu)" 172.17.0.1 - - [16/Feb/2016:14:30:02 +0000] "GET http://d.hatena.ne.jp/Kazuhira/opensearch/archive.xml HTTP/1.1" 200 1026 "http://d.hatena.ne.jp/Kazuhira/" "Wget/1.15 (linux-gnu)" 172.17.0.1 - - [16/Feb/2016:14:30:02 +0000] "GET http://d.hatena.ne.jp/Kazuhira/mobile HTTP/1.1" 200 78822 "http://d.hatena.ne.jp/Kazuhira/" "Wget/1.15 (linux-gnu)" 172.17.0.1 - - [16/Feb/2016:14:30:06 +0000] "GET http://d.hatena.ne.jp/Kazuhira/archive HTTP/1.1" 200 67406 "http://d.hatena.ne.jp/Kazuhira/" "Wget/1.15 (linux-gnu)" 172.17.0.1 - - [16/Feb/2016:14:30:07 +0000] "GET http://d.hatena.ne.jp/Kazuhira/20160214 HTTP/1.1" 200 89232 "http://d.hatena.ne.jp/Kazuhira/" "Wget/1.15 (linux-gnu)" 172.17.0.1 - - [16/Feb/2016:14:30:07 +0000] "GET http://d.hatena.ne.jp/Kazuhira/20160214/1455460595 HTTP/1.1" 200 52945 "http://d.hatena.ne.jp/Kazuhira/" "Wget/1.15 (linux-gnu)" 172.17.0.1 - - [16/Feb/2016:14:30:08 +0000] "GET http://d.hatena.ne.jp/Kazuhira/searchdiary?word=%2A%5BSpring%5D HTTP/1.1" 200 182759 "http://d.hatena.ne.jp/Kazuhira/" "Wget/1.15 (linux-gnu)"
Elasticsearchにインデックステンプレートを登録する
このアクセスログを、Elasticsearchに登録するわけですが、最初にインデックステンプレートを登録しておきます。
今回は、こんなテンプレートにしました。
index.json
{ "template": "apache-accesslog_*", "settings": { "index": { "number_of_shards": 1, "number_of_replicas": 0 } }, "mappings": { "_default_": { "_source": { "enabled": false }, "dynamic_templates": [ { "string_template": { "mapping": { "index": "not_analyzed", "type": "string", "store": "yes" }, "match_mapping_type": "string", "match": "*" } } ] } } }
「apache-accesslog_*」のパターンで、テンプレートにしておきます。
これを、Elasticsearchに登録。
$ curl -XPUT http://localhost:9200/_template/apache-accesslog -d @index.json {"acknowledged":true}
EmbulkでアクセスログをElasticsearchに登録する
取得したアクセスログをElasticsearchに登録するわけですが、何で放り込むのかちょっと迷うところです。
今回は、Embulkにしました。
GitHub - embulk/embulk: Embulk: Pluggable Bulk Data Loader. http://www.embulk.org
インストール。
$ curl --create-dirs -o ~/.embulk/bin/embulk -L "http://dl.embulk.org/embulk-latest.jar" $ chmod +x ~/.embulk/bin/embulk $ echo 'export PATH="$HOME/.embulk/bin:$PATH"' >> ~/.bashrc $ source ~/.bashrc
今回、ApacheのアクセスログをパースしてElasticsearchに放り込むわけですが、その際にアクセスログをフィルタする必要があります。ブログの記事単位のURLは、こういう形式なので。
http://d.hatena.ne.jp/Kazuhira/20160214/1455460595
で、これらを実現するためにプラグインをインストール。
$ embulk gem install embulk-parser-apache-custom-log $ embulk gem install embulk-output-elasticsearch $ embulk gem install embulk-filter-ruby_proc $ embulk gem install embulk-filter-column $ embulk gem install embulk-filter-row
書いた設定ファイルは、こちら。
config.yml
in: type: file path_prefix: logs/access.log parser: type: apache-custom-log format: "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" filters: - type: column add_columns: - { name: "blog-month", src: "request-line" } - type: ruby_proc columns: - name: blog-month proc: | ->(request_line) do match = request_line.match(/.+ https?:\/\/d\.hatena\.ne\.jp\/Kazuhira\/(\d+)\d\d\/\d+ .+/) if match then match[1] else nil end end - type: row conditions: - { column: blog-month, operator: "IS NOT NULL" } out: type: elasticsearch mode: replace nodes: - {host: localhost, port: 9300} index: apache-accesslog index_type: log
Filterで、Request Lineをコピーして目的のURLだけ加工し、条件に合わなかったものを除外しました。その定義が、この部分です。
filters: - type: column add_columns: - { name: "blog-month", src: "request-line" } - type: ruby_proc columns: - name: blog-month proc: | ->(request_line) do match = request_line.match(/.+ https?:\/\/d\.hatena\.ne\.jp\/Kazuhira\/(\d+)\d\d\/\d+ .+/) if match then match[1] else nil end end - type: row conditions: - { column: blog-month, operator: "IS NOT NULL" }
これで、このURLの部分が
http://d.hatena.ne.jp/Kazuhira/20160214/1455460595
こうなります。
201602
こちらが、後の集計対象となります。
実行。
$ embulk run config.yml 2016-02-20 19:32:22.284 +0900: Embulk v0.8.3 2016-02-20 19:32:25.415 +0900 [INFO] (0001:transaction): Loaded plugin embulk-output-elasticsearch (0.2.1) 2016-02-20 19:32:25.574 +0900 [INFO] (0001:transaction): Loaded plugin embulk-filter-column (0.4.0) 2016-02-20 19:32:25.600 +0900 [INFO] (0001:transaction): Loaded plugin embulk-filter-ruby_proc (0.2.0) 2016-02-20 19:32:25.655 +0900 [INFO] (0001:transaction): Loaded plugin embulk-filter-row (0.2.0) 2016-02-20 19:32:25.732 +0900 [INFO] (0001:transaction): Loaded plugin embulk-parser-apache-custom-log (0.4.0) 2016-02-20 19:32:25.808 +0900 [INFO] (0001:transaction): Listing local files at directory 'logs' filtering filename by prefix 'access.log' 2016-02-20 19:32:25.824 +0900 [INFO] (0001:transaction): Loading files [logs/access.log] 2016-02-20 19:32:25.857 +0900 [INFO] (0001:transaction): since format parameter is not given, use DateTimeFormatter. 2016-02-20 19:32:25.956 +0900 [INFO] (0001:transaction): Using local thread executor with max_threads=16 / output tasks 8 = input tasks 1 * 8 2016-02-20 19:32:26.228 +0900 [INFO] (0001:transaction): [Revanche] loaded [], sites [] 2016-02-20 19:32:27.715 +0900 [INFO] (0001:transaction): Executing plugin with 'replace' mode. 2016-02-20 19:32:28.004 +0900 [INFO] (0001:transaction): Inserting data into index[apache-accesslog_20160220-193224] 2016-02-20 19:32:28.009 +0900 [INFO] (0001:transaction): {done: 0 / 1, running: 0} 2016-02-20 19:32:28.047 +0900 [INFO] (0038:task-0000): [Bast] loaded [], sites [] 2016-02-20 19:32:28.152 +0900 [INFO] (0038:task-0000): [Caregiver] loaded [], sites [] 2016-02-20 19:32:28.204 +0900 [INFO] (0038:task-0000): [Sultan] loaded [], sites [] 2016-02-20 19:32:28.256 +0900 [INFO] (0038:task-0000): [Karolina Dean] loaded [], sites [] 2016-02-20 19:32:28.328 +0900 [INFO] (0038:task-0000): [Zach] loaded [], sites [] 2016-02-20 19:32:28.393 +0900 [INFO] (0038:task-0000): [Zeitgeist] loaded [], sites [] 2016-02-20 19:32:28.452 +0900 [INFO] (0038:task-0000): [Aleksei Sytsevich] loaded [], sites [] 2016-02-20 19:32:28.521 +0900 [INFO] (0038:task-0000): [Man-Brute] loaded [], sites [] 2016-02-20 19:32:29.076 +0900 [INFO] (0038:task-0000): since format parameter is not given, use DateTimeFormatter. 2016-02-20 19:32:29.077 +0900 [INFO] (0038:task-0000): since format parameter is not given, use DateTimeFormatter. 2016-02-20 19:32:29.078 +0900 [INFO] (0038:task-0000): LogFormat : %h %l %u %t "%r" %>s %O "%{Referer}i" "%{User-Agent}i" 2016-02-20 19:32:29.078 +0900 [INFO] (0038:task-0000): RegExp : (.*) ([^\s]*) (.*) \[([^\]]+)\] "(.*)" ([1-9]\d{2}) (-?\d+|-) "(.*)" "(.*)" 2016-02-20 19:32:29.079 +0900 [INFO] (0038:task-0000): replacement : 9 2016-02-20 19:32:31.149 +0900 [INFO] (0038:task-0000): Execute 127 bulk actions 2016-02-20 19:32:31.509 +0900 [INFO] (elasticsearch[Bast][listener][T#1]): 127 bulk actions succeeded 2016-02-20 19:32:31.653 +0900 [INFO] (0038:task-0000): Execute 109 bulk actions 2016-02-20 19:32:31.822 +0900 [INFO] (elasticsearch[Caregiver][listener][T#1]): 109 bulk actions succeeded 2016-02-20 19:32:31.868 +0900 [INFO] (0038:task-0000): Execute 19 bulk actions 2016-02-20 19:32:31.916 +0900 [INFO] (elasticsearch[Sultan][listener][T#1]): 19 bulk actions succeeded 2016-02-20 19:32:31.984 +0900 [INFO] (0038:task-0000): Execute 86 bulk actions 2016-02-20 19:32:32.124 +0900 [INFO] (elasticsearch[Karolina Dean][listener][T#1]): 86 bulk actions succeeded 2016-02-20 19:32:32.177 +0900 [INFO] (0038:task-0000): Execute 100 bulk actions 2016-02-20 19:32:32.243 +0900 [INFO] (elasticsearch[Zach][listener][T#1]): 100 bulk actions succeeded 2016-02-20 19:32:32.280 +0900 [INFO] (0038:task-0000): Execute 110 bulk actions 2016-02-20 19:32:32.486 +0900 [INFO] (elasticsearch[Zeitgeist][listener][T#1]): 110 bulk actions succeeded 2016-02-20 19:32:32.558 +0900 [INFO] (0038:task-0000): Execute 96 bulk actions 2016-02-20 19:32:32.669 +0900 [INFO] (elasticsearch[Aleksei Sytsevich][listener][T#1]): 96 bulk actions succeeded 2016-02-20 19:32:32.697 +0900 [INFO] (0038:task-0000): Execute 98 bulk actions 2016-02-20 19:32:32.776 +0900 [INFO] (elasticsearch[Man-Brute][listener][T#1]): 98 bulk actions succeeded 2016-02-20 19:32:32.795 +0900 [INFO] (0001:transaction): {done: 1 / 1, running: 0} 2016-02-20 19:32:32.823 +0900 [INFO] (0001:transaction): Assigned alias[apache-accesslog] to index[apache-accesslog_20160220-193224] 2016-02-20 19:32:32.941 +0900 [INFO] (main): Committed. 2016-02-20 19:32:32.942 +0900 [INFO] (main): Next config diff: {"in":{"last_path":"logs/access.log"},"out":{}}
無事、取り込まれたみたいです。
Kibanaで可視化する
最後は、Kibanaで可視化です。
データを認識することを確認して、今回は「Vertical bar chart」で表示してみました。
※標準は、古い月から並べています
ひと月あたり、20回以上投稿することもあれば、1回しか書いていない月とかありました…。意外と多く書いている月もあるんだなーと、ちょっと驚いた部分も。
まとめ
とりあえず簡単にですが、Elasticsearchに取り込んだデータをKibanaでグラフ表示してみました。今後は、他の用途のデータなどを可視化して、Kibanaに慣れていければなぁと思います。