2016年6月5日日曜日

2016年1月4日月曜日

logstashをWindowsで使う

最近elasticsearchが面白そう。あとログ収集でfluentdを使っているけど、logstashも気になるところ。 ということで、使ってみる。

ここを参考にさせてもらいました(途中まではほぼ一緒)。
http://qiita.com/qtwi/items/98c1184a8a6ce6e2a5e7

やってみることは、fluentdで実装済みなのですが、あるcsvファイルを読み取り、mongodbに保存する。という内容。環境は以下の通り

・ Windows8.1(x64)
・ Java 7u79(JRubyを使う) ※javaはインストール済み
・ logstash 2.1.1
・ mongodb v3.2.0-rc4

0.準備
 
 javaがセットアップされていること。JAVA_HOMEも忘れずにセットアップ。
 mongodbがセットアップされていること。

1.logstash のインストール

  ここからインストーラをダウンロードする。
  https://download.elastic.co/logstash/logstash/logstash-2.1.1.zip
  し、中のフォルダを適当な場所にコピーする。
  ここでは、「C:\logstash-2.1.1」にコピー。

2.pluginのインストール

  今回使うpluginは以下の通り。
  1.ファイルを読み取るに、logstash-input-fileを使う
   2.読み取ったログのデータにfield名をつけるのに、logstash-filter-csvを使う
    3.読み取ったログの数値型データに型を割り当てるのに、logstash-filter-mutateを使う
    4.読み取ったログのデータに日付型を割り当てるのに、logstash-filter-dateを使う
    5.mongodbに入れるのに、logstash-output-mongodbを使う
インストールしてみる。pluginに関するコマンドは、以下の通り。
$ cd C:\logstash-2.1.1\bin

  インストール済みのpluginを表示する。

$ plugin list

  今回のpluginのうち、
  ・logstash-input-file
  ・logstash-filter-csv
  ・logstash-filter-mutate
  ・logstash-filter-date
  はデフォルトでインストールされていることがわかる。
  あとはmongodb。

$ plugin install logstash-output-mongodb

  これでインストールができる。インストール済みのoutputプラグインを見るには、

$ plugin list --group output
  で絞って見れる。plugin名の一部で検索することもできる

$ plugin list mongodb

インストールができていることを確認。

  ※proxy環境下の場合は以下の設定をする
$ export HTTP_PROXY=http://[proxy server]:[proxy port]

3.設定ファイルの作成

 作成するにあたっての想定は以下の通り。

 ・サンプルに使うデータは"c:\test\test.csv"とする
 ・データの中身は以下の通り
time,hostname,object,metric ,value,data1,data2,data3,data4,data5
2015/11/21 00:00:05,server1,object1,metric1,0.13,201511,21,00,00,05
 ・上記のデータのうち、data1まで取り込んで、data2-5は取り込まない
 ・フィールドの"time"はdate型、"value"はfloat型、残りはstring型とする
 ・時間のフィールド名は"time"とする(@timestampではない)

 これで設定をすると、
  
input {
  file{
    path => ['C:\test\test.csv']    # input用ファイル
    start_position => beginning     # ファイルを最初からチェックする
  }
} filter {
  csv {
    columns =>[      # フィールド名
      "time",
      "hostname",
      "object",
      "metric",
      "value",
      "data1"
    ]
    separator => "," # 区切り文字はカンマ
  }
  mutate {
    convert => {
      "value" => "float"    # フィールド"value"の型をfloat型に変換
    }
  }
  date {
    match => [ "time","YYYY/MM/dd HH:mm:ss"]     # フィールド"time"の値をdate型に変換
    timezone => "Asia/Tokyo"                     # タイムゾーンを日本に。
    target => "time"                             # 変換後の値をフィール"time"にセット。デフォルトは"@timestamp"
  }
} output {
    stdout{}               # 確認のために標準出力に出力
    mongodb {    # mongodbに入れる
      codec => plain
      collection => "windows"             # collection名
      database => "logstash"              # database名
      isodate => true                     # 時刻をisodateに
      retry_delay => 10                   # 挿入失敗時のリトライ数
      uri => "mongodb://192.168.x.x"      # mongodb接続文字列
      workers => 1                        # outputで使われるコア数(並列度)
    }
}

としてみた。この結果mongodbに入った情報は、以下の通り。
{
    "_id" : ObjectId("56882fc7564ba8d0b3000002"),
    "message" : "2015/11/21 00:00:28\tserver1\tobject1\tmetric1\t0.13\t2015\t11\t21\t00\t00\t05\r",
    "@version" : "1",
    "@timestamp" : ISODate("2015-11-20T15:00:05.000+0000"),
    "host" : "WIN-O4KS6DHF31D",
    "path" : "C:\\test\\test.csv",
    "time" : ISODate("2015-11-20T15:00:28.000+0000"),
    "hostname" : "server1",
    "object" : "object1",
    "metric" : "metric1",
    "value" : 0.13,
    "data1" : "2015",
    "column7" : "11",
    "column8" : "21",
    "column9" : "00",
    "column10" : "00",
    "column11" : "05"
}
随分と想定外のデータが入ってしまっている。logstash内部やelasticsearchで使うフィールドが
含まれているみたい。mutateのremove_fieldを使用して削除すればいいのかなと。
filterセクションのmutate pluginで以下を追加してみる。
    remove => "message"
    remove => "@version"
    remove => "@timestamp"
    remove => "host"
    remove => "path"
    remove => "column7"
    remove => "column8"
    remove => "column9"
    remove => "column10"
    remove => "column11"
これをすると、以下のエラーがでる
NoMethodError: undefined method `to_iso8601' for nil:NilClass
調べると、もともと@timestampは必須の項目のようで、mongodb pluginのソースをみても、
@timestamp前提で実装されている部分がある。ちなみに、@timestampを削除外にして進めると、エラーがなくなり、登録データもかなり近い状態となった。
{
    "_id" : ObjectId("568846b0564ba8b65b000001"),
    "@timestamp" : ISODate("2016-01-02T21:52:48.431+0000"),
    "time" : ISODate("2015-11-20T15:00:35.000+0000"),
    "hostname" : "server1",
    "object" : "object1",
    "metric" : "metric1",
    "value" : 0.13,
    "data1" : "2015"
}
やはり@timestampが不要(データサイズがかさむ)。ということで、mongodb pluginのソースを
少々いじり、mongodbに登録時のみ@timestampを削除するように1行追加してみた。
C:\logstash-2.1.1\vendor\bundle\jruby\1.9\gems\logstash-output-mongodb-2.0.3\lib\logstash\outputs\mongodb.rbの56行目(insert_oneの直前)に以下を追加
document.delete("@timestamp")
これで登録すると、無事想定通りのデータができあがった。
あとはこのpluginを別のpluginとして登録するようにすれば、とりあえずは解決できそう。
もっとスマートな方法があるかと思うが、とりあえず、終了。

備忘録)
 input pluginのfileだが、Windows環境ではワイルドカードを使うことができません。どうもjruby
 環境にbugがあるようです。rubyだと出ないという記事もありました。


2015年12月25日金曜日

fluentdをWindowsで使う

以前、Windows上でお試し版のfluentdの0.10.46を使っていたが、正式に対応した(masterブランチに組み込まれた)とのことで、改めて導入してみた。

ここを参考にさせてもらいました(ほぼ一緒)。
http://qiita.com/nurse/items/8bbb194982c685f1e795

環境は以下の通り

・ Windows8.1(x64)
・ Ruby 2.2.3(x64) + devkit(Cコンパイル環境)
・ fluentd 0.14.0.pre.1
・ Git for Windows

1.Ruby のインストール

  ここからインストーラをダウンロードし、インストールする。
  ここでは、「c:\Ruby22-x64」にインストールする。
  途中のオプション指定で、PATHに追加する。

2.devkit

  このモジュールも上記と同じサイトから入手する。
  入手したモジュールを実行すると、展開先を聞かれるので、「c:\ruby」としておく。

  展開し終わったら、以下を実施。
$ cd c:\ruby
$ ruby dk.rb init
config.ymlgが生成されるので、そのファイルに1のrubyインストールパスを記載する

  - C:/Ruby22-x64
 
  編集が終わったら、
$ ruby dk.rb install
  を実行する。

3.Git
  
  git レポジトリからfluentdを入手。zip出ダウンロードでもいいかなと思ったのですが、
  fluentdのgemfileの中身で改行がLFとなっていたので、動作しなかった。これだけなら、
  ファイルを改修すればよいかもしれないけど、、他でも問題がでるかもしれないので、、
  素直にgitを入れて、同梱のgit-bashを使うことにする。

  http://www.git-scm.com/download/win

  からダウンロードする。
  サーバ利用などを考慮すると何かとインストールされるのは気持ち悪いので、portableで進める(c:\git)。

4.fluentdのインストール

  以下は、c:\git\bash.exeを実行し、その上で進める。

$ cd /c
$ git clone https://github.com/fluent/fluentd.git
$ cd fluentd
$ gem install bundler
$ bundle
$ bundle exec rake build
$ gem install pkg/fluentd-0.14.0.pre.1.gem

gitをProxy環境下で使用する場合、以下の設定をしておく
$ git config --global http.proxy http://proxy.example.com:8080
$ git config --global https.proxy http://proxy.example.com:8080
同様にgemも使うので、下の設定をしておく
$ export HTTP_PROXY=http://proxy.example.com:8080

これで基本のセットアップ完了。
$ fluentd -c ./example/in_forward.conf

  これでうまく動けばテスト完了。

5.pluginのインストール
 
  試しに、複数のログをtailで読み取り、mongodbに送り込むという処理を実装してみる。

  ・ 複数のファイルを読み取るには、fluent-plugin-tail-exを使う
  ・ 読み取ったログのフォーマットに型を割り当てるのに、fluent-plugin-typecastを使う
  ・ mongodbに入れるのに、 fluent-plugin-mongoを使う
 ということで、以下を実行する。

$ ./bin/fluent-gem install fluent-plugin-tail-ex
$ ./bin/fluent-gem install fluent-plugin-typecast
$ ./bin/fluent-gem install fluent-plugin-mongo
 とここで問題が。
 installすると、fluentdの0.12.19が一緒にインストールされてしまい、fluentd自体が
 そちらを参照しに行ってしまう。
$ fluentd --version
fluentd 0.12.19
 結果として、
$ fluentd -c ./example/in_tail.conf
2015-12-24 00:45:48 +0900 [info]: reading config file path="./example/in_tail.conf"
C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/fluentd-0.12.19/lib/fluent/supervisor.rb:387:in `trap': unsupported signal SIGHUP (ArgumentError)
from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/fluentd-0.12.19/lib/fluent/supervisor.rb:387:in `install_supervisor_signal_handlers'
        from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/fluentd-0.12.19/lib/fluent/supervisor.rb:139:in `start'
        from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/fluentd-0.12.19/lib/fluent/command/fluentd.rb:171:in `&lttop (required)&gt'
        from C:/Ruby22-x64/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:69:in `require'
        from C:/Ruby22-x64/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:69:in `require'
        from C:/Ruby22-x64/lib/ruby/gems/2.2.0/gems/fluentd-0.12.19/bin/fluentd:6:in `&lttop (required)`&gt'
        from C:/Ruby22-x64/bin/fluentd:23:in `load'
        from C:/Ruby22-x64/bin/fluentd:23:in `&ltmain&gt'
 こんなエラーがでるようになる。0.12.19はWindowsに対応していないが原因。
 とりあえず、0.12.19をアンインストールしてみる
 
$ ./bin/fluent-gem uninstall fluentd
Select gem to uninstall:

   1. fluentd-0.12.19
   2. fluentd-0.14.0.pre.1
   3. All versions
  > 1

  Successfully uninstalled fluentd-0.12.19
これで動くかどうか・・ですね。
で設定は以下の通り(ログは適当)。
test.conf
<source>

  type tail_ex
  path c:\fluentd\data\*.tsv
  pos_file c:\fluentd\data\fluentd-tsv.pos
  tag tail_ex.a
  format tsv
  keys time,name,counterobject,counter,value
  time_key time
  refresh_interval 30
</source>

<match tail_ex.*>
  type typecast
  item_types time:string,name:string,counterobject:string,counter:string,value:float
  tag mongo.data
</match>

<match mongo.data>
  # plugin type
  type mongo
  # mongodb db + collection
  database test
  tag_mapped
  emove_tag_prefix mongo.

  # mongodb host + port
  host 192.168.33.207
  port 27017

  # interval
  flush_interval 10s

  #include_time_key true
</match>  
あと今はgit-bash.exe上でテストをしているが、バッチとして実行したいので、コマンドプロンプト
 から実行するには、、
$ c:\git\bin\bash -c "/C/fluentd/bin/fluentd -c /C/fluentd/test.conf"
これで実施できる。  で、試してみたが、、エラーだ・・tail_exが引っかかっているみたい。
 tailであれば、動く。
 tail _exは引き続き調査してみることにしよう・・

2015年10月22日木曜日

node.jsでのログ出力:log4js

node.jsでのlog4jsを使ったログ出力について備忘録。

log4js

javaで使われいてるlog4のjavascript版のlog4jsをNode.jsにコンバージョンしたもの。
https://github.com/nomiddlename/log4js-node
特徴的な機能としては次のようなものがある。
  • Node.jsのConsoleログを置き換えられる
  • file appenderでは、ファイルサイズでローテーションができる
  • メール送信ができる(smtp appender)
  • logstashに送信できる(Logstash UDP appender)
  • Workerプロセス構成のときに使えるmultiprocess appender
  • それぞれのカテゴリにそれぞれのログレベルで出力を設定できる
アプリケーションのログはもちろん、Node.jsのエラーログやアクセスログの取得もできそうなので、morganよりいいんじゃないかと思って試してみる。

■インストール

まずはモジュールのインストール。
$ npm install log4js --save

次に出力設定ファイルを、外部ファイルとして作成する。
log4js_config.json
{
    "appenders": [
        {
            "type":     "File",
            "category": "system",
            "maxLogSize": 1048576,
            "backups": 3,
            "compress":true,
            "filename": "logs/system.log"
        },
        {
            "type":     "dateFile",
            "category": "access",
            "filename": "logs/access.log",
            "pattern":  "-yyyy-MM-dd"
        },
        {
            "type":     "dateFile",
            "category": "error",
            "filename": "logs/error.log",
            "pattern":  "-yyyy-MM-dd"
        },
        {
            "type":     "console",
            "category": "test"
        }
    ],
    "levels": {"system": "INFO"},
    "replaceConsole": true
}
上記を例に設定内容の記載。


appenderstypedateFileローテーション対応したファイルへの出力
fileただのファイルへの出力
consoleすべてをconsoleへの出力
category出力するログの設定の単位。カテゴリごとに設定をすることができる。
filename出力するファイルの名前
patternOutputストリーム。デフォルトは、process.stdoutになっている
maxLogSizetypeがfileの場合の最大ファイルサイズ(bytes)
backupsbackup世代数
compresstypeがfileの場合のオプションで、gzipをするかどうか?フラグ
levels出力するログレベルを設定。例では、categoryがsystemのものをINFOレベル以上で出力するという設定
replaceConsoletypeにconsoleが指定されていた場合、consoleへの出力フォーマットが通常のログ形式と同じになる設定

上記のlevelsで使用できる値は以下の通り。
ALLすべて出力
TRACETRACE 以上を出力
DEBUGDEBUG 以上を出力
INFOINFO 以上を出力
WARNWARN 以上を出力
ERRORERROR 以上を出力
FATALFATAL 以上を出力
MARKMARK 以上を出力
OFF出力しない

以下は実装例。
// モジュールの取り込み
var log4js = require('log4js');

// 設定ファイルの取り込み(ファイルは環境変数:LOG4JS_CONFIGに指定してもよい)
// reloadSecsはオプション(byou )。通常は60秒間隔で設定ファイルがReloadされる。
log4js.configure('path/log4js_config.json', { reloadSecs: 300 });

// 設定がいるを絶対パスで記載することもできる。
log4js.configure('log4js_config.json', { cwd: '/absolute/path/to/log/dir' });

// 出力するカテゴリの指定
var logger = log4js.getLogger('system');

// 出力レベルの設定(設定ファイルのlevelsに相当)
logger.setLevel('ERROR');

// 出力
logger.trace('Entering cheese testing');
logger.debug('Got cheese.');
logger.info('Cheese is Gouda.');
logger.warn('Cheese is quite smelly.');
logger.error('Cheese is too ripe!');
logger.fatal('Cheese was breeding ground for listeria.');

2015年10月10日土曜日

graphiteの環境の構築

CentOS 6.5
■Graphiteの構成
 Graphite-Web
 Carbon
 Whisper

■graphiteのインストール準備

 graphiteをpip(pythonのパッケージ管理ツール)でインストールする。(本家サイトを参照)

#graphiteの構成要素のcarbonをインストールするために、pythonの開発ヘッダーを入れる。

$ sudo yum install python-devel

#pipのセットアップを行うため、setuptoolsをインストールする

$ wget https://bootstrap.pypa.io/ez_setup.py -O - | sudo python

#続いてpipのインストール

$ curl -kL https://raw.github.com/pypa/pip/master/contrib/get-pip.py | sudo python
準備完了!

■graphiteのインストール

 では、さっそくgraphiteのインストールを始める。
$ sudo pip install https://github.com/graphite-project/ceres/tarball/master
$ sudo pip install whisper
$ sudo pip install carbon
$ sudo pip install graphite-web
特にエラーなし。
デフォルトでは、/opt/graphite/にインストールされる。とマニュアルにあるが、
 /usr/lib/python2.6/site-packages/opt/graphite/
に出来上がっている。/opt/graphiteにまとめておきたいので、インストールし直し。
$ sudo pip uninstall carbon
$ sudo pip uninstall graphite-web
$ sudo pip install carbon --install-option="--prefix=/opt/graphite" --install-option="--install-lib=/op¥t/graphite/lib"
$ sudo pip install graphite-web --install-option="--prefix=/opt/graphite" --install-option="--install-lib=/opt/graphite/lib"
$ ls /opt/graphite
bin conf examples lib storage webapp

■graphiteのセットアップ(本家サイトを参照)

graphite-webでは、local_settings.pyを読み込んで設定されるみたいなので、設定を変更する。
サイトのページには
 /opt/graphite/webapp/graphite/local_settings.py
があると記載があったが、見あたらなかった。exampleファイルがあったので、コピーして使う。

$ mkdir /opt/graphite/webapp/graphite
$ cd /opt/graphite/webapp/graphite
$ cp /opt/graphite/lib/graphite/local_settings.py.example ./local_settings.py
$ sudo vi local_settings.py
#以下、変更箇所
TIME_ZONE = 'Asia/Tokyo'
LOG_RENDERING_PERFORMANCE = True
LOG_CACHE_PERFORMANCE = True
LOG_METRIC_ACCESS = True

■carbonのセットアップ(本家サイトを参照)

・carbon.conf
 carbonデーモン用の設定ファイル。ファイル内のセクションは
 [cache]・・・carbon-cacheに関する設定ファイル
 [relay]・・・carbon-relayに関する設定
 [aggregator]・・・carbon-aggregatorに関する設定

今回は設定変更なし進める。

$ cp carbon.conf.example carbon.conf

・storage-schemas.conf

 メトリクスの保持期間の設定をするためのファイル。ここの設定でwhisperに伝達する。
この設定のポイントは

  ・ファイルの上から順に適用される
  ・正規表現が使える
  ・メトリクス名にマッチした最初のパターンが使われる
  ・最初にメトリクスが送られた来たときに、保持期限が設定される
  ・この設定が変更されても、生成済みwspファイルには反映されない。whisper-resize.pyを使って変更する必要がある。
とのこと。

記載例)

[apache_busyWorkers]
pattern = ^servers\.www.*\.workers\.busyWorkers$
retentions = 15s:7d,1m:21d,15m:5y

1行目はセクション名。2行目は
3行目は、7日間は15秒間平均(デフォルトは平均)、21日間は1分平均、5年間は15分平均
という意味になる。
今回はデフォルトで。

$ cp storage-schemas.conf.example storage-schemas.conf
$ less storage-schemas.conf

 Schema definitions for Whisper files. Entries are scanned in order,
# and first match wins. This file is scanned for changes every 60 seconds.
#
#  [name]
#  pattern = regex
#  retentions = timePerPoint:timeToStore, timePerPoint:timeToStore, ...

# Carbon's internal metrics. This entry should match what is specified in
# CARBON_METRIC_PREFIX and CARBON_METRIC_INTERVAL settings
[carbon]
pattern = ^carbon\.
retentions = 60:90d

[default_1min_for_1day]
pattern = .*
retentions = 60s:1d
 
すべてのデータを60秒間平均で1日保持。



■carbonの実行

$ /opt/graphite/bin/carbon-cache.py start


ImportError: Twisted requires zope.interface 3.6.0 or later: no module named zope.interface.

エラーが出た。調べてみると、とりあえずzope.interface自体をインストールする方法があるらしい。

$ sudo pip install zope.interface==3.6.0
$ sudo /opt/graphite/bin/carbon-cache.py start



Starting carbon-cache (instance a)

#ログも確認してみる
$ cd /opt/graphite/storage/log/carbon-cache/
$ ls
carbon-cache-a
$ cd  carbon-cache-a
$ less console.log

10/10/2015 23:39:36 :: Log opened.
10/10/2015 23:39:36 :: twistd 15.4.0 (/usr/bin/python 2.6.6) starting up.
10/10/2015 23:39:36 :: reactor class: twisted.internet.epollreactor.EPollReactor.
10/10/2015 23:39:36 :: ServerFactory starting on 2003
10/10/2015 23:39:36 :: Starting factory <twisted.internet.protocol.ServerFactory instance at 0x308e290>
10/10/2015 23:39:36 :: ServerFactory starting on 2004
10/10/2015 23:39:36 :: Starting factory <twisted.internet.protocol.ServerFactory instance at 0x3097b90>
10/10/2015 23:39:36 :: ServerFactory starting on 7002
10/10/2015 23:39:36 :: Starting factory <twisted.internet.protocol.ServerFactory instance at 0x3097bd8>
10/10/2015 23:40:36 :: /opt/graphite/conf/storage-aggregation.conf not found, ignoring.
10/10/2015 23:41:36 :: /opt/graphite/conf/storage-aggregation.conf not found, ignoring.
10/10/2015 23:42:36 :: /opt/graphite/conf/storage-aggregation.conf not found, ignoring.
10/10/2015 23:43:36 :: /opt/graphite/conf/storage-aggregation.conf not found, ignoring.

ちゃん起動しているようです。



2015年9月23日水曜日

node.jsでのログ出力:morgan

node.jsでのログ出力について備忘録。

morgan

 express 4でアクセスログ出力に使われているモジュール。
 参考にささせていただいたのは以下のサイト。
 http://qiita.com/hoshi-takanori/items/7f5602d7fd7ee0fa6427
 https://github.com/expressjs/morgan

■インストール
1.package.jsonに追加

  例:
{
  "name": "sample_prject",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "body-parser": "~1.12.4",
    "cookie-parser": "~1.3.5",
    "debug": "~2.2.0",
    "ejs": "~2.3.1",
    "express": "~4.12.4",
    "morgan": "~1.5.3",
    "serve-favicon": "~2.2.1",
    "validator": "^3.40.1"
  }
}

2.インストール
 $ npm install
 
(expressでテンプレートを作るとついてくる)
書式:morgan(format[, options])


"format"は、ログのフォーマットを指定する引数。あらかじめ定義されたフォーマットがある。


combined
Apache combinedログフォーマット。デフォルト。tokenを使って書くと以下の通り
:remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"
出力される内容は以下の通り。
::ffff:192.168.33.1 - - [23/Sep/2015:17:04:07 +0000] "GET / HTTP/1.1" 304 - "-" "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36"

formatcombinedApache combinedログフォーマット。デフォルト。tokenを使って書くと以下の通り
:remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"
commonApache commonログフォーマット。tokenを使って書くと以下の通り
:remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length]
devhttp status code毎に色付けして表示されるログ。サーバエラーの場合は赤、クライアントエラーの場合は黄色、Ridirectはシアンで表示される。tokenを使って書くと以下の通り
:remote-addr :remote-user :method :url HTTP/:http-version :status :res[content-length] - :response-time ms
shortデフォルト(combined)より短いフォーマット。tokenを使って書くと以下の通り
:remote-addr :remote-user :method :url HTTP/:http-version :status :res[content-length] - :response-time ms
tiny一番短いログフォーマット。tokenを使って書くと以下の通り
:method :url :status :res[content-length] - :response-time ms
optionsimmediateリクエストを受けた時点でログ出力をする。サーバがCrashしたとき絵も出力されるが、レスポンス時間やコンテキスト長などレスポンスに関する情報は記録されない
skip特定の条件でログを出力しない場合に使う。以下の例では、HTTPのステータスコードが400未満の場合はログ出力をしない。デフォルトは、falseになっている(つまりすべて出力)。
例)
morgan('combined', {
  skip: function (req, res) { return res.statusCode < 400 }
})
streamOutputストリーム。デフォルトは、process.stdoutになっている

使用できるtokenは以下の通り。
:date[format]現時刻。指定できるフォーマットは以下の通り。
clf  common log format。例:10/Oct/2000:13:55:36 +0000
iso  ISO 8601 date time format。例:2000-10-10T13:55:36.000Z
web RFC1123 date time format。例:Tue, 10 Oct 2000 13:55:36 GMT

:http-versionリクエストされたHTTPのバージョン
:methodリクエストのHTTPメソッド
:referrerリクエストのReferrerヘッダー。ミススペルでRefererがあった場合はそれを使う。
:remote-addrリクエストのリモートアドレス。req.ipが使われる。そうでなければreq.connection.remoteAddressが使われる
:remote-user基本認証された場合のユーザID。
:req[header]リクエストの[header]で指定されたヘッダー
:res[header]レスポンスの[header]で指定されたヘッダー
:response-timeリクエストがmorganに来てから、レスポンスヘッダーがかかれるまでの所要時間(ms)
:statusレスポンスのstatus code。レスポンスがクライアントに送信される前に、クライアント側からTCP/IPセッションがクローズされるなどが発生した場合は、ブランクとなる。
:urlリクエストされたURL。req.originalUrlがあればそれを、なければreq.urlが使われる
:user-agentリクエストのUser-Agentヘッダーの値。

また作成することもできる。以下は、content-typeを示す"type"というtokenを作成する例。

morgan.token('type', function(req, res){ return req.headers['content-type']; })

 ・使用例
morganの実施の使用例を以下に。

ログをファイルに出力する例
pathモジュールを入れておく。
$ npm install path --save
var express = require('express')
var fs = require('fs')
var morgan = require('morgan')

var app = express()

// create a write stream (in append mode)
var accessLogStream = fs.createWriteStream(__dirname + '/access.log', {flags: 'a'})

// setup the logger
app.use(morgan('combined', {stream: accessLogStream}))

app.get('/', function (req, res) {
  res.send('hello, world!')
})
ちなみに、createWriteStreamの仕様については以下を参照。
https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options


ログファイルをローテーションする例。
file-stream-rotatorというモジュールを使う。
$ npm install file-stream-rotator --save
var FileStreamRotator = require('file-stream-rotator')
var express = require('express')
var fs = require('fs')
var morgan = require('morgan')

var app = express()
var logDirectory = __dirname + '/log'

// ディレクトリがなければ作成する
fs.existsSync(logDirectory) || fs.mkdirSync(logDirectory)

// write streamをローテーションする設定
var accessLogStream = FileStreamRotator.getStream({
  filename: logDirectory + '/access-%DATE%.log',
  frequency: 'daily',
  verbose: false,
  date_format: "YYYY-MM-DD"
})

// 今回はcombinedで出力
app.use(morgan('combined', {stream: accessLogStream}))

app.get('/', function (req, res) {
  res.send('hello, world!')
})


独自tokenを使う例
Webアプリなどでも使われる(らしい)UUIDを生成して、それをログに記録する例。
var express = require('express')
var morgan = require('morgan')
var uuid = require('node-uuid')

morgan.token('id', function getId(req) {
  return req.id
})

var app = express()

app.use(assignId)
app.use(morgan(':id :method :url :response-time'))

app.get('/', function (req, res) {
  res.send('hello, world!')
})

function assignId(req, res, next) {
  req.id = uuid.v4()
  next()
}
ちなみに、node-uuidの仕様については以下を参照。
https://github.com/broofa/node-uuid
◆winston

2015年6月26日金曜日

Let's Chatを入れてみた

slackを使っていのだけど、やはりプロジェクトによっては社外のサービスを使うのはなぁ?という懸念が上がるときがある。抜け道を考えるのが面倒なので、社内にChatサーバーを立ててることにした(BYOSというの?)。折角なのでSlack風Chatの「Let's Chat」を使ってみようと思ったので、備忘録。


0.環境準備

使った環境は以下の通り。

  ・vagrant
  ・CentOS 6.5
  ・Let's Chat 0.3.12

1.インストール

Let's Chatの導入要件となっている、以下のツール群を導入する。
  ・nodejs v0.12.4
  ・mongodb 3.0
  ・Python 2.7

1.1 nodejsを入れる
 ここを参照しつつ入れる!

1.2 mongodbを入れる

$ wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel62-3.0.3.tgz
$ tar xvfz mongodb-linux-x86_64-rhel62-3.0.3.tgz
$ cd mongodb-linux-x86_64-rhel62-3.0.3 mongo

1.3 python2.7を入れる

$ wget https://www.python.org/ftp/python/2.7.6/Python-2.7.6.tgz
$ tar xvfz Python-2.7.6.tgz
$ cd Python-2.7.6
$ ./configure --enable-shared --with-threads
$ make
$ sudo make install

$ python -V

python: error while loading shared libraries: libpython2.7.so.1.0: cannot open shared object file: No such file or directory

なんだかエラーがでてる。

$ ldd /usr/local/bin/python
        linux-vdso.so.1 =>  (0x00007fffc0ad3000)
        libpython2.7.so.1.0 => not found
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f112bed4000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007f112bcd0000)
        libutil.so.1 => /lib64/libutil.so.1 (0x00007f112bacd000)
        libm.so.6 => /lib64/libm.so.6 (0x00007f112b848000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f112b4b4000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f112c0fb000)

同じですが、libpython2.7.so.1.0がないと言われている。ただ実際には、、

$ sudo find / -name 'libpython2.7.so.1.0'
/home/vagrant/Python-2.7.6/libpython2.7.so.1.0
/usr/local/lib/libpython2.7.so.1.0

といストールされている。のでリンクで対応する。

$ sudo ln /usr/local/lib/libpython2.7.so.1.0 /lib64/libpython2.7.so.1.0
$ python -V

Python 2.7.6

無事。インストールができたみたいです。

1.4 Let's Chatを入れる

最後にLet's Chatを入れる。

$ git clone https://github.com/sdelements/lets-chat.git
$ cd lets-chat/
$ npm install

gyp: Call to 'which icu-config > /dev/null || echo n' returned exit status 0. while trying to load binding.gyp


何やらエラーがでて失敗した。ライブラリを追加で入れる。

$ sudo yum install libicu-devel.x86_64

で、再チャレンジです

$ npm install

node-xmpp-core@1.0.0-alpha14 node_modules/node-xmpp-core
├- tls-connect@0.2.2
├- debug@2.2.0 (ms@0.7.1)
├- reconnect-core@0.0.1 (backoff@2.3.0)
├- node-stringprep@0.7.0 (bindings@1.2.1, debug@2.0.0, nan@1.8.4)
└- mqq ltx@0.9.0 (sax@0.6.1, node-expat@2.3.8)

をーインストール完了。

2. Let's Chatの設定

$ cp settings.yml.sample settings.yml
$ vi settings.yml
================
# Let's Chat Settings
#
# See defaults.yml for all available options
#

env: production # development / production

http:
  enable: true
  host: '0.0.0.0'
  port: 80

https:
  enable: false
  port: 5001
  key: key.pem
  cert: certificate.pem

files:
  enable: true
  provider: local
  local:
    dir: uploads

xmpp:
  enable: false
  port: 5222
  domain: example.com

database:
  uri: mongodb://localhost/letschat

secrets:
  cookie: secretsauce

auth:
  providers: [local]
  local:
    enableRegistration: true
================


$ npm start

 [Error: Cannot find module '../build/Release/bson'] code: 'MODULE_NOT_FOUND' }
js-bson: Failed to load c++ bson extension, using pure JS version

あがるっちゃ、あがるんだけど、エラーがでている。いろいろググってみたけど、

npm cache clean; rm -rf node_modules ;npm install

これで再インストールすればいけるみたい。
ということでやってみたら・・・行けた!

3.検索をカスタマイズ
chatの履歴から全文検索ができるようになっているのですが、、mongodbの全文検索機能を使っており、mongodbのそれは日本語には対応していない。なので、全くといっていいほど使えない。それだったらせめてmessagesの検索だけでも意味があるかなと思い、少しソースを修正。

lets-chat/app/core/messages.js

    if (options.query) {
        find = find.find({$text: {$search: options.query}});
    }


    if (options.query) {
        var r = new RegExp(options.query, "i")
        find = find.find({"text": {$regex:r}});
    }

に変更してみた。これだけでも結構便利!

4.ldap認証

デフォルトでは、local認証となっていて、さらに自由にアカウント登録ができてしまう。今回インターネット経由でアクセスをさせるので、この状態はよろしくない。
とうことで、openldapを立てて、アカウント管理!
※openldapの導入はこちら

settings.yml
===
auth:
  providers: [ldap]
  ldap:
    connect_settings:
      url: ldap://192.168.x.x:389/
    bind_options:
      bindDN: uid=bind,ou=Account,dc=example,dc=com
      bindCredentials: password
    search:
      base: "ou=Account,dc=example,dc=com"
      opts:
        scope: one
        filter: (uid={{username}})
        #filter: (sAMAccountName={{username}})
    field_mappings:
      uid: uid
      firstName: givenName
      lastName: sn
      displayName: givenName

      email: mail
===
いや~バカになるくらいで簡単にできた・・。すごい!