├── README.md ├── js ├── README.md ├── js-3.css ├── js-3.html ├── js-3.js ├── lib │ ├── qunit-1.18.0.css │ └── qunit-1.18.0.js ├── main.js ├── test.html └── test.js ├── perl ├── README.md ├── lib │ ├── Log.pm │ ├── LogCounter.pm │ └── Parser.pm ├── main.pl ├── main2.pl ├── main3.pl └── t │ ├── log.t │ ├── log_counter.t │ └── parser.t ├── sample_data ├── large_log.ltsv └── log.ltsv └── scala ├── .gitignore ├── README.md ├── project ├── Build.scala ├── build.properties └── plugin.sbt └── src ├── main └── scala │ └── hatena │ └── intern │ ├── Exercise1.scala │ ├── Exercise2.scala │ └── Exercise3.scala └── test └── scala └── hatena └── intern ├── Exercise1.scala ├── Exercise2.scala ├── Exercise3.scala └── UnitSpec.scala /README.md: -------------------------------------------------------------------------------- 1 | Hatena-Intern-Exercise 2 | ======================================== 3 | 4 | 基本的な教材は Hatena::Textbook など 5 | 6 | - [Perl 課題](./perl/) 7 | - [Scala 課題](./scala/) 8 | - [JavaScript 課題](./js/) 9 | 10 | ## はじめに 11 | 12 | ※この項は全課題共通になります。はじめに目を通しておきましょう。 13 | 14 | LTSV (Labeled Tab-separated Values) とはラベル付きのTSVフォーマットです。 15 | LTSVの1レコードは、`label:value` という形式で表されたラベル付きの値がタブ文字区切りで並びます。 16 | 17 | 以下に LTSV の例を示します。 18 | 19 | * `sample_data/ltsv.log` 20 | 21 | ``` 22 | host:127.0.0.1 user:frank epoch:1372694390 req:GET /apache_pb.gif HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 23 | host:127.0.0.1 user:john epoch:1372794390 req:GET /apache_pb.gif HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 24 | host:127.0.0.1 user:- epoch:1372894390 req:GET /apache_pb.gif HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 25 | host:127.0.0.1 user:frank epoch:1372694390 req:GET /apache_pb.gif HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 26 | host:127.0.0.1 user:frank epoch:1372794395 req:GET /notfound.gif HTTP/1.0 status:404 size:100 referer:- 27 | ``` 28 | 29 | 例えば、1レコード目の host の値は 127.0.0.1 であり、2レコード目の referer の値は http://b.hatena.ne.jp/hotentry になります。LTSV についてより詳しくは、以下を参照して下さい。 30 | 31 | * http://blog.stanaka.org/entry/2013/02/05/214833 32 | * http://ltsv.org/ 33 | 34 | ## 課題の提出方法について 35 | 36 | 課題の提出は、このリポジトリをForkしてそこにコミットしていってください。 37 | 38 | 課題はそれぞれ複数問あるので、問題ごとにコミットを分けてください(すべての回答を一つのコミットにまとめないようにお願いします)。コミットの粒度は1問1コミットでなくても、細かくコミットしていて構いません。 39 | -------------------------------------------------------------------------------- /js/README.md: -------------------------------------------------------------------------------- 1 | # JavaScript 課題 2 | 3 | この課題は、JavaScript の基礎と簡単な DOM 操作 (DOM 操作とは、HTML 文書の構造などをプログラムで変更するもので、web 開発においては主にユーザーインターフェイスの動的な変更のために使用されます) の理解を確かめるものです。 4 | JavaScript の処理系として [Node.js](http://nodejs.org/) なども存在しますが、ここでは 5 | [Firefox](http://www.mozilla.jp/firefox/) や [Chrome](http://www.google.co.jp/intl/ja/chrome/browser/) といった最近のブラウザを使用するようにしてください。 6 | (クロスブラウザについて考慮する必要はありません。 Firefox や Chrome といった最近のブラウザの最新バージョンで動作するようにしてください。) 7 | 8 | ## 基礎編 9 | 10 | 基礎課題 (課題 JS-3 まで) では、JavaScript を使って Web サーバーのアクセスログ (を模した文字列) を解析し、HTML ドキュメント上にアクセスログの表を表示する、という処理を実装していきます。 11 | これらの課題の解答には、外部ライブラリを使用しないようにしてください。 12 | 13 | ### 課題 JS-0 (課題準備) 14 | 15 | 各自、自分が主に使用しているブラウザにおける開発者用のツールを使ってみてください。 16 | Firefox や Chorome がおすすめですが、Opera や Internet Explorer (10 以降)、Safari といったブラウザでも大丈夫です。 17 | 多くの開発者用のツールでは、次のような機能があります。 18 | 19 | * ページの HTML 文書構造を動的に表示する 20 | * JavaScript 用のコンソールで JavaScript 実行時のエラーを確認する 21 | * デバッガで JavaScript の処理を対話形式で進める 22 | * その際、スコープ内の変数の値を確認できる 23 | * イベント発火に対してブレイクポイントを設定する 24 | 25 | 使いこなせるようになる必要はありませんが、少なくとも JavaScript のエラーが発生しているのかどうかを確認できるようになっていれば、課題を進めやすいと思います。 26 | 27 | 例として Firefox や Chrome に付属している Web 開発用のツールを紹介します。 28 | 29 | * Firefox では、メニューバーの 「ツール」 中の 「Web 開発」 から、web 開発向けの便利なツールを開くことができます。 30 | * Web コンソール: [Web コンソール - Tools | MDN](https://developer.mozilla.org/ja/docs/Tools/Web_Console) 31 | * デバッガ: [デバッガ - Tools | MDN](https://developer.mozilla.org/ja/docs/Tools/Debugger) 32 | * また、Firefox 拡張機能として [Firebug](https://addons.mozilla.org/ja/firefox/addon/firebug/) というものもあります 33 | * Chrome には、デベロッパー ツールという開発者向けのツールが付属しています。 34 | * [Chrome DevTools — Google Developers](https://developers.google.com/chrome-developer-tools/) 35 | * メニューバーの「表示」->「開発/管理」->「デベロッパーツール」など 36 | 37 | ### 課題 JS-1 38 | 39 | Web サーバーへのアクセスログを表す LTSV 形式の文字列を引数として受け取り、その文字列をパースしてオブジェクトの配列にして返す関数 `parseLTSVLog` を `js/main.js` ファイルに記述してください。 40 | アクセスログを表す LTSV 形式の文字列の例を次に示します。 41 | 42 | 注意) タブがスペースになっています。 43 | 44 | ``` 45 | path:/ epoch:200000 46 | path:/bookmark epoch:123456 47 | ``` 48 | 49 | 1 行が 1 つのリクエストに対応しています。 50 | 1 行を 1 つの JavaScript のオブジェクトにし、アクセスログ中のラベルと値をプロパティとして持たせるようにしてください。 51 | 基本的に、アクセスログ中の値は文字列としてプロパティの値にすれば良いですが、`epoch` というラベルの値についてのみは数値にしてください。 52 | 53 | すなわち、 54 | 55 | ```javascript 56 | var logStr = 57 | "path:/\tepoch:200000\n" + 58 | "path:/help\tepoch:300000\n" + 59 | "path:/\tepoch:250000\n"; 60 | ``` 61 | 62 | という文字列を引数として受け取り、 63 | 64 | ```javascript 65 | [ 66 | { path: "/", epoch: 200000 }, 67 | { path: "/help", epoch: 300000 }, 68 | { path: "/", epoch: 250000 }, 69 | ] 70 | ``` 71 | 72 | という構造の配列を返すように `parseLTSVLog` 関数を実装してください。 73 | 74 | 実際のアクセスログには様々なデータが含まれますが、課題では簡単のため `path` というラベルと `epoch` というラベルのみを考慮すればよいものとします。 75 | `path` というラベルの値には、URL として有効な文字列が与えられます。 76 | `epoch` というラベルの値としては、整数値とみなせる文字列が与えられます。 77 | 78 | 課題で指定されていない仕様は、自由に決めてよいものとします。 79 | 例えば、引数が与えられずに関数が呼び出された場合は、例外を送出しても良いですし、空の配列を返すようにしても構いません。 80 | 81 | なお、この関数のテストは、`js/test.html`、`js/test.js` に記述されています。 82 | ブラウザで `js/test.html` を表示すると、自動的にテストが走るようになっています。 83 | 最低限、最初から書かれているテストに通過するように `parseLTSVLog` 関数を実装してください。 84 | 余裕があれば、最初から書かれているテストにさらにテストを追加してみてください。 85 | (例えば、予期せぬ値が引数として渡された場合に関するテストなど。) 86 | テスト用のフレームワークとして [QUnit](http://qunitjs.com/) を使用しています。 87 | 88 | ### 課題 JS-2 89 | 90 | 次の 2 つの値を引数として受け取り、 91 | 92 | * 第 1 引数: `div` 要素を表す DOM オブジェクト 93 | * 第 2 引数: 課題 JS-1 で作成した関数 `parseLTSVLog` の返り値と同じ形式の配列 94 | 95 | 次のような表を (DOM 操作によって) 第 1 引数の `div` 要素の直下に生成する関数 `createLogTable` を js/main.js ファイルに記述してください。 96 | 97 | ```html 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 |
pathepoch
/200000
/help300000
/250000
106 | ``` 107 | 108 | `tbody` 要素内の `tr` 要素は、引数として受け取った配列の各要素に対応します。 109 | 表は 2 カラムにして、最初のカラムには `path` の値を、次のカラムには `epoch` の値を表示するようにしてください。 110 | 111 | また、引数として受け取った配列の最初の要素から順に、表の上から表示するようにしてください。 112 | 113 | ```javascript 114 | // `createLogTable` 関数の使用例 115 | var containerElem = document.getElementById("table-container"); // table-container という ID の div 要素が HTML ドキュメント中にあるとする 116 | createLogTable(containerElem, [ 117 | { path: "/", epoch: 200000 }, 118 | { path: "/help", epoch: 300000 }, 119 | { path: "/", epoch: 250000 } 120 | ]); 121 | ``` 122 | 123 | 課題 JS-1 と同じく、この関数のテストも `js/test.html`、`js/test.js` に記述されています。 124 | 最低限、最初から書かれているテストを通過するように実装し、余裕があれば新たにテストを追加してみてください。 125 | 126 | 参考になりそうな MDN (Mozilla Developer Network) のページを以下に挙げておきます。 127 | (ここで紹介しているメソッド以外を使用しても構いません。) 128 | 129 | * ノードに子ノードを追加: [Node.appendChild - Web API リファレンス | MDN](https://developer.mozilla.org/ja/docs/Web/API/Node.appendChild) 130 | * Element ノードの生成: [document.createElement - Web API リファレンス | MDN](https://developer.mozilla.org/ja/docs/Web/API/document.createElement) 131 | * ノードの子孫のテキストを設定したり読み取ったりする: [Node.textContent - Web API リファレンス | MDN](https://developer.mozilla.org/ja/docs/Web/API/Node.textContent) 132 | 133 | ### 課題 JS-3 134 | 135 | 課題 JS-1 と JS-2 で作成した関数を用いて、`js/js-3.html` のページが次のような動作をするように `js/js-3.js` に JavaScript の処理を記述しなさい。 136 | `js/main.js` には課題 JS-1 と JS-2 で作成した関数が記述されているものとします。 137 | 138 | * `js/js-3.html` の動作: ユーザーが `textarea` 要素に LTSV 形式のアクセスログを入力し、「表に出力する」 ボタンをクリックすると、`access-log-table-container` という `id` 属性をもつ要素の直下にアクセスログの表を表す `table` 要素が作られる。 139 | 140 | `js/js-3.html` の中身は次のとおりです。 141 | 142 | ```html 143 | 144 | 145 | 146 | 147 | Hatena-Intern-Exercise : JavaScript 148 | 149 | 150 | 151 |
152 |

JavaScript 事前課題

153 |

課題 JS-3 : イベントリスナを使用する

154 |

「表に出力する」 ボタンをクリックすると、テキストエリアに入力したログデータが表として表示されるようにしてください。 その際、課題 JS-1 と JS-2 で作成した関数を使用してください。

155 |

また、表 (table 要素) は table-container を id として持つ div 要素の子要素として作成するようにしてください。

156 |
157 | 162 |
163 |
164 |
165 |
166 | 167 | 168 | 169 | 170 | ``` 171 | 172 | 参考になりそうな MDN のページを以下に挙げておきます。 173 | (ここで紹介しているメソッド以外を使用しても構いません。) 174 | 175 | * id を指定して文書中の Element ノードを取得: [document.getElementById - Web API リファレンス | MDN](https://developer.mozilla.org/ja/docs/Web/API/document.getElementById) 176 | * イベントリスナを追加: [EventTarget.addEventListener - Web API リファレンス | MDN](https://developer.mozilla.org/ja/docs/Web/API/EventTarget.addEventListener) 177 | 178 | ## 応用編 179 | 180 | ### 課題 JS-4 181 | 182 | 課題 JS-3 で作成したものに、ログの検索機能を実装してください。 183 | 184 | 例えば、以下の様な機能が考えられます。 185 | - `path` を検索できる機能。例えば `bookmark` と入力して「検索」ボタンを押すと、`path` の `uri` に `bookmark` が含まれるログが表示される 186 | - `POST`のログしか表示しない機能 187 | - 2つ以上の条件を組み合わせて検索できる機能 188 | - インクリメンタルサーチする機能 189 | 190 | どのような検索機能を実装するか、どのような UI を作るかは全て自由とします。複数の機能を作ってもらっても構いません。またこの課題では好きな JavaScript ライブラリを使って構いません。ただし検索機能は自分で実装するようにして下さい。(= 検索機能を提供するライブラリの利用は禁止します。) 特にこだわりがなければ、jQuery や Underscore.js などのユーティリティを使うようにして下さい。 191 | 192 | この課題のための HTML ファイルや JS ファイルは、新たに作成してください (ファイル名は特に指定しませんが、js/js-4.html や js/js-4.js など、わかりやすい名前にしてください)。 193 | また、この課題で使うログデータは `../sample_data/log.ltsv` (または `large_log.ltsv` )にあるデータを ` 20 |
21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /js/js-3.js: -------------------------------------------------------------------------------- 1 | // 課題 JS-3 の実装をここに記述してください。 2 | -------------------------------------------------------------------------------- /js/lib/qunit-1.18.0.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * QUnit 1.18.0 3 | * http://qunitjs.com/ 4 | * 5 | * Copyright jQuery Foundation and other contributors 6 | * Released under the MIT license 7 | * http://jquery.org/license 8 | * 9 | * Date: 2015-04-03T10:23Z 10 | */ 11 | 12 | /** Font Family and Sizes */ 13 | 14 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { 15 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; 16 | } 17 | 18 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } 19 | #qunit-tests { font-size: smaller; } 20 | 21 | 22 | /** Resets */ 23 | 24 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { 25 | margin: 0; 26 | padding: 0; 27 | } 28 | 29 | 30 | /** Header */ 31 | 32 | #qunit-header { 33 | padding: 0.5em 0 0.5em 1em; 34 | 35 | color: #8699A4; 36 | background-color: #0D3349; 37 | 38 | font-size: 1.5em; 39 | line-height: 1em; 40 | font-weight: 400; 41 | 42 | border-radius: 5px 5px 0 0; 43 | } 44 | 45 | #qunit-header a { 46 | text-decoration: none; 47 | color: #C2CCD1; 48 | } 49 | 50 | #qunit-header a:hover, 51 | #qunit-header a:focus { 52 | color: #FFF; 53 | } 54 | 55 | #qunit-testrunner-toolbar label { 56 | display: inline-block; 57 | padding: 0 0.5em 0 0.1em; 58 | } 59 | 60 | #qunit-banner { 61 | height: 5px; 62 | } 63 | 64 | #qunit-testrunner-toolbar { 65 | padding: 0.5em 1em 0.5em 1em; 66 | color: #5E740B; 67 | background-color: #EEE; 68 | overflow: hidden; 69 | } 70 | 71 | #qunit-userAgent { 72 | padding: 0.5em 1em 0.5em 1em; 73 | background-color: #2B81AF; 74 | color: #FFF; 75 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; 76 | } 77 | 78 | #qunit-modulefilter-container { 79 | float: right; 80 | padding: 0.2em; 81 | } 82 | 83 | .qunit-url-config { 84 | display: inline-block; 85 | padding: 0.1em; 86 | } 87 | 88 | .qunit-filter { 89 | display: block; 90 | float: right; 91 | margin-left: 1em; 92 | } 93 | 94 | /** Tests: Pass/Fail */ 95 | 96 | #qunit-tests { 97 | list-style-position: inside; 98 | } 99 | 100 | #qunit-tests li { 101 | padding: 0.4em 1em 0.4em 1em; 102 | border-bottom: 1px solid #FFF; 103 | list-style-position: inside; 104 | } 105 | 106 | #qunit-tests > li { 107 | display: none; 108 | } 109 | 110 | #qunit-tests li.running, 111 | #qunit-tests li.pass, 112 | #qunit-tests li.fail, 113 | #qunit-tests li.skipped { 114 | display: list-item; 115 | } 116 | 117 | #qunit-tests.hidepass li.running, 118 | #qunit-tests.hidepass li.pass { 119 | visibility: hidden; 120 | position: absolute; 121 | width: 0px; 122 | height: 0px; 123 | padding: 0; 124 | border: 0; 125 | margin: 0; 126 | } 127 | 128 | #qunit-tests li strong { 129 | cursor: pointer; 130 | } 131 | 132 | #qunit-tests li.skipped strong { 133 | cursor: default; 134 | } 135 | 136 | #qunit-tests li a { 137 | padding: 0.5em; 138 | color: #C2CCD1; 139 | text-decoration: none; 140 | } 141 | 142 | #qunit-tests li p a { 143 | padding: 0.25em; 144 | color: #6B6464; 145 | } 146 | #qunit-tests li a:hover, 147 | #qunit-tests li a:focus { 148 | color: #000; 149 | } 150 | 151 | #qunit-tests li .runtime { 152 | float: right; 153 | font-size: smaller; 154 | } 155 | 156 | .qunit-assert-list { 157 | margin-top: 0.5em; 158 | padding: 0.5em; 159 | 160 | background-color: #FFF; 161 | 162 | border-radius: 5px; 163 | } 164 | 165 | .qunit-collapsed { 166 | display: none; 167 | } 168 | 169 | #qunit-tests table { 170 | border-collapse: collapse; 171 | margin-top: 0.2em; 172 | } 173 | 174 | #qunit-tests th { 175 | text-align: right; 176 | vertical-align: top; 177 | padding: 0 0.5em 0 0; 178 | } 179 | 180 | #qunit-tests td { 181 | vertical-align: top; 182 | } 183 | 184 | #qunit-tests pre { 185 | margin: 0; 186 | white-space: pre-wrap; 187 | word-wrap: break-word; 188 | } 189 | 190 | #qunit-tests del { 191 | background-color: #E0F2BE; 192 | color: #374E0C; 193 | text-decoration: none; 194 | } 195 | 196 | #qunit-tests ins { 197 | background-color: #FFCACA; 198 | color: #500; 199 | text-decoration: none; 200 | } 201 | 202 | /*** Test Counts */ 203 | 204 | #qunit-tests b.counts { color: #000; } 205 | #qunit-tests b.passed { color: #5E740B; } 206 | #qunit-tests b.failed { color: #710909; } 207 | 208 | #qunit-tests li li { 209 | padding: 5px; 210 | background-color: #FFF; 211 | border-bottom: none; 212 | list-style-position: inside; 213 | } 214 | 215 | /*** Passing Styles */ 216 | 217 | #qunit-tests li li.pass { 218 | color: #3C510C; 219 | background-color: #FFF; 220 | border-left: 10px solid #C6E746; 221 | } 222 | 223 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } 224 | #qunit-tests .pass .test-name { color: #366097; } 225 | 226 | #qunit-tests .pass .test-actual, 227 | #qunit-tests .pass .test-expected { color: #999; } 228 | 229 | #qunit-banner.qunit-pass { background-color: #C6E746; } 230 | 231 | /*** Failing Styles */ 232 | 233 | #qunit-tests li li.fail { 234 | color: #710909; 235 | background-color: #FFF; 236 | border-left: 10px solid #EE5757; 237 | white-space: pre; 238 | } 239 | 240 | #qunit-tests > li:last-child { 241 | border-radius: 0 0 5px 5px; 242 | } 243 | 244 | #qunit-tests .fail { color: #000; background-color: #EE5757; } 245 | #qunit-tests .fail .test-name, 246 | #qunit-tests .fail .module-name { color: #000; } 247 | 248 | #qunit-tests .fail .test-actual { color: #EE5757; } 249 | #qunit-tests .fail .test-expected { color: #008000; } 250 | 251 | #qunit-banner.qunit-fail { background-color: #EE5757; } 252 | 253 | /*** Skipped tests */ 254 | 255 | #qunit-tests .skipped { 256 | background-color: #EBECE9; 257 | } 258 | 259 | #qunit-tests .qunit-skipped-label { 260 | background-color: #F4FF77; 261 | display: inline-block; 262 | font-style: normal; 263 | color: #366097; 264 | line-height: 1.8em; 265 | padding: 0 0.5em; 266 | margin: -0.4em 0.4em -0.4em 0; 267 | } 268 | 269 | /** Result */ 270 | 271 | #qunit-testresult { 272 | padding: 0.5em 1em 0.5em 1em; 273 | 274 | color: #2B81AF; 275 | background-color: #D2E0E6; 276 | 277 | border-bottom: 1px solid #FFF; 278 | } 279 | #qunit-testresult .module-name { 280 | font-weight: 700; 281 | } 282 | 283 | /** Fixture */ 284 | 285 | #qunit-fixture { 286 | position: absolute; 287 | top: -10000px; 288 | left: -10000px; 289 | width: 1000px; 290 | height: 1000px; 291 | } 292 | -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | // 課題 JS-1: 関数 `parseLTSVLog` を記述してください 2 | 3 | // 課題 JS-2: 関数 `createLogTable` を記述してください 4 | -------------------------------------------------------------------------------- /js/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hatena-Intern-Exercise : JavaScript 6 | 7 | 8 | 9 |
10 |
11 | 12 | 13 | 14 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /js/test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | QUnit.module("課題 JS-1"); 4 | 5 | QUnit.test("関数定義の確認", function () { 6 | QUnit.ok(typeof parseLTSVLog === "function", "`parseLTSVLog` という名前の関数がある"); 7 | }); 8 | 9 | QUnit.test("`parseLTSVLog` 関数の動作確認", function () { 10 | var logStr; 11 | var logRecords; 12 | 13 | logStr = "path:/\tepoch:500000\n"; 14 | logRecords = parseLTSVLog(logStr); 15 | QUnit.deepEqual(logRecords, [ 16 | { path: "/", epoch: 500000 } 17 | ], "1 行のみのログデータが期待通りパースされる"); 18 | 19 | logStr = 20 | "path:/\tepoch:400000\n" + 21 | "path:/uname\tepoch:123456\n" + 22 | "path:/\tepoch:500000\n"; 23 | logRecords = parseLTSVLog(logStr); 24 | QUnit.deepEqual(logRecords, [ 25 | { path: "/", epoch: 400000 }, 26 | { path: "/uname", epoch: 123456 }, 27 | { path: "/", epoch: 500000 } 28 | ], "3 行からなるログデータが期待通りパースされる"); 29 | 30 | logStr = ""; 31 | logRecords = parseLTSVLog(logStr); 32 | QUnit.deepEqual(logRecords, [], "空文字列を渡したときは空の配列を返す"); 33 | 34 | // テストを追加する場合は、この下に追加しても構いませんし、 35 | // `QUnit.test` 関数や `QUnit.asyncTest` 関数を用いて別に定義しても良いです。 36 | 37 | }); 38 | 39 | QUnit.module("課題 JS-2"); 40 | 41 | QUnit.test("関数定義の確認", function () { 42 | QUnit.ok(typeof createLogTable === "function", "`createLogTable` という名前の関数がある"); 43 | }); 44 | 45 | QUnit.test("`createLogTable` 関数の動作確認", function () { 46 | // #qunit-fixture という要素は、QUnit が自動的に後片付けしてくれる要素 47 | var fixtureElem = document.getElementById("qunit-fixture"); 48 | var elem = fixtureElem.appendChild(document.createElement("div")); 49 | 50 | createLogTable(elem, [ 51 | { path: "/", epoch: 400000 }, 52 | { path: "/uname", epoch: 123456 }, 53 | { path: "/", epoch: 500000 }, 54 | ]); 55 | 56 | QUnit.strictEqual(elem.childNodes.length, 1, "渡した要素に子ノードが 1 つ追加されている"); 57 | var tableElem = elem.firstChild; 58 | QUnit.strictEqual(tableElem.tagName, "TABLE", "渡した要素に追加された子ノードは table 要素"); 59 | 60 | QUnit.strictEqual(tableElem.childNodes.length, 2, "table 要素の子ノードは 2 個"); 61 | var theadElem = tableElem.firstChild; 62 | QUnit.strictEqual(theadElem.tagName, "THEAD", "table 要素の 1 つ目の子ノードは thead 要素"); 63 | QUnit.strictEqual(theadElem.childNodes.length, 1, "thead 要素の子ノードは 1 個"); 64 | var tbodyElem = theadElem.nextSibling; 65 | QUnit.strictEqual(tbodyElem.tagName, "TBODY", "table 要素の 2 つ目の子ノードは tbody 要素"); 66 | QUnit.strictEqual(tbodyElem.childNodes.length, 3, "tbody 要素の子ノードは 3 個"); 67 | 68 | var expectedTrElem = document.createElement("tr"); 69 | 70 | var actualTheadTrElem = theadElem.firstChild; 71 | expectedTrElem.innerHTML = "pathepoch"; 72 | QUnit.ok(expectedTrElem.isEqualNode(actualTheadTrElem), 73 | "thead 要素の子要素の tr 要素の中身: " + expectedTrElem.innerHTML); 74 | 75 | var actualTbodyTrElem = tbodyElem.firstChild; 76 | expectedTrElem.innerHTML = "/400000"; 77 | QUnit.ok(expectedTrElem.isEqualNode(actualTbodyTrElem), 78 | "tbody 要素の子要素の 1 番目の tr 要素の中身: " + expectedTrElem.innerHTML); 79 | 80 | actualTbodyTrElem = actualTbodyTrElem.nextSibling; 81 | expectedTrElem.innerHTML = "/uname123456"; 82 | QUnit.ok(expectedTrElem.isEqualNode(actualTbodyTrElem), 83 | "tbody 要素の子要素の 2 番目の tr 要素の中身: " + expectedTrElem.innerHTML); 84 | 85 | actualTbodyTrElem = actualTbodyTrElem.nextSibling; 86 | expectedTrElem.innerHTML = "/500000"; 87 | QUnit.ok(expectedTrElem.isEqualNode(actualTbodyTrElem), 88 | "tbody 要素の子要素の 3 番目の tr 要素の中身: " + expectedTrElem.innerHTML); 89 | 90 | // テストを追加する場合は、この下に追加しても構いませんし、 91 | // `QUnit.test` 関数や `QUnit.asyncTest` 関数を用いて別に定義しても良いです。 92 | 93 | }); 94 | -------------------------------------------------------------------------------- /perl/README.md: -------------------------------------------------------------------------------- 1 | Hatena-Intern-Exercise 2 | ======================================== 3 | 4 | 以下基本的な教材は Hatena::Textbook など 5 | 6 | # Perl 課題 7 | 8 | ## 課題 Perl-1 9 | 10 | ["はじめに"の項](../README.md)にあった LTSV の1レコードを表す Log クラスを実装して下さい。 11 | (Perl のオブジェクト指向については Hatena::Textbook などを参考にして下さい。) 12 | 13 | Log クラスは以下のメソッドを持ちます。 14 | * `req` の値に含まれている HTTP メソッド名を返す `method` メソッド 15 | * `req` の値に含まれているリクエストパスを返す `path` メソッド 16 | * `req` の値に含まれているプロトコル名を返す `protocol` メソッド 17 | * `host` と `req` の値からリクエストされた uri を組み立てて返す `uri` メソッド 18 | * `epoch` が表している時刻を `YYYY-MM-DDThh:mm:ss` というフォーマットの文字列に変換して返す `time` メソッド 19 | * 日付を扱うモジュールを用いてかまいません 20 | * タイムゾーンは GMT として下さい 21 | 22 | 23 | * main.pl 24 | 25 | ```perl 26 | use strict; 27 | use warnings; 28 | 29 | use Log; 30 | 31 | my $log = Log->new( 32 | host => '127.0.0.1', 33 | user => 'frank', 34 | epoch => '1372694390', 35 | req => 'GET /apache_pb.gif HTTP/1.0', 36 | status => '200', 37 | size => '2326', 38 | referer => 'http://www.hatena.ne.jp/', 39 | ); 40 | print $log->method . "\n"; 41 | print $log->path . "\n"; 42 | print $log->protocol . "\n"; 43 | print $log->uri . "\n"; 44 | print $log->time . "\n"; 45 | ``` 46 | 47 | ```shell 48 | $ perl -Ilib main.pl 49 | ``` 50 | 51 | * 出力 52 | 53 | ```perl 54 | $ perl -Ilib main.pl 55 | GET 56 | /apache_pb.gif 57 | HTTP/1.0 58 | http://127.0.0.1/apache_pb.gif 59 | 2013-07-01T15:59:50 60 | ``` 61 | 62 | また、後述するリポジトリに含まれているテストを実行して、課題が正しく動作していることを確認してください。 63 | 64 | ```shell 65 | $ prove -lvr t/log.t 66 | (中略) 67 | Result: PASS 68 | ``` 69 | 70 | * Perl のテストの実行に関して、詳しくは [Hatena-Textbook](https://github.com/hatena/Hatena-Textbook/blob/master/oop-for-perl.md#-58) を参照して下さい。 71 | 72 | ## 課題 Perl-2 73 | 74 | ["はじめに"の項](../README.md)にあった LTSV フォーマットのログファイルを読み込み、以下のLog オブジェクトの配列を返すパーザーを Parser クラスとして実装してください 75 | 76 | * main2.pl 77 | 78 | ```perl 79 | use strict; 80 | use warnings; 81 | 82 | use Data::Dumper; 83 | 84 | use Parser; 85 | 86 | my $parser = Parser->new( filename => '../sample_data/log.ltsv' ); 87 | warn Dumper $parser->parse; 88 | ``` 89 | 90 | ```shell 91 | $ perl -Ilib main2.pl 92 | ``` 93 | 94 | * 出力フォーマット 95 | 96 | ```perl 97 | $VAR1 = [ 98 | bless( { 99 | 'epoch' => '1372694390', 100 | 'req' => 'GET /apache_pb.gif HTTP/1.0', 101 | 'status' => '200', 102 | 'user' => 'frank', 103 | 'referer' => 'http://www.hatena.ne.jp/', 104 | 'size' => '2326', 105 | 'host' => '127.0.0.1' 106 | }, 'Log' ), 107 | ... 108 | ]; 109 | ``` 110 | 111 | 実装に関しては、以下の条件を守って下さい。 112 | 113 | * LTSV をパースするCPANモジュールを用いないこと 114 | * LTSVでは値が定義されていない場合は`host:-`のように値に`-`が入ります。値が定義されていなかった場合には、そのラベルをハッシュリファレンスに含めないようにして下さい 115 | * map や grep といった関数を積極的に利用するとよいでしょう (必須ではない) 116 | 117 | 118 | 課題 Perl-1 と同じように、テストを実行して正しく動作していることを確認して下さい 119 | 120 | 121 | * Perl でファイルを読み込む方法はいろいろあって混乱しやすいため、以下にサンプルコードを載せておきます 122 | 123 | ```perl 124 | open my $fh, '<', 'log.ltsv' or die $!; 125 | my $line = <$fh>; # スカラーコンテキストなので一行読み込む 126 | my @lines = <$fh>; # リストコンテキストなので(残りの)すべての行を配列として読み込む 127 | print $line; 128 | print "----\n"; 129 | print @lines; 130 | ``` 131 | 132 | ## 課題 Perl-3 133 | 134 | Log を集計する LogCounter クラスを実装して下さい 135 | LogCounter は、HTTP サーバーエラー (500番台) の数を数える `count_error` とユーザーごとにログをまとめる `group_by_user` メソッドを持ちます。 136 | 137 | * main3.pl 138 | 139 | ```perl 140 | my $parser = Parser->new( filename => '../sample_data/log.ltsv' ); 141 | my $counter = LogCounter->new($parser->parse); 142 | print 'total error size: ' . $counter->count_error . "\n"; 143 | print Dumper $counter->group_by_user; 144 | ``` 145 | 146 | ```shell 147 | $ perl -Ilib main3.pl 148 | ``` 149 | 150 | * 出力 151 | 152 | ```perl 153 | $VAR1 = { 154 | 'guest' => [ 155 | bless( { 156 | 'epoch' => '1372894390', 157 | 'req' => 'GET /apache_pb.gif HTTP/1.0', 158 | 'status' => '503', 159 | 'referer' => 'http://www.example.com/start.html', 160 | 'size' => '9999', 161 | 'host' => '127.0.0.1' 162 | }, 'Log' ) 163 | ], 164 | 'frank' => [ 165 | bless( { 166 | 'epoch' => '1372694390', 167 | 'req' => 'GET /apache_pb.gif HTTP/1.0', 168 | 'status' => '200', 169 | 'user' => 'frank', 170 | 'referer' => 'http://www.hatena.ne.jp/', 171 | 'size' => '2326', 172 | 'host' => '127.0.0.1' 173 | }, 'Log' ), 174 | ... 175 | ], 176 | ... 177 | }; 178 | ``` 179 | 180 | * List::Util や List::UtilsBy といった便利なモジュールがありますが、今回は利用せずに実装してみましょう 181 | * user の値がない(値が `-` である)場合は `guest` という名前にして集計して下さい 182 | * いろいろな集計処理が考えられます。余裕があれば自分で考えて実装し、テストも追加してみて下さい。 183 | 184 | 185 | 186 | ## 課題 Perl-4 (応用編) 187 | 188 | 課題 Perl-3 までで作った LTSV パーザーを用いてこれまでに使ったデータより大きなデータから可視化を行い、標準出力にダイアグラム (グラフ) を出力してください。 189 | 190 | サンプルデータは `../sample_data/large_log.ltsv` にあるのでこれを利用してください。 191 | 192 | 様々なデータの可視化が考えられます。 193 | 194 | 例: 195 | 196 | * 日毎のリクエスト URI の分布 197 | * リクエスト URI 毎のステータスコード (`status`) の分布 198 | 199 | ダイアグラムの例: 200 | 201 | ``` 202 | / 203 | ---: 100 200 204 | 200:=================* 205 | 403:==* 206 | 404:===* 207 | 500:=* 208 | 209 | /bookmark 210 | ---: 100 200 211 | 200:=================* 212 | 403:==* 213 | 404:=======* 214 | 500:===* 215 | ``` 216 | 217 | 上記の例に限らず、自由に可視化の対象・方法を考えてみてください。 218 | -------------------------------------------------------------------------------- /perl/lib/Log.pm: -------------------------------------------------------------------------------- 1 | package Log; 2 | use strict; 3 | use warnings; 4 | 5 | sub new { 6 | my ($class, %args) = @_; 7 | return bless \%args, $class; 8 | } 9 | 10 | sub protocol { 11 | } 12 | 13 | sub method { 14 | } 15 | 16 | sub path { 17 | } 18 | 19 | sub uri { 20 | } 21 | 22 | sub time { 23 | } 24 | 25 | 1; 26 | -------------------------------------------------------------------------------- /perl/lib/LogCounter.pm: -------------------------------------------------------------------------------- 1 | package LogCounter; 2 | use strict; 3 | use warnings; 4 | 5 | sub new { 6 | my ($class, $logs) = @_; 7 | return bless { logs => $logs }, $class; 8 | }; 9 | 10 | sub group_by_user { 11 | } 12 | 13 | sub count_error { 14 | } 15 | 16 | 1; 17 | -------------------------------------------------------------------------------- /perl/lib/Parser.pm: -------------------------------------------------------------------------------- 1 | package Parser; 2 | use strict; 3 | use warnings; 4 | 5 | sub new { 6 | my ($class, %args) = @_; 7 | return bless \%args, $class; 8 | } 9 | 10 | sub parse { 11 | } 12 | 13 | 1; 14 | -------------------------------------------------------------------------------- /perl/main.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | use Log; 5 | 6 | my $log = Log->new( 7 | host => '127.0.0.1', 8 | user => 'frank', 9 | epoch => '1372694390', 10 | req => 'GET /apache_pb.gif HTTP/1.0', 11 | status => '200', 12 | size => '2326', 13 | referer => 'http://www.hatena.ne.jp/', 14 | ); 15 | print $log->method . "\n"; 16 | print $log->path . "\n"; 17 | print $log->protocol . "\n"; 18 | print $log->uri . "\n"; 19 | print $log->time . "\n"; 20 | -------------------------------------------------------------------------------- /perl/main2.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | use Data::Dumper; 5 | 6 | use Parser; 7 | 8 | my $parser = Parser->new( filename => '../sample_data/log.ltsv' ); 9 | warn Dumper $parser->parse; 10 | -------------------------------------------------------------------------------- /perl/main3.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | use Data::Dumper; 5 | 6 | use Parser; 7 | use LogCounter; 8 | 9 | my $parser = Parser->new( filename => '../sample_data/log.ltsv' ); 10 | my $counter = LogCounter->new($parser->parse); 11 | print 'total error size: ' . $counter->count_error . "\n"; 12 | print Dumper $counter->group_by_user; 13 | -------------------------------------------------------------------------------- /perl/t/log.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | use Test::More; 5 | 6 | use_ok 'Log'; 7 | 8 | my $log = Log->new( 9 | host => '127.0.0.1', 10 | user => 'frank', 11 | epoch => '1372694390', 12 | req => 'GET /apache_pb.gif HTTP/1.0', 13 | status => '200', 14 | size => '2326', 15 | referer => 'http://www.hatena.ne.jp/', 16 | ); 17 | is $log->method, 'GET'; 18 | is $log->path, '/apache_pb.gif'; 19 | is $log->protocol, 'HTTP/1.0'; 20 | is $log->uri, 'http://127.0.0.1/apache_pb.gif'; 21 | is $log->time, '2013-07-01T15:59:50'; 22 | 23 | done_testing(); 24 | -------------------------------------------------------------------------------- /perl/t/log_counter.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | use Test::More; 5 | 6 | use_ok 'LogCounter'; 7 | 8 | use Parser; 9 | use LogCounter; 10 | 11 | my $parser = Parser->new( filename => '../sample_data/log.ltsv' ); 12 | my $counter = LogCounter->new($parser->parse); 13 | is $counter->count_error, 2; 14 | is_deeply $counter->group_by_user, { 15 | 'john' => [ 16 | { 17 | 'epoch' => '1372794390', 18 | 'req' => 'GET /apache_pb.gif HTTP/1.0', 19 | 'status' => '200', 20 | 'user' => 'john', 21 | 'referer' => 'http://b.hatena.ne.jp/hotentry', 22 | 'size' => '1234', 23 | 'host' => '127.0.0.1' 24 | } 25 | ], 26 | 'frank' => [ 27 | { 28 | 'epoch' => '1372694390', 29 | 'req' => 'GET /apache_pb.gif HTTP/1.0', 30 | 'status' => '200', 31 | 'user' => 'frank', 32 | 'referer' => 'http://www.hatena.ne.jp/', 33 | 'size' => '2326', 34 | 'host' => '127.0.0.1' 35 | }, 36 | { 37 | 'epoch' => '1372694390', 38 | 'req' => 'GET /apache_pb.gif HTTP/1.0', 39 | 'status' => '500', 40 | 'user' => 'frank', 41 | 'referer' => 'http://www.hatena.ne.jp/', 42 | 'size' => '2326', 43 | 'host' => '127.0.0.1' 44 | }, 45 | { 46 | 'epoch' => '1372794395', 47 | 'req' => 'GET /notfound.gif HTTP/1.0', 48 | 'status' => '404', 49 | 'user' => 'frank', 50 | 'size' => '100', 51 | 'host' => '127.0.0.1' 52 | } 53 | ], 54 | 'guest' => [ 55 | { 56 | 'epoch' => '1372894390', 57 | 'req' => 'GET /apache_pb.gif HTTP/1.0', 58 | 'status' => '503', 59 | 'referer' => 'http://www.example.com/start.html', 60 | 'size' => '9999', 61 | 'host' => '127.0.0.1' 62 | } 63 | ] 64 | }; 65 | 66 | done_testing(); 67 | -------------------------------------------------------------------------------- /perl/t/parser.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | use Test::More; 5 | 6 | use_ok 'Parser'; 7 | 8 | my $parser = Parser->new( filename => '../sample_data/log.ltsv' ); 9 | isa_ok $parser, 'Parser'; 10 | 11 | my $parsed = $parser->parse; 12 | 13 | isa_ok $parsed->[0], 'Log'; 14 | isa_ok $parsed->[1], 'Log'; 15 | isa_ok $parsed->[2], 'Log'; 16 | 17 | is_deeply $parsed->[0]->to_hash, { 18 | 'status' => '200', 19 | 'time' => '2013-07-01T15:59:50', 20 | 'size' => '2326', 21 | 'uri' => 'http://127.0.0.1/apache_pb.gif', 22 | 'user' => 'frank', 23 | 'method' => 'GET', 24 | 'referer' => 'http://www.hatena.ne.jp/' 25 | }; 26 | is_deeply $parsed->[1]->to_hash, { 27 | 'status' => '200', 28 | 'time' => '2013-07-02T19:46:30', 29 | 'size' => '1234', 30 | 'uri' => 'http://127.0.0.1/apache_pb.gif', 31 | 'user' => 'john', 32 | 'method' => 'GET', 33 | 'referer' => 'http://b.hatena.ne.jp/hotentry' 34 | }; 35 | is_deeply $parsed->[2]->to_hash, { 36 | 'status' => '503', 37 | 'time' => '2013-07-03T23:33:10', 38 | 'method' => 'GET', 39 | 'referer' => 'http://www.example.com/start.html', 40 | 'size' => '9999', 41 | 'uri' => 'http://127.0.0.1/apache_pb.gif' 42 | }; 43 | 44 | done_testing(); 45 | -------------------------------------------------------------------------------- /sample_data/large_log.ltsv: -------------------------------------------------------------------------------- 1 | host:127.0.0.1 user:frank epoch:1436175526 req:GET /fotolife HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 2 | host:127.0.0.1 user:john epoch:1436175527 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 3 | host:127.0.0.1 user:- epoch:1436175528 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 4 | host:127.0.0.1 user:frank epoch:1436175529 req:GET /profile HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 5 | host:127.0.0.1 user:frank epoch:1436175529 req:GET /fotolife HTTP/1.0 status:404 size:100 referer:- 6 | host:127.0.0.1 user:frank epoch:1436175529 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 7 | host:127.0.0.1 user:john epoch:1436175529 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 8 | host:127.0.0.1 user:- epoch:1436175530 req:GET /fotolife HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 9 | host:127.0.0.1 user:frank epoch:1436175531 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 10 | host:127.0.0.1 user:frank epoch:1436175531 req:GET /blog HTTP/1.0 status:404 size:100 referer:- 11 | host:127.0.0.1 user:frank epoch:1436175536 req:GET /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 12 | host:127.0.0.1 user:john epoch:1436175537 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 13 | host:127.0.0.1 user:- epoch:1436175538 req:GET /profile HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 14 | host:127.0.0.1 user:frank epoch:1436175539 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 15 | host:127.0.0.1 user:frank epoch:1436175540 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 16 | host:127.0.0.1 user:frank epoch:1436175540 req:POST /keyword HTTP/1.0 status:404 size:2326 referer:http://www.hatena.ne.jp/ 17 | host:127.0.0.1 user:john epoch:1436175540 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 18 | host:127.0.0.1 user:- epoch:1436175543 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 19 | host:127.0.0.1 user:frank epoch:1436175543 req:POST /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 20 | host:127.0.0.1 user:frank epoch:1436175543 req:GET / HTTP/1.0 status:404 size:100 referer:- 21 | host:127.0.0.1 user:frank epoch:1436175550 req:GET /profile HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 22 | host:127.0.0.1 user:john epoch:1436175550 req:GET / HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 23 | host:127.0.0.1 user:- epoch:1436175550 req:GET /bookmark HTTP/1.0 status:302 size:9999 referer:http://www.example.com/start.html 24 | host:127.0.0.1 user:frank epoch:1436175551 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 25 | host:127.0.0.1 user:frank epoch:1436175551 req:GET /bookmark HTTP/1.0 status:404 size:100 referer:- 26 | host:127.0.0.1 user:frank epoch:1436175551 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 27 | host:127.0.0.1 user:john epoch:1436175552 req:POST /hoge HTTP/1.0 status:404 size:1234 referer:http://b.hatena.ne.jp/hotentry 28 | host:127.0.0.1 user:- epoch:1436175553 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 29 | host:127.0.0.1 user:frank epoch:1436175554 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 30 | host:127.0.0.1 user:frank epoch:1436175554 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 31 | host:127.0.0.1 user:frank epoch:1436175554 req:GET /fotolife HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 32 | host:127.0.0.1 user:john epoch:1436175557 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 33 | host:127.0.0.1 user:- epoch:1436175558 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 34 | host:127.0.0.1 user:frank epoch:1436175559 req:GET /profile HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 35 | host:127.0.0.1 user:frank epoch:1436175559 req:GET /fotolife HTTP/1.0 status:404 size:100 referer:- 36 | host:127.0.0.1 user:frank epoch:1436175559 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 37 | host:127.0.0.1 user:john epoch:1436175560 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 38 | host:127.0.0.1 user:- epoch:1436175561 req:GET /fotolife HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 39 | host:127.0.0.1 user:frank epoch:1436175561 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 40 | host:127.0.0.1 user:frank epoch:1436175561 req:GET /blog HTTP/1.0 status:404 size:100 referer:- 41 | host:127.0.0.1 user:frank epoch:1436175566 req:GET /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 42 | host:127.0.0.1 user:john epoch:1436175567 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 43 | host:127.0.0.1 user:- epoch:1436175568 req:GET /profile HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 44 | host:127.0.0.1 user:frank epoch:1436175569 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 45 | host:127.0.0.1 user:frank epoch:1436175570 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 46 | host:127.0.0.1 user:frank epoch:1436175570 req:POST /keyword HTTP/1.0 status:404 size:2326 referer:http://www.hatena.ne.jp/ 47 | host:127.0.0.1 user:john epoch:1436175570 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 48 | host:127.0.0.1 user:- epoch:1436175573 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 49 | host:127.0.0.1 user:frank epoch:1436175573 req:POST /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 50 | host:127.0.0.1 user:frank epoch:1436175573 req:GET / HTTP/1.0 status:404 size:100 referer:- 51 | host:127.0.0.1 user:frank epoch:1436175576 req:GET /profile HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 52 | host:127.0.0.1 user:john epoch:1436175576 req:GET / HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 53 | host:127.0.0.1 user:- epoch:1436175576 req:GET /bookmark HTTP/1.0 status:302 size:9999 referer:http://www.example.com/start.html 54 | host:127.0.0.1 user:frank epoch:1436175577 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 55 | host:127.0.0.1 user:frank epoch:1436175578 req:GET /bookmark HTTP/1.0 status:404 size:100 referer:- 56 | host:127.0.0.1 user:frank epoch:1436175579 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 57 | host:127.0.0.1 user:john epoch:1436175580 req:POST /hoge HTTP/1.0 status:404 size:1234 referer:http://b.hatena.ne.jp/hotentry 58 | host:127.0.0.1 user:- epoch:1436175581 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 59 | host:127.0.0.1 user:frank epoch:1436175590 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 60 | host:127.0.0.1 user:frank epoch:1436175591 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 61 | host:127.0.0.1 user:frank epoch:1436175592 req:GET /fotolife HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 62 | host:127.0.0.1 user:john epoch:1436175593 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 63 | host:127.0.0.1 user:- epoch:1436175594 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 64 | host:127.0.0.1 user:frank epoch:1436175594 req:GET /profile HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 65 | host:127.0.0.1 user:frank epoch:1436175594 req:GET /fotolife HTTP/1.0 status:404 size:100 referer:- 66 | host:127.0.0.1 user:frank epoch:1436175595 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 67 | host:127.0.0.1 user:john epoch:1436175596 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 68 | host:127.0.0.1 user:- epoch:1436175597 req:GET /fotolife HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 69 | host:127.0.0.1 user:frank epoch:1436175598 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 70 | host:127.0.0.1 user:frank epoch:1436175599 req:GET /blog HTTP/1.0 status:404 size:100 referer:- 71 | host:127.0.0.1 user:frank epoch:1436175600 req:GET /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 72 | host:127.0.0.1 user:john epoch:1436175601 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 73 | host:127.0.0.1 user:- epoch:1436175602 req:GET /profile HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 74 | host:127.0.0.1 user:frank epoch:1436175603 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 75 | host:127.0.0.1 user:frank epoch:1436175604 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 76 | host:127.0.0.1 user:frank epoch:1436175605 req:POST /keyword HTTP/1.0 status:404 size:2326 referer:http://www.hatena.ne.jp/ 77 | host:127.0.0.1 user:john epoch:1436175606 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 78 | host:127.0.0.1 user:- epoch:1436175607 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 79 | host:127.0.0.1 user:frank epoch:1436175608 req:POST /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 80 | host:127.0.0.1 user:frank epoch:1436175609 req:GET / HTTP/1.0 status:404 size:100 referer:- 81 | host:127.0.0.1 user:frank epoch:1436175610 req:GET /profile HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 82 | host:127.0.0.1 user:john epoch:1436175611 req:GET / HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 83 | host:127.0.0.1 user:- epoch:1436175612 req:GET /bookmark HTTP/1.0 status:302 size:9999 referer:http://www.example.com/start.html 84 | host:127.0.0.1 user:frank epoch:1436175613 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 85 | host:127.0.0.1 user:frank epoch:1436175614 req:GET /bookmark HTTP/1.0 status:404 size:100 referer:- 86 | host:127.0.0.1 user:frank epoch:1436175615 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 87 | host:127.0.0.1 user:john epoch:1436175616 req:POST /hoge HTTP/1.0 status:404 size:1234 referer:http://b.hatena.ne.jp/hotentry 88 | host:127.0.0.1 user:- epoch:1436175617 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 89 | host:127.0.0.1 user:frank epoch:1436175618 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 90 | host:127.0.0.1 user:frank epoch:1436175619 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 91 | host:127.0.0.1 user:frank epoch:1436175620 req:GET /fotolife HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 92 | host:127.0.0.1 user:john epoch:1436175620 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 93 | host:127.0.0.1 user:- epoch:1436175620 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 94 | host:127.0.0.1 user:frank epoch:1436175620 req:GET /profile HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 95 | host:127.0.0.1 user:frank epoch:1436175620 req:GET /fotolife HTTP/1.0 status:404 size:100 referer:- 96 | host:127.0.0.1 user:frank epoch:1436175620 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 97 | host:127.0.0.1 user:john epoch:1436175620 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 98 | host:127.0.0.1 user:- epoch:1436175620 req:GET /fotolife HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 99 | host:127.0.0.1 user:frank epoch:1436175620 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 100 | host:127.0.0.1 user:frank epoch:1436175620 req:GET /blog HTTP/1.0 status:404 size:100 referer:- 101 | host:127.0.0.1 user:frank epoch:1436175626 req:GET /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 102 | host:127.0.0.1 user:john epoch:1436175627 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 103 | host:127.0.0.1 user:- epoch:1436175628 req:GET /profile HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 104 | host:127.0.0.1 user:frank epoch:1436175629 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 105 | host:127.0.0.1 user:frank epoch:1436175630 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 106 | host:127.0.0.1 user:frank epoch:1436175631 req:POST /keyword HTTP/1.0 status:404 size:2326 referer:http://www.hatena.ne.jp/ 107 | host:127.0.0.1 user:john epoch:1436175632 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 108 | host:127.0.0.1 user:- epoch:1436175633 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 109 | host:127.0.0.1 user:frank epoch:1436175633 req:POST /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 110 | host:127.0.0.1 user:frank epoch:1436175633 req:GET / HTTP/1.0 status:404 size:100 referer:- 111 | host:127.0.0.1 user:frank epoch:1436175633 req:GET /profile HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 112 | host:127.0.0.1 user:john epoch:1436175633 req:GET / HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 113 | host:127.0.0.1 user:- epoch:1436175634 req:GET /bookmark HTTP/1.0 status:302 size:9999 referer:http://www.example.com/start.html 114 | host:127.0.0.1 user:frank epoch:1436175635 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 115 | host:127.0.0.1 user:frank epoch:1436175636 req:GET /bookmark HTTP/1.0 status:404 size:100 referer:- 116 | host:127.0.0.1 user:frank epoch:1436175637 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 117 | host:127.0.0.1 user:john epoch:1436175638 req:POST /hoge HTTP/1.0 status:404 size:1234 referer:http://b.hatena.ne.jp/hotentry 118 | host:127.0.0.1 user:- epoch:1436175639 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 119 | host:127.0.0.1 user:frank epoch:1436175640 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 120 | host:127.0.0.1 user:frank epoch:1436175641 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 121 | host:127.0.0.1 user:frank epoch:1436175642 req:GET /fotolife HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 122 | host:127.0.0.1 user:john epoch:1436175643 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 123 | host:127.0.0.1 user:- epoch:1436175644 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 124 | host:127.0.0.1 user:frank epoch:1436175645 req:GET /profile HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 125 | host:127.0.0.1 user:frank epoch:1436175646 req:GET /fotolife HTTP/1.0 status:404 size:100 referer:- 126 | host:127.0.0.1 user:frank epoch:1436175647 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 127 | host:127.0.0.1 user:john epoch:1436175652 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 128 | host:127.0.0.1 user:- epoch:1436175653 req:GET /fotolife HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 129 | host:127.0.0.1 user:frank epoch:1436175654 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 130 | host:127.0.0.1 user:frank epoch:1436175654 req:GET /blog HTTP/1.0 status:404 size:100 referer:- 131 | host:127.0.0.1 user:frank epoch:1436175654 req:GET /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 132 | host:127.0.0.1 user:john epoch:1436175654 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 133 | host:127.0.0.1 user:- epoch:1436175658 req:GET /profile HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 134 | host:127.0.0.1 user:frank epoch:1436175659 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 135 | host:127.0.0.1 user:frank epoch:1436175660 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 136 | host:127.0.0.1 user:frank epoch:1436175661 req:POST /keyword HTTP/1.0 status:404 size:2326 referer:http://www.hatena.ne.jp/ 137 | host:127.0.0.1 user:john epoch:1436175662 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 138 | host:127.0.0.1 user:- epoch:1436175662 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 139 | host:127.0.0.1 user:frank epoch:1436175662 req:POST /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 140 | host:127.0.0.1 user:frank epoch:1436175662 req:GET / HTTP/1.0 status:404 size:100 referer:- 141 | host:127.0.0.1 user:frank epoch:1436175662 req:GET /profile HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 142 | host:127.0.0.1 user:john epoch:1436175662 req:GET / HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 143 | host:127.0.0.1 user:- epoch:1436175662 req:GET /bookmark HTTP/1.0 status:302 size:9999 referer:http://www.example.com/start.html 144 | host:127.0.0.1 user:frank epoch:1436175662 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 145 | host:127.0.0.1 user:frank epoch:1436175662 req:GET /bookmark HTTP/1.0 status:404 size:100 referer:- 146 | host:127.0.0.1 user:frank epoch:1436175662 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 147 | host:127.0.0.1 user:john epoch:1436175662 req:POST /hoge HTTP/1.0 status:404 size:1234 referer:http://b.hatena.ne.jp/hotentry 148 | host:127.0.0.1 user:- epoch:1436175662 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 149 | host:127.0.0.1 user:frank epoch:1436175662 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 150 | host:127.0.0.1 user:frank epoch:1436175662 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 151 | host:127.0.0.1 user:frank epoch:1436175662 req:GET /fotolife HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 152 | host:127.0.0.1 user:john epoch:1436175662 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 153 | host:127.0.0.1 user:- epoch:1436175662 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 154 | host:127.0.0.1 user:frank epoch:1436175662 req:GET /profile HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 155 | host:127.0.0.1 user:frank epoch:1436175662 req:GET /fotolife HTTP/1.0 status:404 size:100 referer:- 156 | host:127.0.0.1 user:frank epoch:1436175662 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 157 | host:127.0.0.1 user:john epoch:1436175662 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 158 | host:127.0.0.1 user:- epoch:1436175662 req:GET /fotolife HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 159 | host:127.0.0.1 user:frank epoch:1436175663 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 160 | host:127.0.0.1 user:frank epoch:1436175664 req:GET /blog HTTP/1.0 status:404 size:100 referer:- 161 | host:127.0.0.1 user:frank epoch:1436175665 req:GET /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 162 | host:127.0.0.1 user:john epoch:1436175666 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 163 | host:127.0.0.1 user:- epoch:1436175667 req:GET /profile HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 164 | host:127.0.0.1 user:frank epoch:1436175668 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 165 | host:127.0.0.1 user:frank epoch:1436175669 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 166 | host:127.0.0.1 user:frank epoch:1436175670 req:POST /keyword HTTP/1.0 status:404 size:2326 referer:http://www.hatena.ne.jp/ 167 | host:127.0.0.1 user:john epoch:1436175671 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 168 | host:127.0.0.1 user:- epoch:1436175672 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 169 | host:127.0.0.1 user:frank epoch:1436175673 req:POST /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 170 | host:127.0.0.1 user:frank epoch:1436175674 req:GET / HTTP/1.0 status:404 size:100 referer:- 171 | host:127.0.0.1 user:frank epoch:1436175675 req:GET /profile HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 172 | host:127.0.0.1 user:john epoch:1436175676 req:GET / HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 173 | host:127.0.0.1 user:- epoch:1436175677 req:GET /bookmark HTTP/1.0 status:302 size:9999 referer:http://www.example.com/start.html 174 | host:127.0.0.1 user:frank epoch:1436175678 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 175 | host:127.0.0.1 user:frank epoch:1436175679 req:GET /bookmark HTTP/1.0 status:404 size:100 referer:- 176 | host:127.0.0.1 user:frank epoch:1436175680 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 177 | host:127.0.0.1 user:john epoch:1436175681 req:POST /hoge HTTP/1.0 status:404 size:1234 referer:http://b.hatena.ne.jp/hotentry 178 | host:127.0.0.1 user:- epoch:1436175682 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 179 | host:127.0.0.1 user:frank epoch:1436175682 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 180 | host:127.0.0.1 user:frank epoch:1436175682 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 181 | host:127.0.0.1 user:frank epoch:1436175682 req:GET /fotolife HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 182 | host:127.0.0.1 user:john epoch:1436175682 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 183 | host:127.0.0.1 user:- epoch:1436175683 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 184 | host:127.0.0.1 user:frank epoch:1436175684 req:GET /profile HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 185 | host:127.0.0.1 user:frank epoch:1436175685 req:GET /fotolife HTTP/1.0 status:404 size:100 referer:- 186 | host:127.0.0.1 user:frank epoch:1436175686 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 187 | host:127.0.0.1 user:john epoch:1436175687 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 188 | host:127.0.0.1 user:- epoch:1436175688 req:GET /fotolife HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 189 | host:127.0.0.1 user:frank epoch:1436175689 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 190 | host:127.0.0.1 user:frank epoch:1436175690 req:GET /blog HTTP/1.0 status:404 size:100 referer:- 191 | host:127.0.0.1 user:frank epoch:1436175691 req:GET /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 192 | host:127.0.0.1 user:john epoch:1436175692 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 193 | host:127.0.0.1 user:- epoch:1436175693 req:GET /profile HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 194 | host:127.0.0.1 user:frank epoch:1436175694 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 195 | host:127.0.0.1 user:frank epoch:1436175695 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 196 | host:127.0.0.1 user:frank epoch:1436175696 req:POST /keyword HTTP/1.0 status:404 size:2326 referer:http://www.hatena.ne.jp/ 197 | host:127.0.0.1 user:john epoch:1436175697 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 198 | host:127.0.0.1 user:- epoch:1436175698 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 199 | host:127.0.0.1 user:frank epoch:1436175699 req:POST /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 200 | host:127.0.0.1 user:frank epoch:1436175700 req:GET / HTTP/1.0 status:404 size:100 referer:- 201 | host:127.0.0.1 user:frank epoch:1436175701 req:GET /profile HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 202 | host:127.0.0.1 user:john epoch:1436175702 req:GET / HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 203 | host:127.0.0.1 user:- epoch:1436175703 req:GET /bookmark HTTP/1.0 status:302 size:9999 referer:http://www.example.com/start.html 204 | host:127.0.0.1 user:frank epoch:1436175704 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 205 | host:127.0.0.1 user:frank epoch:1436175705 req:GET /bookmark HTTP/1.0 status:404 size:100 referer:- 206 | host:127.0.0.1 user:frank epoch:1436175706 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 207 | host:127.0.0.1 user:john epoch:1436175707 req:POST /hoge HTTP/1.0 status:404 size:1234 referer:http://b.hatena.ne.jp/hotentry 208 | host:127.0.0.1 user:- epoch:1436175708 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 209 | host:127.0.0.1 user:frank epoch:1436175709 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 210 | host:127.0.0.1 user:frank epoch:1436175709 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 211 | host:127.0.0.1 user:frank epoch:1436175709 req:GET /fotolife HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 212 | host:127.0.0.1 user:john epoch:1436175709 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 213 | host:127.0.0.1 user:- epoch:1436175709 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 214 | host:127.0.0.1 user:frank epoch:1436175709 req:GET /profile HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 215 | host:127.0.0.1 user:frank epoch:1436175709 req:GET /fotolife HTTP/1.0 status:404 size:100 referer:- 216 | host:127.0.0.1 user:frank epoch:1436175709 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 217 | host:127.0.0.1 user:john epoch:1436175709 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 218 | host:127.0.0.1 user:- epoch:1436175710 req:GET /fotolife HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 219 | host:127.0.0.1 user:frank epoch:1436175711 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 220 | host:127.0.0.1 user:frank epoch:1436175712 req:GET /blog HTTP/1.0 status:404 size:100 referer:- 221 | host:127.0.0.1 user:frank epoch:1436175713 req:GET /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 222 | host:127.0.0.1 user:john epoch:1436175714 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 223 | host:127.0.0.1 user:- epoch:1436175715 req:GET /profile HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 224 | host:127.0.0.1 user:frank epoch:1436175716 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 225 | host:127.0.0.1 user:frank epoch:1436175717 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 226 | host:127.0.0.1 user:frank epoch:1436175718 req:POST /keyword HTTP/1.0 status:404 size:2326 referer:http://www.hatena.ne.jp/ 227 | host:127.0.0.1 user:john epoch:1436175719 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 228 | host:127.0.0.1 user:- epoch:1436175720 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 229 | host:127.0.0.1 user:frank epoch:1436175720 req:POST /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 230 | host:127.0.0.1 user:frank epoch:1436175720 req:GET / HTTP/1.0 status:404 size:100 referer:- 231 | host:127.0.0.1 user:frank epoch:1436175720 req:GET /profile HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 232 | host:127.0.0.1 user:john epoch:1436175720 req:GET / HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 233 | host:127.0.0.1 user:- epoch:1436175721 req:GET /bookmark HTTP/1.0 status:302 size:9999 referer:http://www.example.com/start.html 234 | host:127.0.0.1 user:frank epoch:1436175722 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 235 | host:127.0.0.1 user:frank epoch:1436175723 req:GET /bookmark HTTP/1.0 status:404 size:100 referer:- 236 | host:127.0.0.1 user:frank epoch:1436175724 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 237 | host:127.0.0.1 user:john epoch:1436175725 req:POST /hoge HTTP/1.0 status:404 size:1234 referer:http://b.hatena.ne.jp/hotentry 238 | host:127.0.0.1 user:- epoch:1436175726 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 239 | host:127.0.0.1 user:frank epoch:1436175727 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 240 | host:127.0.0.1 user:frank epoch:1436175728 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 241 | host:127.0.0.1 user:frank epoch:1436175729 req:GET /fotolife HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 242 | host:127.0.0.1 user:john epoch:1436175730 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 243 | host:127.0.0.1 user:- epoch:1436175731 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 244 | host:127.0.0.1 user:frank epoch:1436175732 req:GET /profile HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 245 | host:127.0.0.1 user:frank epoch:1436175733 req:GET /fotolife HTTP/1.0 status:404 size:100 referer:- 246 | host:127.0.0.1 user:frank epoch:1436175734 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 247 | host:127.0.0.1 user:john epoch:1436175735 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 248 | host:127.0.0.1 user:- epoch:1436175736 req:GET /fotolife HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 249 | host:127.0.0.1 user:frank epoch:1436175737 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 250 | host:127.0.0.1 user:frank epoch:1436175737 req:GET /blog HTTP/1.0 status:404 size:100 referer:- 251 | host:127.0.0.1 user:frank epoch:1436175737 req:GET /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 252 | host:127.0.0.1 user:john epoch:1436175737 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 253 | host:127.0.0.1 user:- epoch:1436175737 req:GET /profile HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 254 | host:127.0.0.1 user:frank epoch:1436175741 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 255 | host:127.0.0.1 user:frank epoch:1436175741 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 256 | host:127.0.0.1 user:frank epoch:1436175741 req:POST /keyword HTTP/1.0 status:404 size:2326 referer:http://www.hatena.ne.jp/ 257 | host:127.0.0.1 user:john epoch:1436175741 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 258 | host:127.0.0.1 user:- epoch:1436175742 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 259 | host:127.0.0.1 user:frank epoch:1436175743 req:POST /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 260 | host:127.0.0.1 user:frank epoch:1436175744 req:GET / HTTP/1.0 status:404 size:100 referer:- 261 | host:127.0.0.1 user:frank epoch:1436175745 req:GET /profile HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 262 | host:127.0.0.1 user:john epoch:1436175746 req:GET / HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 263 | host:127.0.0.1 user:- epoch:1436175747 req:GET /bookmark HTTP/1.0 status:302 size:9999 referer:http://www.example.com/start.html 264 | host:127.0.0.1 user:frank epoch:1436175748 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 265 | host:127.0.0.1 user:frank epoch:1436175749 req:GET /bookmark HTTP/1.0 status:404 size:100 referer:- 266 | host:127.0.0.1 user:frank epoch:1436175750 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 267 | host:127.0.0.1 user:john epoch:1436175751 req:POST /hoge HTTP/1.0 status:404 size:1234 referer:http://b.hatena.ne.jp/hotentry 268 | host:127.0.0.1 user:- epoch:1436175752 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 269 | host:127.0.0.1 user:frank epoch:1436175753 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 270 | host:127.0.0.1 user:frank epoch:1436175754 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 271 | host:127.0.0.1 user:frank epoch:1436175755 req:GET /fotolife HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 272 | host:127.0.0.1 user:john epoch:1436175756 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 273 | host:127.0.0.1 user:- epoch:1436175757 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 274 | host:127.0.0.1 user:frank epoch:1436175758 req:GET /profile HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 275 | host:127.0.0.1 user:frank epoch:1436175759 req:GET /fotolife HTTP/1.0 status:404 size:100 referer:- 276 | host:127.0.0.1 user:frank epoch:1436175760 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 277 | host:127.0.0.1 user:john epoch:1436175761 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 278 | host:127.0.0.1 user:- epoch:1436175762 req:GET /fotolife HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 279 | host:127.0.0.1 user:frank epoch:1436175763 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 280 | host:127.0.0.1 user:frank epoch:1436175764 req:GET /blog HTTP/1.0 status:404 size:100 referer:- 281 | host:127.0.0.1 user:frank epoch:1436175765 req:GET /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 282 | host:127.0.0.1 user:john epoch:1436175766 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 283 | host:127.0.0.1 user:- epoch:1436175767 req:GET /profile HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 284 | host:127.0.0.1 user:frank epoch:1436175768 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 285 | host:127.0.0.1 user:frank epoch:1436175769 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 286 | host:127.0.0.1 user:frank epoch:1436175770 req:POST /keyword HTTP/1.0 status:404 size:2326 referer:http://www.hatena.ne.jp/ 287 | host:127.0.0.1 user:john epoch:1436175771 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 288 | host:127.0.0.1 user:- epoch:1436175772 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 289 | host:127.0.0.1 user:frank epoch:1436175773 req:POST /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 290 | host:127.0.0.1 user:frank epoch:1436175774 req:GET / HTTP/1.0 status:404 size:100 referer:- 291 | host:127.0.0.1 user:frank epoch:1436175775 req:GET /profile HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 292 | host:127.0.0.1 user:john epoch:1436175776 req:GET / HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 293 | host:127.0.0.1 user:- epoch:1436175777 req:GET /bookmark HTTP/1.0 status:302 size:9999 referer:http://www.example.com/start.html 294 | host:127.0.0.1 user:frank epoch:1436175778 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 295 | host:127.0.0.1 user:frank epoch:1436175779 req:GET /bookmark HTTP/1.0 status:404 size:100 referer:- 296 | host:127.0.0.1 user:frank epoch:1436175780 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 297 | host:127.0.0.1 user:john epoch:1436175781 req:POST /hoge HTTP/1.0 status:404 size:1234 referer:http://b.hatena.ne.jp/hotentry 298 | host:127.0.0.1 user:- epoch:1436175782 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 299 | host:127.0.0.1 user:frank epoch:1436175783 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 300 | host:127.0.0.1 user:frank epoch:1436175784 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 301 | host:127.0.0.1 user:frank epoch:1436175785 req:GET /fotolife HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 302 | host:127.0.0.1 user:john epoch:1436175786 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 303 | host:127.0.0.1 user:- epoch:1436175787 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 304 | host:127.0.0.1 user:frank epoch:1436175788 req:GET /profile HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 305 | host:127.0.0.1 user:frank epoch:1436175789 req:GET /fotolife HTTP/1.0 status:404 size:100 referer:- 306 | host:127.0.0.1 user:frank epoch:1436175790 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 307 | host:127.0.0.1 user:john epoch:1436175791 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 308 | host:127.0.0.1 user:- epoch:1436175792 req:GET /fotolife HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 309 | host:127.0.0.1 user:frank epoch:1436175793 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 310 | host:127.0.0.1 user:frank epoch:1436175794 req:GET /blog HTTP/1.0 status:404 size:100 referer:- 311 | host:127.0.0.1 user:frank epoch:1436175795 req:GET /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 312 | host:127.0.0.1 user:john epoch:1436175796 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 313 | host:127.0.0.1 user:- epoch:1436175797 req:GET /profile HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 314 | host:127.0.0.1 user:frank epoch:1436175798 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 315 | host:127.0.0.1 user:frank epoch:1436175799 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 316 | host:127.0.0.1 user:frank epoch:1436175800 req:POST /keyword HTTP/1.0 status:404 size:2326 referer:http://www.hatena.ne.jp/ 317 | host:127.0.0.1 user:john epoch:1436175801 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 318 | host:127.0.0.1 user:- epoch:1436175802 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 319 | host:127.0.0.1 user:frank epoch:1436175803 req:POST /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 320 | host:127.0.0.1 user:frank epoch:1436175804 req:GET / HTTP/1.0 status:404 size:100 referer:- 321 | host:127.0.0.1 user:frank epoch:1436175805 req:GET /profile HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 322 | host:127.0.0.1 user:john epoch:1436175806 req:GET / HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 323 | host:127.0.0.1 user:- epoch:1436175807 req:GET /bookmark HTTP/1.0 status:302 size:9999 referer:http://www.example.com/start.html 324 | host:127.0.0.1 user:frank epoch:1436175808 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 325 | host:127.0.0.1 user:frank epoch:1436175809 req:GET /bookmark HTTP/1.0 status:404 size:100 referer:- 326 | host:127.0.0.1 user:frank epoch:1436175810 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 327 | host:127.0.0.1 user:john epoch:1436175811 req:POST /hoge HTTP/1.0 status:404 size:1234 referer:http://b.hatena.ne.jp/hotentry 328 | host:127.0.0.1 user:- epoch:1436175812 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 329 | host:127.0.0.1 user:frank epoch:1436175813 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 330 | host:127.0.0.1 user:frank epoch:1436175814 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 331 | host:127.0.0.1 user:frank epoch:1436175815 req:GET /fotolife HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 332 | host:127.0.0.1 user:john epoch:1436175816 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 333 | host:127.0.0.1 user:- epoch:1436175817 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 334 | host:127.0.0.1 user:frank epoch:1436175818 req:GET /profile HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 335 | host:127.0.0.1 user:frank epoch:1436175819 req:GET /fotolife HTTP/1.0 status:404 size:100 referer:- 336 | host:127.0.0.1 user:frank epoch:1436175820 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 337 | host:127.0.0.1 user:john epoch:1436175821 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 338 | host:127.0.0.1 user:- epoch:1436175822 req:GET /fotolife HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 339 | host:127.0.0.1 user:frank epoch:1436175823 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 340 | host:127.0.0.1 user:frank epoch:1436175824 req:GET /blog HTTP/1.0 status:404 size:100 referer:- 341 | host:127.0.0.1 user:frank epoch:1436175825 req:GET /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 342 | host:127.0.0.1 user:john epoch:1436175826 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 343 | host:127.0.0.1 user:- epoch:1436175827 req:GET /profile HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 344 | host:127.0.0.1 user:frank epoch:1436175828 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 345 | host:127.0.0.1 user:frank epoch:1436175829 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 346 | host:127.0.0.1 user:frank epoch:1436175830 req:POST /keyword HTTP/1.0 status:404 size:2326 referer:http://www.hatena.ne.jp/ 347 | host:127.0.0.1 user:john epoch:1436175831 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 348 | host:127.0.0.1 user:- epoch:1436175832 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 349 | host:127.0.0.1 user:frank epoch:1436175833 req:POST /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 350 | host:127.0.0.1 user:frank epoch:1436175834 req:GET / HTTP/1.0 status:404 size:100 referer:- 351 | host:127.0.0.1 user:frank epoch:1436175835 req:GET /profile HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 352 | host:127.0.0.1 user:john epoch:1436175836 req:GET / HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 353 | host:127.0.0.1 user:- epoch:1436175837 req:GET /bookmark HTTP/1.0 status:302 size:9999 referer:http://www.example.com/start.html 354 | host:127.0.0.1 user:frank epoch:1436175838 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 355 | host:127.0.0.1 user:frank epoch:1436175839 req:GET /bookmark HTTP/1.0 status:404 size:100 referer:- 356 | host:127.0.0.1 user:frank epoch:1436175840 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 357 | host:127.0.0.1 user:john epoch:1436175841 req:POST /hoge HTTP/1.0 status:404 size:1234 referer:http://b.hatena.ne.jp/hotentry 358 | host:127.0.0.1 user:- epoch:1436175842 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 359 | host:127.0.0.1 user:frank epoch:1436175843 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 360 | host:127.0.0.1 user:frank epoch:1436175844 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 361 | host:127.0.0.1 user:frank epoch:1436175845 req:GET /fotolife HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 362 | host:127.0.0.1 user:john epoch:1436175846 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 363 | host:127.0.0.1 user:- epoch:1436175847 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 364 | host:127.0.0.1 user:frank epoch:1436175848 req:GET /profile HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 365 | host:127.0.0.1 user:frank epoch:1436175849 req:GET /fotolife HTTP/1.0 status:404 size:100 referer:- 366 | host:127.0.0.1 user:frank epoch:1436175850 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 367 | host:127.0.0.1 user:john epoch:1436175851 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 368 | host:127.0.0.1 user:- epoch:1436175852 req:GET /fotolife HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 369 | host:127.0.0.1 user:frank epoch:1436175853 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 370 | host:127.0.0.1 user:frank epoch:1436175854 req:GET /blog HTTP/1.0 status:404 size:100 referer:- 371 | host:127.0.0.1 user:frank epoch:1436175855 req:GET /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 372 | host:127.0.0.1 user:john epoch:1436175856 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 373 | host:127.0.0.1 user:- epoch:1436175857 req:GET /profile HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 374 | host:127.0.0.1 user:frank epoch:1436175858 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 375 | host:127.0.0.1 user:frank epoch:1436175900 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 376 | host:127.0.0.1 user:frank epoch:1436175901 req:POST /keyword HTTP/1.0 status:404 size:2326 referer:http://www.hatena.ne.jp/ 377 | host:127.0.0.1 user:john epoch:1436175902 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 378 | host:127.0.0.1 user:- epoch:1436175903 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 379 | host:127.0.0.1 user:frank epoch:1436175904 req:POST /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 380 | host:127.0.0.1 user:frank epoch:1436175905 req:GET / HTTP/1.0 status:404 size:100 referer:- 381 | host:127.0.0.1 user:frank epoch:1436175906 req:GET /profile HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 382 | host:127.0.0.1 user:john epoch:1436175907 req:GET / HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 383 | host:127.0.0.1 user:- epoch:1436175908 req:GET /bookmark HTTP/1.0 status:302 size:9999 referer:http://www.example.com/start.html 384 | host:127.0.0.1 user:frank epoch:1436175909 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 385 | host:127.0.0.1 user:frank epoch:1436175910 req:GET /bookmark HTTP/1.0 status:404 size:100 referer:- 386 | host:127.0.0.1 user:frank epoch:1436175911 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 387 | host:127.0.0.1 user:john epoch:1436175912 req:POST /hoge HTTP/1.0 status:404 size:1234 referer:http://b.hatena.ne.jp/hotentry 388 | host:127.0.0.1 user:- epoch:1436175913 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 389 | host:127.0.0.1 user:frank epoch:1436175914 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 390 | host:127.0.0.1 user:frank epoch:1436175915 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 391 | host:127.0.0.1 user:frank epoch:1436175916 req:GET /fotolife HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 392 | host:127.0.0.1 user:john epoch:1436175917 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 393 | host:127.0.0.1 user:- epoch:1436175918 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 394 | host:127.0.0.1 user:frank epoch:1436175919 req:GET /profile HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 395 | host:127.0.0.1 user:frank epoch:1436175919 req:GET /fotolife HTTP/1.0 status:404 size:100 referer:- 396 | host:127.0.0.1 user:frank epoch:1436175919 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 397 | host:127.0.0.1 user:john epoch:1436175919 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 398 | host:127.0.0.1 user:- epoch:1436175923 req:GET /fotolife HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 399 | host:127.0.0.1 user:frank epoch:1436175924 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 400 | host:127.0.0.1 user:frank epoch:1436175925 req:GET /blog HTTP/1.0 status:404 size:100 referer:- 401 | host:127.0.0.1 user:frank epoch:1436175926 req:GET /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 402 | host:127.0.0.1 user:john epoch:1436175927 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 403 | host:127.0.0.1 user:- epoch:1436175928 req:GET /profile HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 404 | host:127.0.0.1 user:frank epoch:1436175929 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 405 | host:127.0.0.1 user:frank epoch:1436175930 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 406 | host:127.0.0.1 user:frank epoch:1436175931 req:POST /keyword HTTP/1.0 status:404 size:2326 referer:http://www.hatena.ne.jp/ 407 | host:127.0.0.1 user:john epoch:1436175932 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 408 | host:127.0.0.1 user:- epoch:1436175933 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 409 | host:127.0.0.1 user:frank epoch:1436175934 req:POST /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 410 | host:127.0.0.1 user:frank epoch:1436175935 req:GET / HTTP/1.0 status:404 size:100 referer:- 411 | host:127.0.0.1 user:frank epoch:1436175936 req:GET /profile HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 412 | host:127.0.0.1 user:john epoch:1436175936 req:GET / HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 413 | host:127.0.0.1 user:- epoch:1436175936 req:GET /bookmark HTTP/1.0 status:302 size:9999 referer:http://www.example.com/start.html 414 | host:127.0.0.1 user:frank epoch:1436175936 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 415 | host:127.0.0.1 user:frank epoch:1436175936 req:GET /bookmark HTTP/1.0 status:404 size:100 referer:- 416 | host:127.0.0.1 user:frank epoch:1436175941 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 417 | host:127.0.0.1 user:john epoch:1436175942 req:POST /hoge HTTP/1.0 status:404 size:1234 referer:http://b.hatena.ne.jp/hotentry 418 | host:127.0.0.1 user:- epoch:1436175943 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 419 | host:127.0.0.1 user:frank epoch:1436175944 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 420 | host:127.0.0.1 user:frank epoch:1436175945 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 421 | host:127.0.0.1 user:frank epoch:1436175946 req:GET /fotolife HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 422 | host:127.0.0.1 user:john epoch:1436175947 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 423 | host:127.0.0.1 user:- epoch:1436175948 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 424 | host:127.0.0.1 user:frank epoch:1436175949 req:GET /profile HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 425 | host:127.0.0.1 user:frank epoch:1436175950 req:GET /fotolife HTTP/1.0 status:404 size:100 referer:- 426 | host:127.0.0.1 user:frank epoch:1436175960 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 427 | host:127.0.0.1 user:john epoch:1436175961 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 428 | host:127.0.0.1 user:- epoch:1436175962 req:GET /fotolife HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 429 | host:127.0.0.1 user:frank epoch:1436175963 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 430 | host:127.0.0.1 user:frank epoch:1436175964 req:GET /blog HTTP/1.0 status:404 size:100 referer:- 431 | host:127.0.0.1 user:frank epoch:1436175965 req:GET /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 432 | host:127.0.0.1 user:john epoch:1436175966 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 433 | host:127.0.0.1 user:- epoch:1436175967 req:GET /profile HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 434 | host:127.0.0.1 user:frank epoch:1436175968 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 435 | host:127.0.0.1 user:frank epoch:1436175969 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 436 | host:127.0.0.1 user:frank epoch:1436175970 req:POST /keyword HTTP/1.0 status:404 size:2326 referer:http://www.hatena.ne.jp/ 437 | host:127.0.0.1 user:john epoch:1436175971 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 438 | host:127.0.0.1 user:- epoch:1436175972 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 439 | host:127.0.0.1 user:frank epoch:1436175973 req:POST /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 440 | host:127.0.0.1 user:frank epoch:1436175974 req:GET / HTTP/1.0 status:404 size:100 referer:- 441 | host:127.0.0.1 user:frank epoch:1436175975 req:GET /profile HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 442 | host:127.0.0.1 user:john epoch:1436175976 req:GET / HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 443 | host:127.0.0.1 user:- epoch:1436175977 req:GET /bookmark HTTP/1.0 status:302 size:9999 referer:http://www.example.com/start.html 444 | host:127.0.0.1 user:frank epoch:1436175978 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 445 | host:127.0.0.1 user:frank epoch:1436175979 req:GET /bookmark HTTP/1.0 status:404 size:100 referer:- 446 | host:127.0.0.1 user:frank epoch:1436175980 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 447 | host:127.0.0.1 user:john epoch:1436175981 req:POST /hoge HTTP/1.0 status:404 size:1234 referer:http://b.hatena.ne.jp/hotentry 448 | host:127.0.0.1 user:- epoch:1436175982 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 449 | host:127.0.0.1 user:frank epoch:1436175983 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 450 | host:127.0.0.1 user:frank epoch:1436175984 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 451 | host:127.0.0.1 user:frank epoch:1436175985 req:GET /fotolife HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 452 | host:127.0.0.1 user:john epoch:1436175986 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 453 | host:127.0.0.1 user:- epoch:1436175987 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 454 | host:127.0.0.1 user:frank epoch:1436175988 req:GET /profile HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 455 | host:127.0.0.1 user:frank epoch:1436175989 req:GET /fotolife HTTP/1.0 status:404 size:100 referer:- 456 | host:127.0.0.1 user:frank epoch:1436175990 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 457 | host:127.0.0.1 user:john epoch:1436175991 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 458 | host:127.0.0.1 user:- epoch:1436175992 req:GET /fotolife HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 459 | host:127.0.0.1 user:frank epoch:1436175993 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 460 | host:127.0.0.1 user:frank epoch:1436175994 req:GET /blog HTTP/1.0 status:404 size:100 referer:- 461 | host:127.0.0.1 user:frank epoch:1436175995 req:GET /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 462 | host:127.0.0.1 user:john epoch:1436175996 req:GET /blog HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 463 | host:127.0.0.1 user:- epoch:1436175997 req:GET /profile HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 464 | host:127.0.0.1 user:frank epoch:1436175998 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 465 | host:127.0.0.1 user:frank epoch:1436175999 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 466 | host:127.0.0.1 user:frank epoch:1436176000 req:POST /keyword HTTP/1.0 status:404 size:2326 referer:http://www.hatena.ne.jp/ 467 | host:127.0.0.1 user:john epoch:1436176001 req:POST /hoge HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 468 | host:127.0.0.1 user:- epoch:1436176002 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 469 | host:127.0.0.1 user:frank epoch:1436176003 req:POST /bookmark HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 470 | host:127.0.0.1 user:frank epoch:1436176004 req:GET / HTTP/1.0 status:404 size:100 referer:- 471 | host:127.0.0.1 user:frank epoch:1436176005 req:GET /profile HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 472 | host:127.0.0.1 user:john epoch:1436176006 req:GET / HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 473 | host:127.0.0.1 user:- epoch:1436176007 req:GET /bookmark HTTP/1.0 status:302 size:9999 referer:http://www.example.com/start.html 474 | host:127.0.0.1 user:frank epoch:1436176008 req:GET / HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 475 | host:127.0.0.1 user:frank epoch:1436176009 req:GET /bookmark HTTP/1.0 status:404 size:100 referer:- 476 | host:127.0.0.1 user:frank epoch:1436176010 req:POST /keyword HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 477 | host:127.0.0.1 user:john epoch:1436176011 req:POST /hoge HTTP/1.0 status:404 size:1234 referer:http://b.hatena.ne.jp/hotentry 478 | host:127.0.0.1 user:- epoch:1436176012 req:GET /bookmark HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 479 | host:127.0.0.1 user:frank epoch:1436176013 req:POST /bookmark HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 480 | host:127.0.0.1 user:frank epoch:1436176013 req:GET /profile HTTP/1.0 status:404 size:100 referer:- 481 | -------------------------------------------------------------------------------- /sample_data/log.ltsv: -------------------------------------------------------------------------------- 1 | host:127.0.0.1 user:frank epoch:1372694390 req:GET /apache_pb.gif HTTP/1.0 status:200 size:2326 referer:http://www.hatena.ne.jp/ 2 | host:127.0.0.1 user:john epoch:1372794390 req:GET /apache_pb.gif HTTP/1.0 status:200 size:1234 referer:http://b.hatena.ne.jp/hotentry 3 | host:127.0.0.1 user:- epoch:1372894390 req:GET /apache_pb.gif HTTP/1.0 status:503 size:9999 referer:http://www.example.com/start.html 4 | host:127.0.0.1 user:frank epoch:1372694390 req:GET /apache_pb.gif HTTP/1.0 status:500 size:2326 referer:http://www.hatena.ne.jp/ 5 | host:127.0.0.1 user:frank epoch:1372794395 req:GET /notfound.gif HTTP/1.0 status:404 size:100 referer:- 6 | -------------------------------------------------------------------------------- /scala/.gitignore: -------------------------------------------------------------------------------- 1 | cted from https://github.com/ulrich/macaron-factory/blob/master/.gitignore 2 | # Ignore all dotfiles... 3 | .* 4 | # except for .gitignore 5 | !.gitignore 6 | !.travis.yml 7 | !.coveralls.yml 8 | 9 | # Ignore Play! working directory # 10 | db 11 | eclipse 12 | lib 13 | log 14 | logs 15 | modules 16 | precompiled 17 | project/project 18 | project/target 19 | target 20 | tmp 21 | test-result 22 | server.pid 23 | *.iml 24 | *.eml 25 | target/ 26 | -------------------------------------------------------------------------------- /scala/README.md: -------------------------------------------------------------------------------- 1 | # Scala 課題 2 | 3 | ### 準備編(sbtの簡単な使い方) 4 | 5 | この課題では [sbt](http://www.scala-sbt.org/) を使用します。お手元の環境にinstallしてください。brewが利用できる環境であれば、`brew install sbt` で手軽にinstallできます。 6 | 7 | 以下この課題で必要だと思われるsbtの簡単な使い方を説明します。 8 | 9 | #### sbtの設定 10 | 11 | Scala課題用のsbtの設定がすでにセットアップされています。 12 | 13 | `scala/project/Build.scala` に設定が記述されているので、使いたいライブラリなどがあればここに追記しても構いません。 14 | 15 | #### sbtの起動 16 | 17 | sbtの起動は本READMEが置いてあるディレクトリで`sbt`と打つと実行できます。 18 | 19 | #### コンパイル 20 | 21 | ソースコードをコンパイルするにはsbtを起動し、`compile` と打つと実行されます。 22 | 23 | このとき、テストコードはコンパイルされません。テストコードも含めてコンパイルする場合は、`test:compile`としてください。 24 | 25 | #### テストの実行 26 | 27 | 本課題では、実装の結果はすべてテストにより確認する必要があります。テストの実行は、sbtを起動し、`test`と打ってください。すべてのテストが実行されます。 28 | 29 | ある特定のテストのみ実行する場合は`testOnly`を使います。例えば、課題1のテストを実行する場合は `testOnly hatena.intern.exercise1.Exercise1Spec` となります。 30 | 31 | この課題では、テスティングフレームワークは[ScalaTest](http://www.scalatest.org/)を使っています。 32 | 33 | #### コンソールでの動作確認 34 | 35 | 課題の実装中、自分の書いたメソッドを実際に動かして確認したい場合はsbt consoleを使います。 36 | 37 | sbt起動時に、`console` と打ってください。sbt上でScalaの[REPL](http://www.ne.jp/asahi/hishidama/home/tech/scala/scala.html)が起動し、自分が定義したクラスやメソッドを実行できます。 38 | 39 | 例えば、課題1のLogクラスのメソッドを呼びたい場合は以下のようになります。 40 | 41 | ``` 42 | scala> import hatena.intern._ 43 | import hatena.intern._ 44 | 45 | scala> val log = Log( 46 | | host = "127.0.0.1", 47 | | user = "frank", 48 | | epoch = 1372694390, 49 | | req = "GET /apache_pb.gif HTTP/1.0", 50 | | status = 200, 51 | | size = 2326, 52 | | referer = "http://www.hatena.ne.jp/" 53 | | ) 54 | log: hatena.intern.Log = Log(127.0.0.1,frank,1372694390,GET /apache_pb.gif HTTP/1.0,200,2326,http://www.hatena.ne.jp/) 55 | 56 | scala> log.method 57 | ``` 58 | 59 | consoleを終了してsbtに戻る場合は`ctrl+D`で戻ります。 60 | 61 | ### 課題 Scala-1 62 | 63 | ["はじめに"の項](../README.md)にあった LTSV の1レコードを表す Log クラスを実装して下さい。 64 | 65 | Log クラスは以下のメソッドを持ちます。 66 | * `req` の値に含まれている HTTP メソッド名を返す `method` メソッド 67 | * `req` の値に含まれているリクエストパスを返す `path` メソッド 68 | * `req` の値に含まれているプロトコル名を返す `protocol` メソッド 69 | * `host` と `req` の値からリクエストされた uri を組み立てて返す `uri` メソッド 70 | * `epoch` が表している時刻を `YYYY-MM-DDThh:mm:ss` というフォーマットの文字列に変換して返す `time` メソッド 71 | * 日付を扱うモジュールを用いてかまいません 72 | * タイムゾーンは GMT として下さい 73 | 74 | できあがったクラスは以下のように実行できます。 75 | 76 | 77 | ```scala 78 | val log = Log( 79 | host = "127.0.0.1", 80 | user = "frank", 81 | epoch = 1372694390, 82 | req = "GET /apache_pb.gif HTTP/1.0", 83 | status = 200, 84 | size = 2326, 85 | referer = "http://www.hatena.ne.jp/" 86 | ) 87 | 88 | println(log.method) 89 | println(log.path) 90 | println(log.protocol) 91 | println(log.uri) 92 | println(log.time) 93 | ``` 94 | 95 | * 出力 96 | 97 | ``` 98 | GET 99 | /apache_pb.gif 100 | HTTP/1.0 101 | http://127.0.0.1/apache_pb.gif 102 | 2013-07-01T15:59:50 103 | ``` 104 | 105 | 実装は`src/main/scala/hatena/intern/Exercise1.scala`で行ってください。クラスの雛形があらかじめ用意されていますが、この形式に合わせなくてもかまいません。sbt上で以下のようにテストを実行して、課題が正しく動作していることを確認してください。 106 | 107 | ``` 108 | scala> testOnly hatena.intern.Exercise1Spec 109 | ``` 110 | 111 | 112 | ### 課題 Scala-2 113 | 114 | ["はじめに"の項](../README.md)にあった LTSV フォーマットのログファイルを読み込み、以下のLog オブジェクトの配列を返すパーザーを LtsvParser オブジェクトとして実装してください 115 | 116 | ファイルはリポジトリ内の`sample_data/log.ltsv`を読み込んでください。 117 | 118 | ファイル読み込みには Build.scala でScala IOを使えるようにしていますが、他のライブラリを使っても構いません 119 | 120 | 121 | 実装に関しては、以下の条件を守って下さい。 122 | 123 | * LTSV をパースするライブラリを用いないこと 124 | * 今回のログでは、userとrefererに値がない場合に、"-"がセットされます。この2つの項目をOption[String]に変更し、ログの中身が"-"だった場合はNoneになるように変更してください。また、それにあわせて課題-1のテストも修正してください。 125 | * LTSVの各項目は必ず同じ順番で並んでいるとは限りません。それぞれの項目がどの順番で並んでいてもパースできるように実装してください。 126 | 127 | 実装は`src/main/scala/hatena/intern/Exercise2.scala`で行ってください。クラスの雛形があらかじめ用意されていますが、この形式に合わせなくてもかまいません。 以下のようにsbtでテストを実行して正しく動作していることを確認して下さい。 128 | 129 | ``` 130 | scala> testOnly hatena.intern.Exercise2Spec 131 | ``` 132 | 133 | また、このテストは十分ではないため、`src/test/scala/hatena/intern/Exercise2.scala`を変更してテストを追加してみてください(コード内に指示があります)。 134 | 135 | 136 | ### 課題 Scala-3 137 | 138 | Log を集計する LogCounter クラスを実装して下さい 139 | LogCounter は、HTTP サーバーエラー (500番台) の数を数える `countError` とユーザーごとにログをまとめる `groupByUser` メソッドを持ちます。 140 | 141 | 142 | * user の値がない(値が `-` である)場合は `guest` という名前にして集計して下さい 143 | * いろいろな集計処理が考えられます。余裕があれば自分で考えて実装し、テストも追加してみて下さい。 144 | 145 | 実装は`src/main/scala/hatena/intern/Exercise3.scala`で行ってください。 以下のようにsbtでテストを実行して正しく動作していることを確認して下さい。 146 | 147 | ``` 148 | scala> testOnly hatena.intern.Exercise3Spec 149 | ``` 150 | 151 | ここまでの課題と同様に`src/test/scala/hatena/intern/Exercise3.scala`を変更してテストを追加してみてください。クラスの雛形があらかじめ用意されていますが、この形式に合わせなくてもかまいません。 152 | 153 | 154 | ## 応用編 Scala-4 155 | 156 | 課題 Scala-3 までで作った LTSV パーザーを用いてこれまでに使ったデータより大きなデータから可視化を行い、標準出力にダイアグラム (グラフ) を出力してください。 157 | 158 | サンプルデータは `sample_data/large_log.ltsv` にあるのでこれを利用してください。 159 | 160 | 様々なデータの可視化が考えられます。 161 | 162 | 例: 163 | 164 | * 日毎のリクエスト URI の分布 165 | * リクエスト URI 毎のステータスコード (`status`) の分布 166 | 167 | ダイアグラムの例: 168 | 169 | ``` 170 | / 171 | ---: 100 200 172 | 200:=================* 173 | 403:==* 174 | 404:===* 175 | 500:=* 176 | 177 | /bookmark 178 | ---: 100 200 179 | 200:=================* 180 | 403:==* 181 | 404:=======* 182 | 500:===* 183 | ``` 184 | 185 | 上記の例に限らず、自由に可視化の対象・方法を考えてみてください。 186 | -------------------------------------------------------------------------------- /scala/project/Build.scala: -------------------------------------------------------------------------------- 1 | import com.typesafe.sbt.SbtScalariform 2 | import com.typesafe.sbt.SbtScalariform.ScalariformKeys 3 | import sbt.Keys._ 4 | import sbt._ 5 | 6 | import scalariform.formatter.preferences._ 7 | 8 | object hatenaInternExerciseBuild extends Build { 9 | val appName = "hatena-intern-exercise" 10 | val appVersion = "0.0.1" 11 | val appScalaVersion = "2.11.6" 12 | 13 | val main = Project( 14 | appName, 15 | base = file("."), 16 | settings = Seq( 17 | version := appVersion, 18 | scalaVersion := appScalaVersion, 19 | libraryDependencies ++= Seq( 20 | "joda-time" % "joda-time" % "2.7", 21 | "org.joda" % "joda-convert" % "1.7", 22 | "org.scalatest" %% "scalatest" % "2.2.4" % "test", 23 | "com.github.scala-incubator.io" %% "scala-io-core" % "0.4.3", 24 | "com.github.scala-incubator.io" %% "scala-io-file" % "0.4.3" 25 | ), 26 | resolvers ++= Seq( 27 | "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/" 28 | ), 29 | fork in Test := true, 30 | scalacOptions in Test ++= Seq("-Yrangepos") 31 | ) ++ formatSettings 32 | ).settings(SbtScalariform.scalariformSettings: _*) 33 | 34 | lazy val formatSettings = Seq( 35 | ScalariformKeys.preferences := FormattingPreferences() 36 | .setPreference(IndentWithTabs, true) 37 | .setPreference(DoubleIndentClassDeclaration, true) 38 | .setPreference(PreserveDanglingCloseParenthesis, true) 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /scala/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.8 2 | -------------------------------------------------------------------------------- /scala/project/plugin.sbt: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | 3 | import Defaults._ 4 | 5 | // Comment to get more information during initialization 6 | logLevel := Level.Info 7 | 8 | resolvers ++= Seq( 9 | DefaultMavenRepository, 10 | "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/", 11 | "Sonatype snapshots" at "http://oss.sonatype.org/content/repositories/snapshots/", 12 | Classpaths.typesafeReleases, 13 | Classpaths.typesafeSnapshots, 14 | Classpaths.sbtPluginReleases 15 | ) 16 | 17 | addSbtPlugin("com.typesafe.sbt" % "sbt-scalariform" % "1.3.0") 18 | 19 | addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.6.0") 20 | -------------------------------------------------------------------------------- /scala/src/main/scala/hatena/intern/Exercise1.scala: -------------------------------------------------------------------------------- 1 | package hatena.intern 2 | 3 | case class Log(host: String, user: String, epoch: Int, req: String, status: Int, size: Int, referer: String) { 4 | def method: String = ??? 5 | def path: String = ??? 6 | def protocol: String = ??? 7 | def uri: String = ??? 8 | def time: String = ??? 9 | } 10 | -------------------------------------------------------------------------------- /scala/src/main/scala/hatena/intern/Exercise2.scala: -------------------------------------------------------------------------------- 1 | package hatena.intern 2 | 3 | import scalax.file.Path 4 | 5 | object LtsvParser { 6 | def parse(filePath: String): Iterable[Log] = ??? 7 | } 8 | -------------------------------------------------------------------------------- /scala/src/main/scala/hatena/intern/Exercise3.scala: -------------------------------------------------------------------------------- 1 | package hatena.intern 2 | 3 | case class LogCounter(logs: Iterable[Log]) { 4 | def countError: Int = ??? 5 | 6 | type User = String 7 | def groupByUser: Map[User, Iterable[Log]] = ??? 8 | } 9 | -------------------------------------------------------------------------------- /scala/src/test/scala/hatena/intern/Exercise1.scala: -------------------------------------------------------------------------------- 1 | package hatena.intern 2 | 3 | import hatena.intern.helper._ 4 | 5 | class Exercise1Spec extends UnitSpec { 6 | 7 | describe("Log class") { 8 | it("Logクラスのメソッドが正しく実装されている") { 9 | 10 | val log = Log( 11 | host = "127.0.0.1", 12 | user = "frank", 13 | epoch = 1372694390, 14 | req = "GET /apache_pb.gif HTTP/1.0", 15 | status = 200, 16 | size = 2326, 17 | referer = "http://www.hatena.ne.jp/" 18 | ) 19 | 20 | log.method shouldBe "GET" 21 | log.path shouldBe "/apache_pb.gif" 22 | log.protocol shouldBe "HTTP/1.0" 23 | log.uri shouldBe "http://127.0.0.1/apache_pb.gif" 24 | log.time shouldBe "2013-07-01T15:59:50" 25 | 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /scala/src/test/scala/hatena/intern/Exercise2.scala: -------------------------------------------------------------------------------- 1 | package hatena.intern 2 | 3 | import hatena.intern.helper._ 4 | 5 | class Exercise2Spec extends UnitSpec { 6 | 7 | describe("LTSV Parser") { 8 | it("LTSVファイルが正しくパースされていること") { 9 | val logs = LtsvParser.parse("/path/to/sample_data/log.ltsv") // リポジトリ内の`sample_data/log.ltsv`へのパスを指定してください 10 | logs.size shouldBe 5 11 | 12 | // 以降ファイルが正しくLogクラスにパースされているテストを書いてみてください 13 | } 14 | 15 | it("LTSVファイルが正しくパースできない形式の場合") { 16 | // エラーハンドリングの設計を考えながら、テストを書いてみてください 17 | } 18 | 19 | it("LTSVファイルが存在しない場合") { 20 | // エラーハンドリングの設計を考えながら、テストを書いてみてください 21 | } 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /scala/src/test/scala/hatena/intern/Exercise3.scala: -------------------------------------------------------------------------------- 1 | package hatena.intern 2 | 3 | import hatena.intern.helper._ 4 | 5 | class Exercise3Spec extends UnitSpec { 6 | describe("LTSV Counter") { 7 | 8 | val logs = LtsvParser.parse("/path/to/sample_data/log.ltsv") // リポジトリ内の`sample_data/log.ltsv`へのパスを指定してください 9 | 10 | it("エラー数が正しくカウントされていること") { 11 | LogCounter(logs).countError shouldBe 2 12 | } 13 | 14 | it("ユーザごとにログがグループ化されていること") { 15 | val groupdLogs = LogCounter(logs).groupByUser 16 | val franksLogs = groupdLogs.get("frank") 17 | 18 | groupdLogs.get("john").size shouldBe 1 19 | groupdLogs.get("guest").size shouldBe 1 20 | 21 | franksLogs.size shouldBe 3 22 | // ただしくグルーピングされているかどうかを検査するテストの続きを書いてみてください 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /scala/src/test/scala/hatena/intern/UnitSpec.scala: -------------------------------------------------------------------------------- 1 | package hatena.intern.helper 2 | 3 | import org.scalatest._ 4 | 5 | abstract class UnitSpec extends FunSpec with Matchers with OptionValues with Inside with Inspectors 6 | --------------------------------------------------------------------------------