Amazon S3のアクセスログを手早く集計する

Amazon S3アクセスログを集計する必要が有ったので、方法を調べてみた。(Amazon S3側のアクセスログ出力は設定済みという前提)

ログは意外にややこしいフォーマットで下記の解析をすれば良いとは分かるのだけど、今回はそこまでの時間がなかったので、ツールの導入をメインに調べてみた。

AmazonS3のサーバーアクセスログをパースして変換するために使った方法をご紹介します。

[AWS][Java] AmazonS3のサーバーアクセスログを解析する [正規表現] | DevelopersIO

必要な要件は下記の通り。

  • 簡単に道入出来ること
    • サーバの運用管理者が使うような高機能なものでは無くて良く、機能は限られていても良いので簡単に導入出来て直ぐに使えるツールが望ましい。
  • 必要な情報は限定的
    • Amazon S3アクセスログなのだから、処理時間などは不要。単純にリソースへのアクセス件数や時刻などが分かれば良い。

いろいろ試したところ、下記の"request-log-analyzer"が最も使い勝手が良く、出力結果も期待に沿ったものだった。(出力例はこちら

A command line tool that parses your log files to create reports.

request-log-analyzer

導入方法は下記の通り。

$ gem install request-log-analyzer

あとはAmazon S3からダウンロードしたログフォルダを指定すれば、結果がHTMLに出力された。

$ request-log-analyzer -f amazon_s3 --output html --file report.html ./logs/

使い勝手が良好だったので、s3cmdを使ってAmazon S3からのログのダウンロード(同期)も自動化させたスクリプトを用意した。これなら、「ローカルに無い差分のログファイルのみをダウンロードしてから、全てのログファイルを対象にアクセスログを集計する」ことが可能になる。

#!/bin/sh
s3cmd sync --skip-existing --delete-removed s3://foo/bar/logs/ ./logs/
request-log-analyzer -f amazon_s3 --output html --file report.html ./logs/

スクリプトはJenkinsのジョブに放り込んで、定期的に実行させるように設定しておいた。優れたツールを使って公開してくれている方に感謝!


関連

Redmineのwikiにチケット件数の集計を表示させる

Redminewikiには、基本的に箇条書きで情報を記載するようにしている。記載は簡単だし、見た目も良い。しかしながら、中には箇条書きではなく表形式で表示させた方が分かりやすい類の情報も有るし、Excel文化の名残りなのか何でもかんでも表にしなければ気が済まない人もいる。

箇条書きなら簡単なwikiも表を書くとなると少々手間がかかる。行と列の整合が取れるように注意深く記載するものの、セルのデータが予期せぬ箇所に表示されたりするので、プレビュー機能を使って何度も記載を確認する羽目になる。

そんな訳で、ある程度まとまった情報を記載する際には、一旦Excelで記載したデータを、下記のツールを使ってwikiフォーマットへ変換するようにしている。これならデータの配置に悩む必要はないし、少なくともイチから全て表を書くことに比べたら、手間は大きく削減できる。

ただし、気をつけねばならないのは、そんなデータの2重管理をしてしまうと「wikiの記載は古いので、最新のデータはExcelファイルを見てね」と本末転倒な状況になってしまう事だ。これでは何のためにwikiを使っているのか分からなくなってしまう。

しかし、(記載する内容に依存するけれど)チケットの件数を表形式で示すのが目的なら、もっと手っ取り早い方法がある。例えば、下記のような「各バージョン毎のチケット件数」を表示させる場合、wikiをベタに記載するとこのような形になる。

|バージョン|件数|
|version#61|1|
|version#63|9|
|version#65|5|
|合計|15|

実際の表示例は、もちろんこんな形だ。分かりやすい形の表現ではあるけど、誰かが更新しない限り、wikiは古い情報を表示し続けることになる。

バージョン 件数
ver-1.0 1
ver-1.1 9
ver-1.2 5
合計 15

ここでWiki Listsプラグインを導入して、"-c"オプションを指定して件数を表示させる機能を使う。

上記の場合のwikiの記載は、下記の形になる。

|バージョン|件数|
|version#61|{{ref_issues(-p=darkside,-i=12, -c)}}|
|version#63|{{ref_issues(-p=darkside,-i=14, -c)}}|
|version#65|{{ref_issues(-p=darkside,-i=16, -c)}}|
|合計|{{ref_issues(-p=darkside,-i=20, -c)}}|

wikiを表示させた結果は全く同じだれど、ページ更新時にチケットのクエリが必ず処理されるので、「wikiを表示させれば常に最新情報が表示される」メリットがある。例えば、打合せ中にチケットのステータスを更新して、その直後にwikiページを表示させると、既にチケットの変更が反映された表(件数)が表示されるわけだ。これなら、わざわざ手作業でwikiを更新する必要が無く、大変便利だ。

マクロだらけのややこしい記載になってしまうし、テーブルが大きくなるほどクエリの件数も増えるので表示に時間がかかってしまうデメリットは有るけれど、メリットの方が遥かに大きいのでお気に入りの記載方法だったりする。


OSX10.11のRedmineを3.2.0に更新した

OSX 10.11 (El Capitan) にて、Redmineを3.0.0から3.2.0に更新した。

環境

問題

  • ffiインストールにてエラーが発生した。

詳細

  • いつもの様に更新を試みるが、ffiのNative Extension生成に失敗してしまう。
$ bundle install
...
Installing ffi 1.9.10 with native extensions

Gem::Installer::ExtensionBuildError: ERROR: Failed to build gem native extension.

    /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/ruby extconf.rb
checking for ffi.h... no
checking for ffi.h in /usr/local/include,/usr/include/ffi... no
checking for rb_thread_blocking_region()... yes
checking for rb_thread_call_with_gvl()... yes
checking for rb_thread_call_without_gvl()... yes
checking for ffi_prep_cif_var()... no
creating extconf.h
creating Makefile

make "DESTDIR=" 
...
An error occurred while installing ffi (1.9.10), and Bundler cannot continue.
Make sure that `gem install ffi -v '1.9.10'` succeeds before bundling.

原因

  • "checking for ffi.h... no"という表示が出る時点でおかしい。libffiはHomebrewで既にインストール済みなのだ。
$ brew info libffi
libffi: stable 3.0.13 (bottled), HEAD [keg-only]
Portable Foreign Function Interface library
https://sourceware.org/libffi/
/usr/local/Cellar/libffi/3.0.13 (13 files, 388K)
  Poured from bottle
From: https://github.com/Homebrew/homebrew/blob/master/Library/Formula/libffi.rb
==> Caveats
This formula is keg-only, which means it was not symlinked into /usr/local.

Some formulae require a newer version of libffi.

Generally there are no consequences of this for you. If you build your
own software and it requires this formula, you'll need to add to your
build variables:

    LDFLAGS:  -L/usr/local/opt/libffi/lib
  • しかし、上記の説明をよく見てみれば"keg-only"なのでリンクは設定されていないらしい。Fomulaを確認したところ、下記の記載があった。

flags = ["-L#{lib}", "-lffi", "-I#{lib}/libffi-#{version}/include"]

https://github.com/Homebrew/homebrew/blob/master/Library/Formula/libffi.rb
  • パスを明示したところ、今度はインストールに成功した。
$ gem install ffi -- --with-opt-dir=/usr/local/opt/libffi --with-opt-include=/usr/local/opt/libffi/lib/libffi-3.0.13/include
Building native extensions with: '--with-opt-dir=/usr/local/opt/libffi --with-opt-include=/usr/local/opt/libffi/lib/libffi-3.0.13/include'
This could take a while...
Successfully installed ffi-1.9.10
Parsing documentation for ffi-1.9.10
Done installing documentation for ffi after 0 seconds
1 gem installed
  • この後の更新手順は前回までと同様だ。

既知の問題

LDAP認証の問題

既に3.2.1向けのチケットに上がっているけれど、LDAPアカウントの生成時に非ASCII文字が含まれていると失敗してしまうらしい。これは会社内で利用するには厳しい制約だろう。プラグインを以前のバージョン(?)へ戻せば回避は可能なようだ。

I confirm this bug, redmine 3.2.0 with net-ldap-0.12.1. If I rollback on net-ldap-0.3.1 - all is ok.

Defect #21453: LDAP account creation fails when first name/last name contain non ASCII - Redmine


Jenkinsの環境変数BUILD_IDは消えた

以前のエントリ「Jenkinsの環境変数のバグにはまる」で取り上げたJenkinsの環境変数BUILD_IDのバグをウォッチしていたのだけど、結局、修正されないことに決まってしまった。下記に理由が載っている。

Assuming the maintainer of the Zentimestamp plugin is responsive (I do not follow it), this should be simple to implement there, and provide more flexibility anyway. (The old BUILD_ID lacked millisecond resolution, whereas Jenkins now supports >1 build of a given project per second, making timestamps in that format nonunique; and also used the server’s current timezone, leading to subtle problems during DST changes or when simply moving servers.)

[JENKINS-26520] Environment Variables BUILD_ID and BUILD_NUMBER now return the same value - Jenkins JIRA

理由は分かるのだけど、突然の方針変更に失望を隠せないのは他の方も同様のようだ。

i'm very disapointed to see this new message ! won't fix ...

[JENKINS-26520] Environment Variables BUILD_ID and BUILD_NUMBER now return the same value - Jenkins JIRA

2015/4/12にリリースされたver1.609にて対応済みというステータスになっており、サイトに掲載されているドキュメントも新しい説明に更新されている。

What's new in 1.609 (2015/04/12)
Documentation for $BUILD_ID did not reflect current reality (issue 26520)

Changelog

BUILD_ID The current build id, such as "2005-08-22_23-59-59" (YYYY-MM-DD_hh-mm-ss, defunct since version 1.597)

Building a software project - Jenkins - Jenkins Wiki

暫定的な解決策は、上記のバグレポートのコメントに載っているように、EnvInjectプラグインを導入して下記のgroovyスクリプトを使うことだ。これにより、BUILD_IDは従来通りの形式で使うことが出来る。

return [BUILD_ID:currentBuild.getTime().format("yyyy-MM-dd_HH-mm-ss")]

手元の環境ではJenkinsのジョブをRubyで書くことが多いのだけど、その際の開発途中で「Jenkins外で動作する時も、Jenkins内で動作する時と同じ環境変数BUILD_IDを参照したい」ことがあるので、実は下記のような処理が入っていて影響を受けない処理も有る。

ENV['BUILD_ID'] = Time.now.strftime("%Y-%m-%d_%H-%M-%S")

もっとも、BUILD_IDはもはや意味を成さないし、下手に残しておくと誤解を招く恐れもある。今後の長期的なメンテナンスも考慮すれば、BUILD_TIMESTAMPなど他の変数へ置き換える必要がありそうだ。


Excelファイルの更新をJenkinsに監視させる

サーバに置かれたExcelファイルの更新を定期的に確認するという心躍る楽しい作業が有る。たまにファイルを見に行って、ファイルが更新されているか否か確認するだけの単純作業とは言え、せっかく確認しても更新されているとは限らないし、第一、桜が花開く春の季節にそんな退屈な仕事をするようでは心が寒くなってしまう。そこでJenkinsにファイル監視を代行させることにした。

前提条件

  • Windows上でJenkinsを稼働させている
  • サーバに置かれたExcelファイルはJenkinsからアクセス可能である(アクセス可能な権限でJenkinsが動作している)

ファイル更新の監視

Jenkinsのジョブのトリガーには、ファイル監視を行うFSTriggerプラグインを利用した。単純に更新のタイミングさえ分かれば良いのなら、これだけで良い。

FSTrigger provides polling mechanisms to monitor a file system and trigger a build if a file or a set of files have changed.

FSTrigger Plugin - Jenkins - Jenkins Wiki

最終更新者の取得

せっかくなのでExcelファイルのプロパティも知らせて欲しい。そんな情報はxdoc2txtで取得可能だ。

xdoc2txtはPDF,WORD,EXCEL,一太郎などの各種バイナリ文書から、テキスト要素を抽出する汎用テキストコンバータであり、Windowsコマンドラインで動作します。

xdoc2txt

こんなバッチファイルを用意しておき、Jenkinsのジョブで呼び出す。この時、"-p"オプションを使うとExcelファイルのプロパティが出力される。

chcp 65001
set XDOC2TXT="C:\Program Files\xd2tx200\command\xdoc2txt.exe"
set SRC_FILE="\\path\to\file.xlsx"

echo __START_OF_LOG__
%XDOC2TXT% -p -8 %SRC_FILE%
echo __END_OF_LOG__

メール送信処理にはEmail-ext pluginを利用した。

This plugin allows you to configure every aspect of email notifications. You can customize when an email is sent, who should receive it, and what the email says.

https://wiki.jenkins-ci.org/display/JENKINS/Email-ext+plugin

メール本文として「ログの中でデリミタで指定された範囲」を選択出来るので、上記で使ったデリミタを指定する。

${BUILD_LOG_EXCERPT, start="__START_OF_LOG__", end="__END_OF_LOG__"}

結果的に、こんな内容のメールが届くことになる。Excelファイルの最終更新者や更新日時が分かるのは便利だ。(もちろん、短時間に何人も更新したら途中の更新履歴までは分からないけど、そこは無視する)

<Author>foo</Author>
<LastAuthor>rabbit2go</LastAuthor>
<ApplicationName>Microsoft Excel</ApplicationName>
<EditTime>2015/03/31 20:08:42</EditTime>
<LastPrinted>2015/03/18 10:35:03</LastPrinted>
<Created>2014/10/11 18:08:32</Created>

変更箇所の差分取得

せっかくxdoc2txtを使うのだから、テキスト化した情報を元に「ファイル更新内容」を通知してみる。具体的には下記のようなバッチファイルを呼び出せば良い。

chcp 65001
set XDOC2TXT="C:\Program Files\xd2tx200\command\xdoc2txt.exe"
set SRC_FILE="\\path\to\file.xlsx"

set OUT_TXT=out.txt
set PREVIOUS_TXT=previous.txt
if exist %PREVIOUS_TXT% DEL %PREVIOUS_TXT%
if exist %OUT_TXT% COPY %OUT_TXT% %PREVIOUS_TXT%

%XDOC2TXT% -8 %SRC_FILE% > %OUT_TXT%

set CMD_DIFF="C:\Program Files\Gow\bin\diff.exe"
set DIFF_TXT="diff.txt"
if exist %PREVIOUS_TXT% %CMD_DIFF% %OUT_TXT% %PREVIOUS_TXT% > %DIFF_TXT%

echo __START_OF_LOG__
if exist %DIFF_TXT% cat %DIFF_TXT%
echo __END_OF_LOG__

やっていることは単純で、Excelファイルの内容をテキストに変換して一旦保存しておき、次回のExcelファイル確認時に、両者の差分を取り出すだけだ。上記と同じようにログでデリミタを指定しておくと、前回の確認時からの変更点のみが記載された、こんなメールが届くことになる。

29c29
< foo foo@xxxx
---
> foo ○ ○ ○ foo@xxx
44c44
< 人数 14 16 18	
---
> 人数 15 17 19	

これならExcelファイルを開くこと無く、手っ取り早く内容を確認できて便利だ。

なお、ここでdiffの取得には、手元の環境に入っていたgowのコマンドを使った。

Gow (Gnu On Windows) is the lightweight alternative to Cygwin. It uses a convenient NSIS installer that installs over 100 extremely useful open source UNIX applications compiled as native win32 binaries.

Home · bmatzelle/gow Wiki · GitHub

セルのデータ取得

差分のデータなんか要らない、ピンポイントで特定のセルのデータだけ分かれば良いというケースもある。そこでRubyからExcelファイルを読み込むためのライブラリであるRooを使ってセルの値を抽出した。

Roo implements read access for all common spreadsheet types.

GitHub - roo-rb/roo: Roo provides an interface to spreadsheets of several sorts.

Excelファイルのセルの値を取り出す処理は下記の通り。Rubyでたった2行で済む手軽さだ。Jenkinsからはこのスクリプトを呼び出す。ログからメールで通知する処理は、上記と同様に行えば良い。

require "roo"
xlsx = Roo::Spreadsheet.open('file.xlsx')
puts xlsx.sheet(0).cell(2,2)

たった1行のメールでも必要なデータさえ載っていれば、価値ある情報となる。退屈な事務作業も、少しばかりの小道具を使うと楽に片付けることが出来るという一例でした。


OSX10.10のRedmineを3.0.0に更新した

OSX 10.10 (Yosetmite)にて、Redmineを2.6.1から3.0.0に更新した。

環境

問題

  • bundlerにてエラーが発生した。

詳細

  • いつもの様に更新を試みるが、(これまたいつもの様に)エラーが発生してしまう。今回はbundlerだ。
$ bundle install
`x64_mingw` is not a valid platform. The available options are: [:ruby, :ruby_18,
:ruby_19, :ruby_20, :mri, :mri_18, :mri_19, :mri_20, :rbx, :jruby, :mswin, :mingw,
:mingw_18, :mingw_19, :mingw_20]

原因

  • "x64_mingw"とは一体どういう意味だろう?原因を調べたところ、既に問題として報告されていた。

":x64_mingw" is defined in bundler 1.4.0.rc.1.
...
Bundler did not release 1.4.0, so do we need to suggests using 1.5.0?

Defect #19172: "gem update bundler" suggestion for "`x64_mingw` is not a valid platform" - Redmine
  • 確認したところ、確かに少々古いバージョンのbundlerが使われていたようだ。
$ bundle --version
Bundler version 1.3.5
  • コメント欄の指示に従って、bundlerを更新してみる。

And could you try this command?
$ gem update bundler

Defect #19172: "gem update bundler" suggestion for "`x64_mingw` is not a valid platform" - Redmine
$ sudo gem update bundler
Password:
Updating installed gems
Updating bundler
Fetching: bundler-1.8.3.gem (100%)
Successfully installed bundler-1.8.3
Parsing documentation for bundler-1.8.3
Installing ri documentation for bundler-1.8.3
Installing darkfish documentation for bundler-1.8.3
Gems updated: bundler
  • 今度は無事にインストールが成功した。
$ bundle install
Fetching gem metadata from https://rubygems.org/.........
Fetching version metadata from https://rubygems.org/..
Resolving dependencies...
Using rake 10.4.2
...
Bundle complete! 25 Gemfile dependencies, 62 gems now installed.
  • この後の更新手順は前回までと同様で、nokogiriやrmagickでも特に問題は発生しなかった。

プラグイン

redmine_issues_summary_graph
$ rake db:migrate RAILS_ENV="production" 
An error occurred while loading the routes definition of redmine_issues_summary_graph plugin (/Library/Server/Web/Data/redmine-3.0.0/plugins/redmine_issues_summary_graph/config/routes.rb): You should not use the `match` method in your router without specifying an HTTP method.
If you want to expose your action to both GET and POST, add `via: [:get, :post]` option.
If you want to expose your action to GET, use `get` in the router:
  Instead of: match "controller#action" 
  Do: get "controller#action".
  • 既に3.0.0向けの変更は入っているようだが、手元の環境では動作しなかった。

Redmine 3.0.0 support

Releases · suer/redmine_issues_summary_graph · GitHub
redmine_wiki_extensions
  • ページ表示時にエラーが発生したので、redmine_wiki_extensions を外した。
Started GET "/redmine/issues/622" for fe80::2a37:37ff:fe1a:2520 at 2015-03-02 22:35:32 +0900
Processing by IssuesController#show as HTML
  Parameters: {"id"=>"622"}
  Current user: rabbit2go (id=2)
  Rendered issues/_action_menu.html.erb (12.3ms)
  Rendered issue_relations/_form.html.erb (3.1ms)
  Rendered issues/_relations.html.erb (19.8ms)
  Rendered issues/_history.html.erb (173.4ms)
  Rendered issues/_action_menu.html.erb (3.3ms)
  Rendered issues/_form_custom_fields.html.erb (0.7ms)
  Rendered issues/_attributes.html.erb (241.9ms)
  Rendered plugins/redmine_wiki_extensions/app/views/wiki_extensions/_issues_form_details_bottom.html.erb (13.0ms)
  Rendered issues/_form.html.erb (303.0ms)
  Rendered issues/_edit.html.erb (308.4ms)
  Rendered issues/show.html.erb within layouts/base (934.0ms)
Completed 500 Internal Server Error in 1324ms
  • 既に3.0.0向けの変更は加わっているので、間もなく出て来そうだ。

Compatible with Redmine3

haru_iida / redmine_wiki_extensions / Commits — Bitbucket
  • Rails 4移行に伴う問題の原因は、下記に説明されていた。

Rails 4に移行して発生するエラーで多いのは、config/routes.rbにおいて、match行に:via指定が省略されている行です。Rails 4から、:via指定が必須になっています。

Redmine 3.0.0とプラグイン動作メモ - torutkのブログ


デブサミ2015に参加した

先週金曜日(2015/02/20)はデブサミ2015に参加してきた。会場はいつもの目黒雅叙園で来場者も多く、活気のあるイベントだった。

様々な抵抗や障壁を乗り越えて、アクションを成功させるためには、継続と改善のプロセスが必要です。言い換えれば、継続的にアクションを育てていくことが必要なのです。今年のデブサミは、Growthというキーワードを掲げ、アクションを育て、成長しようとするエンジニアを応援していきたいと考えています。

Developers Summit 2015 概要

参加したのは一日だけだったけど、聞いた講演の中では特に下記が印象に残った。

おさえておきたいモダンなチーム開発を支えるツール連携

個人的には未だに「Microsoftの人」という印象が拭い切れない、アトラシアンの長沢さんによる講演。今回は自社製品の一連のツールを使って、企画案件の登録から実開発に至る至る一連のワークフローをデモしていた。普段、Redmineでのチケット管理に慣れている身としては、作業項目の登録から進捗管理、成果物のコミットなどの作業は目新しいものではないけど、専用ツールを駆使した流れるような操作性は圧巻で、さすがに上手く出来ていると感心してしまった。無償ツールと有償ツールの比較はもちろんフェアではないけれど、少しばかりの投資で作業効率が大きく改善できるのなら、それは充分に検討に値するものだと思っている。無償で使えるツールも有るらしいので、今後の可能性を探るべく調べてみたいと思っている。

テストケースの優先順位をつけてテストを最適化しよう!

最近のテストツールの技術進歩には目覚ましいものがあるけど、今回のCoverityもその一つだと思う。講演では、Coverityがランタイム系の障害検出に重点を置いていることが紹介され、「ソースに変更が加わっているが、テスト出来ていない処理」を見つけ出すためのテストケース作成支援の機能が説明されていた。単なる静的解析や、テスト結果に基づくカバレッジ分析は昔からあるけれど、このツールのようにテストの実行結果を元にテストケースへフィードバックしてくれる仕組みは頼もしい存在だと思う。事例では、リグレッションテストでの対象を効率的に絞り込むことにより、リリース期間を1/3に短縮したという航空会社のサイトが紹介され、ツールの威力を実感できる内容だった。

Agile TED

開発現場にてふり返りを行ってプロセス改善を進めた事例や、プロジェクトに関わる用語(一例が「ドバイ」ですね)を作って共通の理解を深めた事例など、現場からのボトムアップの事例が幾つも紹介されていた。いずれも開発チームも(発表用に幾分脚色されているとはいえ)実に楽しそうに改善が行われている点が興味深く、ボトムアップ的なアプローチながら、組織自身の改善力として目覚ましいものが有るのは素晴らしいと思う。

従来の伝統的な開発の歴史を持つ組織だと、様々な規則、ルール、手順などががんじがらめに決められていて、開発プロセスとしては確かに正論なのだけど、現場はその決まりに機械的に従うことだけを要求され、ひたすら「やらされ感」だけが残る運営になりがちだ。双方の開発チームの運営として同じようなゴールを目指しているはずなのに、一方では自分たちの課題として認識して自ら改善を行い、また一方では他人事のような改善を半ば義務的に強いられている。聞いてみたことは無いけれど、開発者自身の仕事の満足度は両者で大きく異なるのだろうと思っている。