GoAccessでLTSV形式のアクセスログをリアルタイムに解析する


# GoAccessとは

[GoAccess](http://goaccess.prosoftcorp.com/ "GoAccess")は、CLIで動作するリアルタイムアクセスログ解析ツールです。
[htop](http://hisham.hm/htop/ "htop")を眺める感覚でリアルタイムにログ解析ができます。

![goaccess01](https://24.media.tumblr.com/2922d2b50299a8bc678a81b58480fba2/tumblr_n5hi4d6lu81tbcnimo1_500.png)

[GoAccessでリアルタイムにアクセスログを解析する](http://qiita.com/ariarijp/items/0a57996a93b4b4149a88) でより分かりやすく解説されています。

# 背景

リアルタイムアクセスログ解析といえば、いまどきは ```fluentd + elasticsearch + kibana3``` のような構成が主流だと思います。簡単に設定できるうえ、見た目にもきれいで分かりやすいですからね。

```fluentd + elasticsearch + kibana3``` の構成を組む場合、ウェブサーバーのログをLTSV形式で出力するようにしておくと ```fluentd``` でパースするときに難解な正規表現を書く必要がなくなり便利なのですが、それだとGoAccessでパースができなくなります。

せっかくなので ```fluentd + elasticsearch + kibana3``` の構成を組みつつ、GoAccessでも解析できるようにしたいと思います。

# 導入

早速、GoAccessをインストールしましょう。

## 環境

* [DigitalOcean](https://www.digitalocean.com/?refcode=1253632a31d5 "DigitalOcean")
* CentOS6.5 x86_64
* nginx 1.4.3
* goaccess 0.7.1

## インストール（RHEL)

GoAccessは ```yum``` に ```epel``` リポジトリを追加すれば簡単にインストールできます。

```
sudo yum --enablerepo=epel -y install goaccess
```

srcからビルドしたい場合は [公式ドキュメント](http://goaccess.prosoftcorp.com/download#installation) を参照してください

# 設定

## nginx

nginx.confにLTSV形式でログを出力するためのフォーマットを追加します。

```/etc/nginx/nginx.conf

log_format  ltsv  "host:$remote_addr"
                  "\tuser:$remote_user"
                  "\ttime:$time_local"
                  "\treq:$request"
                  "\tstatus:$status"
                  "\tsize:$body_bytes_sent"
                  "\treferer:$http_referer"
                  "\tua:$http_user_agent"
                  "\tforwardedfor:$http_x_forwarded_for"
                  "\treqtime:$request_time"
                  "\tapptime:$upstream_response_time";

```

nginx.confにフォーマットを追加したら、アクセスログをLTSV形式で出力するように変更します。

```/etc/nginx/conf.d/ap.conf

access_log  /var/log/nginx/ap.access.log  ltsv;

```

設定が終わったらnginxをリロードします。

```
sudo /bin/bash -c "service nginx configtest && service nginx reload"
```

## apache

apacheの場合は以下の設定をLogFormatとして追加します。
（最後のapptimeの項目は便宜上nginxのフォーマットと揃えるためのダミーです）

```/etc/httpd/conf/httpd.conf

LogFormat "\
host:%h\
\tuser:%u\
\ttime:%t\
\ttime:%{%d/%b/%Y:%T %z}t\
\treq:%r\
\tstatus:%>s\
\tsize:%b\
\treferer:%{Referer}i\
\tua:%{User-Agent}i\
\tforwardedfor:%{X-Forwarded-For}i\
\treqtime:%D\
\tapptime:%D" ltsv

```

フォーマットを追加したら、アクセスログをLTSV形式で出力するように変更します。

```/etc/httpd/conf.d/ap.conf

CustomLog logs/access_log ltsv

```

nginx同様、設定が終わったらapacheをリロードします。

```
sudo /bin/bash -c "service httpd configtest && service httpd reload"
```

## GoAccess

LTSV形式のログをパースするためのカスタムログフォーマットを定義します。

### nginx用

```/etc/goaccess/ltsv.nginx

color_scheme 2
date_format %d/%b/%Y
time_format %H:%M:%S
log_format host:%h\tuser:%^\ttime:%d:%t %^\treq:%r\tstatus:%s\tsize:%b\treferer:%R\tua:%u\tforwardedfor:%^\treqtime:%T\tapptime:%^

```

この定義で、下記フォーマットのログをパースできるようになります。

```/var/log/nginx/ap.access.log

host:192.168.1.23     user:-     time:18/Mar/2014:04:46:39 +0000     req:GET /fonts/fontawesome-webfont.woff?v=4.0.3 HTTP/1.1     status:200     size:44432     referer:https://example.com/invitation     ua:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.152 Safari/537.36     forwardedfor:192.168.1.23     reqtime:0.006     apptime:0.006

```

### apache用

```/etc/goaccess/ltsv.httpd

color_scheme 2
date_format %d/%b/%Y:%T %z
log_format host:%h\tuser:%^\ttime:%d\treq:%r\tstatus:%s\tsize:%b\treferer:%R\tua:%u\tforwardedfor:%^\treqtime:%D\tapptime:%^

```

この定義で、下記フォーマットのログをパースできるようになります。

```/var/log/httpd/ap.access.log

host:192.168.1.23     user:-     time:18/Mar/2014:04:46:39 +0900	req:GET /fonts/fontawesome-webfont.woff?v=4.0.3	HTTP/1.1	status:200	size:7149	referer:-	ua:Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)	forwardedfor:-	reqtime:435959	apptime:435965

```

# 使い方

使い方はとても簡単、```解析したいログファイル``` と ```カスタムログフォーマット``` を指定するだけです。

```usage
goaccess -f /var/log/nginx/ap.access.log -p /etc/goaccess/ltsv.nginx
```

# おまけ

上記の使い方では、サーバー毎のアクセス解析しかできません。

ウェブサーバーが複数台あれば ```fluentd``` でアクセスログをログサーバーに集約していることが多いと思います。
せっかくなので、```fluentd``` が集約している最中のアクセスログも解析できるようにしてみましょう。

## td-agent

### ウェブサーバー側の設定

```/etc/td-agent/conf.d/nginx.conf

# input
<source>
  type         tail
  format       ltsv
  path         /var/log/nginx/ap.access.log
  pos_file     /var/log/td-agent/nginx.ap.access.log.pos
  time_format  %d/%b/%Y:%H:%M:%S %z
  tag          nginx.ap.access.log
</source>

# output
<match nginx.ap.access.log>
  type               forward
  send_timeout       60s
  recover_wait       60s
  heartbeat_interval 1s
  phi_threshold      8
  hard_timeout       60s

  <server>
    name   logme
    host   192.168.1.45
    port   24224
    weight 60
  </server>
</match>

```

### ログサーバー側の設定

[fluent-plugin-typecast](https://github.com/tarom/fluent-plugin-typecast)で特定のフィールドを数値に変換しています。

```/etc/td-agent/conf.d/nginx.conf

# input
<source>
  type forward
  port 24224
  bind 0.0.0.0
</source>

# output
<match nginx.ap.access.log>
  type        typecast
  item_types  time:time,status:integer,size:integer,reqtime:float,apptime:float
  time_format %d/%b/%Y:%H:%M:%S %z
  prefix      cast
</match>

<match cast.nginx.ap.access.log>
  type              file
  path              /var/log/td-agent/ap.access.log
  time_slice_format %Y%m%d
  time_slice_wait   5m
  compress          gzip
</match>

```

各ウェブサーバーのアクセスログがログサーバーに集約されます。

```/var/log/td-agent

-rw-r--r-- 1 td-agent td-agent  8345  3月 20 00:05 2014 ap.access.log.20140319_0.log.gz
-rw-r--r-- 1 td-agent td-agent  5490  3月 21 00:05 2014 ap.access.log.20140320_0.log.gz
-rw-r--r-- 1 td-agent td-agent  5852  3月 21 09:36 2014 ap.access.log.20140321.b4f51468d38e9c1d8

```

## GoAccess

```fluentd``` が出力するログをパースするためのカスタムログフォーマットを定義します。

### nginx用

```/etc/goaccess/ltsv.fluentd.nginx

color_scheme 2
date_format %F
log_format %dT%^\t%^\t{"host":"%h","user":"%^","req":"%r","status":%s,"size":%b,"referer":"%R","ua":"%u","forwardedfor":"%^","reqtime":%T,"apptime":%^}

```

この定義で、下記フォーマットのログをパースできるようになります。

```/var/log/td-agent/ap.access.log

2014-03-21T04:30:51+00:00     cast.nginx.ap.access.log     {"host":"192.168.1.23","user":"-","req":"GET /test HTTP/1.1","status":302,"size":0,"referer":"https://example.com/invitation","ua":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.152 Safari/537.36","forwardedfor":"192.168.1.23","reqtime":1.012,"apptime":1.012}

```

### apache用

```/etc/goaccess/ltsv.fluentd.httpd

color_scheme 2
date_format %F
log_format %dT%^\t%^\t{"host":"%h","user":"%^","req":"%r","status":%s,"size":%b,"referer":"%R","ua":"%u","forwardedfor":"%^","reqtime":%D,"apptime":%^}

```

この定義で、下記フォーマットのログをパースできるようになります。

```/var/log/td-agent/ap.access.log

2014-03-21T04:30:51+00:00     cast.httpd.ap.access.log     {"host":"192.168.1.23","user":"-","req":"GET /test HTTP/1.1","status":302,"size":0,"referer":"https://example.com/invitation","ua":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.152 Safari/537.36","forwardedfor":"192.168.1.23","reqtime":435959,"apptime":435959}

```

## 使い方

使い方はこうなります。

```usage
goaccess -f /var/log/td-agent/ap.access.log.20140321.b4f51468d38e9c1d8 -p /etc/goaccess/ltsv.fluentd.nginx
```

```fluentd``` で集約しているログを解析する場合、解析されるタイミングは ```fluentd``` の ```buffer``` からログファイルに ```flush``` されるタイミングに依存するため、ウェブサーバーで直接ログファイルを解析している場合と比べリアルタイム性はかなり落ちます。

# まとめ

インストールも使い方もとても簡単ですね。 ```tail``` や ```grep``` で地味にログをチェックするという作業に ```GoAccess``` で解析するという一手間を加えてみるといいです。

```404``` になっているファイルやロードに時間がかかっているリソースも一目瞭然なので開発時から活躍してくれると思います。