2013年3月15日金曜日

nginx>fluentd>mongodb

スマートフォンからのアクセスをサービスのAPサーバに影響なく指標値として収集したくて、アクセスログやアプリケーションログを使おうと思って、まずは、nginxのアクセスログを保存する方法を検討して、fluentdmongodb を試してみました。

まず、fluentd に mongodb の plugin をインストールします。
[horiga@bin]: fluent-gem install fluent-plugin-mongo
Successfully installed fluent-plugin-mongo-0.6.13
1 gem installed
Installing ri documentation for fluent-plugin-mongo-0.6.13...
Installing RDoc documentation for fluent-plugin-mongo-0.6.13...
で、fluentd の設定ファイルを以下のようにしました。
<source>
 type tail
 path /usr/local/nginx/logs/access_heartbeat.log
 tag nginx.hb
 format /^(?<ipaddr>[^ ]*) \[(?<ts>[^\]]*)\] "(?<httpm>\S+)(?: +(?<path>[^ ]*) +\S*)?" (?<stat>[^ ]*) "(?<ua>[^\"]*)" (?<chid>[^ ]*)$/
 time_format %d/%b/%Y:%H:%M:%S %z
 pos_file /usr/local/fluent/logs/nginx_access_heartbeat.log.pos
</source>
<match nginx.**>
 type mongo
 database heartbeatdb
 collection heartbeat
 host localhost
 port 27017
 flush_interval 10s
</match>
そして、fluentd を起動しておきます。ext_bson とかいうpluginの警告ログがでますが、まぁBSONは使わないので無視しておきました。
次に、mongodb を起動します。とりあえずオプションなしのシングルモードで起動します。
defaultポートが27017で起動しました。fluent2mongo.conf に設定した port になりますね。
[horiga@bin]: ./mongod
./mongod --help for help and startup options
Fri Mar 15 21:03:56 [initandlisten] MongoDB starting : pid=51694 port=27017 dbpath=/data/db/ 64-bit host=hiroyuki-no-MacBook-Air.local
Fri Mar 15 21:03:56 [initandlisten] 
Fri Mar 15 21:03:56 [initandlisten] ** WARNING: soft rlimits too low. Number of files is 256, should be at least 1000
Fri Mar 15 21:03:56 [initandlisten] db version v2.2.2, pdfile version 4.5
Fri Mar 15 21:03:56 [initandlisten] git version: d1b43b61a5308c4ad0679d34b262c5af9d664267
Fri Mar 15 21:03:56 [initandlisten] build info: Darwin bs-osx-106-x86-64-1.local 10.8.0 Darwin Kernel Version 10.8.0: Tue Jun  7 16:33:36 PDT 2011; root:xnu-1504.15.3~1/RELEASE_I386 i386 BOOST_LIB_VERSION=1_49
Fri Mar 15 21:03:56 [initandlisten] options: {}
Fri Mar 15 21:03:56 [initandlisten] journal dir=/data/db/journal
Fri Mar 15 21:03:56 [initandlisten] recover : no journal files present, no recovery needed
Fri Mar 15 21:03:56 [websvr] admin web console waiting for connections on port 28017
Fri Mar 15 21:03:56 [initandlisten] waiting for connections on port 27017
nginx の設定はとりあえず静的ファイルを変換するように以下のように設定
#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

 log_format fluent '$remote_addr [$time_local] "$request" $status "$http_user_agent" $http_x_test_chid'; 
    
 access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       9001;
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   html;
            index  index.html index.htm;
        }

  location /hb/  {
    root html;
    index success.html;
    access_log logs/access_heartbeat.log fluent;
  }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}
nginx を起動して、"http://localhost:9001/hb/?q=123" へアクセスしてみるとnginxのアクセスログに確かにアクセスログが残っていることを確認
[horiga@bin]: curl -H 'X-TEST-ChId:123456' http://localhost:9001/hb/?q=123
ok
[horiga@logs]: tail -F access_heartbeat.log 
127.0.0.1 [15/Mar/2013:21:15:25 +0900] "GET /hb/?q=123 HTTP/1.1" 200 "curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8r zlib/1.2.5" 123456
で、問題のmongodbにはどうか?
> use heartbeatdb 
> db.heartbeat.find();
{ "_id" : ObjectId("514310dc0840f7cb14000001"), "ipaddr" : "127.0.0.1", "ts" : "15/Mar/2013:21:14:27 +0900", "httpm" : "GET", "path" : "/hb/?q=123", "stat" : "200", "ua" : "curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8r zlib/1.2.5", "chid" : "123456", "time" : ISODate("2013-03-15T12:15:13Z") }
おおっ!!たしかにmongodbにも保存されてました。ここまで特にプログラミングなし、設定のみでできます。
あとは、このデータを使ってデータ分析だな。mongodb からデータを hdfs 上にもってきて分析結果をまた mongodb に格納するとか検討してみます。








2013年3月10日日曜日

Netty with html template engine.


最近 playframework やら、twitter の Finagle などが利用されていることで知られる nettyをいろいろ個人的に使っているのですが、netty は基本的に network framework なので基本的には html を生成するような昨日はありません。こういったことはアプリケーションの実装に任されています。playframework などは基盤のevent-drivenのnetworkフレームワークにnettyを利用しながら、httpのframeworkとしてよく考えられていることで人気があります。
playframework を使っても良かったのですが、playframework も設定などはplayframeworkの作法があります。netty だけを使ってアプリケーションは自由に実装したいと思いまずは、html をレンダリングすることからとおもいます。html のテンプレートエンジンは前回のブログで書いた mustache を使おうとおもいます。

まずは、pom.xml
<project 
  xmlns="http://maven.apache.org/POM/4.0.0" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>

 <groupId>com.blogspot.3agiroh.netty.template.html</groupId>
 <artifactId>mastache-template-test</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>jar</packaging>

 <name>mastache-template-test</name>
 <url>http://maven.apache.org</url>

 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 </properties>

 <build>
  <plugins>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
     <source>1.7</source>
     <target>1.7</target>
     <encoding>UTF-8</encoding>
    </configuration>
   </plugin>
   <plugin>
    <groupId>org.apache.felix</groupId>
    <artifactId>maven-bundle-plugin</artifactId>
    <version>2.3.7</version>
    <extensions>true</extensions>
    <configuration>
     <instructions>
      <!--<Export-Package>*</Export-Package> <Import-Package>*</Import-Package> -->
     </instructions>
    </configuration>
   </plugin>
  </plugins>
 </build>

 <dependencies>
  <!-- mustache template engine. 
  @see https://github.com/spullara/mustache.java -->
  <dependency>
   <groupId>com.github.spullara.mustache.java</groupId>
   <artifactId>compiler</artifactId>
   <version>0.8.10</version>
  </dependency>

  <dependency>
   <groupId>io.netty</groupId>
   <artifactId>netty</artifactId>
   <version>3.6.2.Final</version>
  </dependency>
  <dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-api</artifactId>
   <version>1.7.2</version>
  </dependency>
  <dependency>
   <groupId>ch.qos.logback</groupId>
   <artifactId>logback-classic</artifactId>
   <version>1.0.9</version>
  </dependency>
  <dependency>
   <groupId>commons-configuration</groupId>
   <artifactId>commons-configuration</artifactId>
   <version>1.9</version>
  </dependency>
  <dependency>
   <groupId>commons-lang</groupId>
   <artifactId>commons-lang</artifactId>
   <version>2.6</version>
  </dependency>

  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>3.8.1</version>
   <scope>test</scope>
  </dependency>
 </dependencies>
</project>

起動のクラスはこんな感じで

package com.blogspot.agiroh.netty.template.html;

import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
import org.jboss.netty.handler.codec.http.HttpContentCompressor;
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
import org.jboss.netty.handler.codec.http.HttpResponseEncoder;

public class SimpleHttpServer {

 final int port;
 
 public SimpleHttpServer( int port) {
  this.port = port;
 }
 
 public void run() {
  
  ServerBootstrap bootstrap = new ServerBootstrap(
      new NioServerSocketChannelFactory(
        Executors.newCachedThreadPool(), 
        Executors.newCachedThreadPool()));
  
  bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
   
   @Override
   public ChannelPipeline getPipeline() throws Exception {
    
    ChannelPipeline pipeline = Channels.pipeline();
    pipeline.addLast("decoder", new HttpRequestDecoder());
    pipeline.addLast("aggregator", new HttpChunkAggregator(1048576));
    pipeline.addLast("encoder", new HttpResponseEncoder());
    pipeline.addLast("deflater", new HttpContentCompressor());
    pipeline.addLast("htmlrender", new HtmlRenderHandler());
    
    return pipeline;
   }
  });
  
  bootstrap.bind(new InetSocketAddress(port));
  
 }
 
 public static void main(String[] args) {
  int port = 9000;
  new SimpleHttpServer(port).run();
 }
}
で、HTMLを生成するところは mustache java を利用してこんな感じ
package com.blogspot.agiroh.netty.template.html;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBufferOutputStream;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpMessage;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.codec.http.HttpVersion;
import org.jboss.netty.handler.codec.http.QueryStringDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.mustachejava.DefaultMustacheFactory;
import com.github.mustachejava.Mustache;
import com.github.mustachejava.MustacheFactory;

public class HtmlRenderHandler
  extends SimpleChannelUpstreamHandler {

 static Logger log = LoggerFactory.getLogger(HtmlRenderHandler.class);

 public static final int RES_BUFFER_CAPACITY = 65535;

 public static class ExamplePojo {
  String str;
  long num;
  boolean flag;
  Map<String, Object> data;
  List<String> array;
  String escape;
 }

 @Override
 public void messageReceived(
   ChannelHandlerContext ctx, MessageEvent e) throws Exception {
  
  Object msg = e.getMessage();
  
  if (msg instanceof HttpRequest) {

   HttpRequest request = (HttpRequest) msg;

   if (HttpHeaders.is100ContinueExpected(request)) {
    send100Continue(e);
   }

   ExamplePojo pojo = new ExamplePojo();
   pojo.str = "テスト";
   pojo.num = 30;
   pojo.flag = true;

   QueryStringDecoder qsd = new QueryStringDecoder(request.getUri());
   Map<String, List<String>> params = qsd.getParameters();
   pojo.data = new HashMap<String, Object>();
   if (!params.isEmpty()) {
    for (Map.Entry<String, List<String>> entry : params.entrySet())
     pojo.data.put(entry.getKey(), StringUtils.join(entry.getValue(), ","));
   } else {
    pojo.data.put("key1", "value1");
   }

   pojo.array = Arrays.asList("hoge", "fuga");
   pojo.escape = "<p>hogehogehoge</p>";

   render(e, "html/ja/test.mustache", pojo);
   return;
  }
  
  log.debug("[unknown]");
 }

 protected void render(
   MessageEvent e,  // HTTP request event.
   String resource, // mustache template. 
   Object data)   // mustache template bindings.
     throws IOException {
  
  log.debug("[{}] start rendering.", resource);
  
  HttpMessage req = (HttpMessage) e.getMessage();
  HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);

  MustacheFactory mf = new DefaultMustacheFactory();
  Mustache mustache = mf.compile(resource);

  ChannelBuffer buffer = ChannelBuffers.buffer(RES_BUFFER_CAPACITY);
  ChannelBufferOutputStream out = new ChannelBufferOutputStream(buffer);
  mustache.execute(new PrintWriter(out), data).flush();
  res.setContent(out.buffer());
  try {
   out.close();
  } catch (Exception ee) {}

  res.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/html; charset=utf-8;");

  final boolean keepalive = HttpHeaders.isKeepAlive(req);
  if (keepalive) {
   res.setHeader(HttpHeaders.Names.CONTENT_LENGTH, res.getContent().readableBytes());
   res.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
  }

  // Unsupported cookie.

  ChannelFuture future = e.getChannel().write(res);
  if (!keepalive) {
   future.addListener(ChannelFutureListener.CLOSE);
  }

 }

 private static void send100Continue(MessageEvent e) {
  log.info("send 100 continue.");
  HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE);
  e.getChannel().write(response);
 }
 
 @Override
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
            throws Exception {
  log.error("Error!! : {}", e.getCause().getMessage());
        e.getCause().printStackTrace();
        e.getChannel().close();
    }
}

そしてテンプレートは以下のようにして実行する。

<!DOCTYPE html>
<html>
<head>
<title>Mustache</title>
<meta charset="UTF-8">
</head>
<body>
 {{!This is comment of Mustache!!}}
 <h1>pojo.str</h1>
 <h2>{{str}}</h2>
 
 <h1>pojo.num</h1>
 <h2>{{num}}</h2>
 
 <h1>pojo.flag</h1>
 {{#flag}}flag = true {{/flag}}
 {{^flag}}flag = false {{/flag}}
 
 <h1>pojo.array</h1>
 {{#array}}
 {{.}}</br>
 {{/array}}
 
 <h1>pojo.data</h1>
 {{#data}}
 <p>upper: {{a}} , {{b}}</p></br>
 {{/data}}
 
 <h1>Escaped Characters</h1>
 {{escape}}
</body>
</html>

http://localhost:9000?a=hogehoge&b=fugafuga こんなアクセスをすると結果こうなりました。きちんと表示されますね。


Template Engine - mustache

テンプレートエンジンはみなさんご存知かと思います。有名なのですと以下のようなものがあります。
  • JSP
  • Velocity
  • FreeMarker
「テンプレートエンジンはテンプレートと呼ばれる雛形と、あるデータモデルで表現される入力データを合成し、成果ドキュメントを出力するソフトウェアまたはソフトウェアコンポーネントである」とあります。

最近だと以下の様なのあるようです。

  • Thymeleaf
    • 所見:コーダーさんが用意してくれるhtmlをほぼ変更せずにテンプレートエンジンを適用できるような感じ。
  • Mustache
    • 所見:「ますたっしゅ」って呼ぶそうです。Ruby、Java、Python、Javascript、node.js、... と多くの言語をサポート。使い方がシンプル。
今回は mustache についてJavaを使って簡単に試してみた結果をまとめます。
まずはテンプレートは以下の様なものを用意しました。

<!DOCTYPE html>
<html>
<head>
<title>Mustache</title>
<meta charset="UTF-8">
</head>
<body>
 {{!This is comment of Mustache!!}}
 <h1>pojo.str</h1>
 <h2>{{str}}</h2>
 
 <h1>pojo.num</h1>
 <h2>{{num}}</h2>
 
 <h1>pojo.flag</h1>
 {{#flag}}flag = true {{/flag}}
 {{^flag}}flag = false {{/flag}}
 
 <h1>pojo.array</h1>
 {{#array}}
 {{.}}</br>
 {{/array}}
 
 <h1>pojo.data</h1>
 {{#data}}
 <p>upper: {{a}} , {{b}}</p></br>
 {{/data}}
 
 <h1>Escaped Characters</h1>
 {{escape}}
</body>
</html>
</html>

{{ }} で囲われたところにデータで埋め込まれます。{{#xxx}}〜{{/xxx}}が配列データの繰り返しです。囲われたデータにアクセスする際は、{{.}}とドットでアクセスします。またMapデータのようなkey-valueは、{{key}}でvalue要素にアクセスします。


でJavaは以下のように用意してみました。

package com.blogspot.agiroh.netty.template.html;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import com.github.mustachejava.DefaultMustacheFactory;
import com.github.mustachejava.Mustache;
import com.github.mustachejava.MustacheFactory;

public class ExampleMustache {
 
 final static String template_path = "template/mustache/example.mustache";
 
 public static class ExamplePojo {
  String str;
  long num;
  boolean flag;
  Map<String, Object> data;
  List<String> array;
  String escape;
 }
 
 static Map<String, Mustache> templates = new HashMap<>();
 
 public void run() throws Exception {
  
  MustacheFactory factory = new DefaultMustacheFactory(); // default is classpath root
  
  Mustache mustache = null;
  if ( !templates.containsKey(template_path)) {
   
   mustache = factory.compile(template_path);
   templates.put(template_path, mustache);
  } else {
   System.out.println("templates from cache");
   mustache = templates.get(template_path);
  }
  
  ExamplePojo pojo = new ExamplePojo();
  pojo.str = UUID.randomUUID().toString();
  pojo.num = new Date().getTime()/1000;
  pojo.flag = true;
  pojo.data = new HashMap<String, Object>();
  pojo.data.put("a", "A");
  pojo.data.put("b", "B");
  pojo.array = new ArrayList<>();
  pojo.array.add("hoge");
  pojo.array.add("fuga");
  pojo.escape = "<p>\"te&st\"</p>";
  
  mustache.execute( new PrintWriter(System.out), pojo).flush();
 }
 
 public static void main(String[] args) {
  try {
    new ExampleMustache().run();
  } catch (Exception e) {
   e.printStackTrace();
  }
 } 
}

結果は以下になります。きちんとhtmlのエスケープ処理もされています。なかなか使いやすいとおもいますがいかがでしょうか。

<!DOCTYPE html>
<html>
<head>
<title>Mustache</title>
<meta charset="UTF-8">
</head>
<body>
 
 <h1>pojo.str</h1>
 <h2>5b954508-cf30-4501-b173-75a33446e4ba</h2>
 
 <h1>pojo.num</h1>
 <h2>1362906356</h2>
 
 <h1>pojo.flag</h1>
 flag = true 
 
 
 <h1>pojo.array</h1>
 hoge</br>
 fuga</br>
 
 <h1>pojo.data</h1>
 <p>upper: A , B</p></br>
 
 <h1>Escaped Characters</h1>
 &lt;p&gt;&quot;te&amp;st&quot;&lt;/p&gt;
</body>
</html>
</html>

2013年3月8日金曜日

fluent-logger-java を使ってみた

アプリケーションログを fluentd で収集して、アプリケーションエラーのログとかイベントログの収集してKPIとか集めたくてちょっとまとめます。
とりあえず、ローカル環境は MacOS ですが、fluentd のインストールについては割愛します。rubyやらなんやら入れる必要あります。
※ここら辺を参照

まずは、fluentd 側の設定。本家サイトの通りでおk


[horiga@fluent]: cat fluent4j.conf 
<source></source>
 type forward
 port 24224

<match fluentd.test.**>
 type stdout
</match>


で、起動コマンドはこんな感じで


[horiga@bin]: ./fluentd --config ../fluent4j.conf 


問題なく起動して、コンソールにログが出力されます

次はアプリケーション側。


<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://maven.apache.org/POM/4.0.0"
  xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelversion>4.0.0</modelversion>

 <groupid>com.blogspot.3agiroh.examples.fluentlogger</groupid>
 <artifactid>fluentlogger</artifactid>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>jar</packaging>

 <name>fluentlogger</name>
 <url>http://maven.apache.org</url>

 <properties>
  <project .build.sourceencoding="">UTF-8</project>
 </properties>

 <repositories>
  <repository>
   <id>fluentd.org</id>
   <name>Fluentd Maven2 Repository</name>
   <url>http://fluentd.org/maven2</url>
  </repository>
 </repositories>

 <dependencies>

  <dependency>
      <groupid>com.google.code.gson</groupid>
      <artifactid>gson</artifactid>
      <version>2.2.2</version>
      <scope>compile</scope>
    </dependency>
    
  <dependency>
   <groupid>org.fluentd</groupid>
   <artifactid>fluent-logger</artifactid>
   <version>0.2.6</version>
   
  </dependency>

  <dependency>
   <groupid>junit</groupid>
   <artifactid>junit</artifactid>
   <version>3.8.1</version>
   <scope>test</scope>
  </dependency>
 </dependencies>
</project>


今回テストしたのはJavaでこんなコードで実行しました
本家サイトにありますが、少しバージョンが古かったですね。Map に文字列だけでなく数字、bool型も


package com.blogspot.agiroh.examples.fluentlogger;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.fluentd.logger.FluentLogger;

import com.google.gson.Gson;

/**
 * This is Fluentd4 java test 
 */
public class App {

 static FluentLogger logger = FluentLogger.getLogger("fluentd.test");
 
 public static class Pojo {
  String str;
  int num;
  
  public String toString() {
   return new Gson().toJson(this);
  }
 }
 
 public static void main(String[] args) {
   Map<string object=""> data = new HashMap<string object="">();
   data.put("name", "hoge");
   data.put("age", 35);
   data.put("loggedin", true);
   long ts = new Date().getTime();
   data.put("ts", ts);
   
   Pojo pojo = new Pojo();
   pojo.str = "fuga";
   pojo.num = 100;
   data.put("pojo", pojo);
   
   // default timestamp is [sec]
   logger.log("fluent4j", data);
   
   // timestamp [millis]
   // logger.log("fluent4j", data, new Date().getTime());
 }
}


結果


[horiga@bin]: ./fluentd --config ../fluent4j.conf 
2013-03-08 20:05:11 +0900: starting fluentd-0.10.23
2013-03-08 20:05:11 +0900: reading config file path="../fluent4j.conf"
2013-03-08 20:05:11 +0900: adding source type="forward"
2013-03-08 20:05:11 +0900: adding match pattern="fluentd.test.**" type="stdout"
2013-03-08 20:05:11 +0900: listening fluent socket on 0.0.0.0:24224
2013-03-08 20:13:59 +0900 fluentd.test.fluent4j: {"ts":1362741239520,"loggedin":true,"age":35,"name":"hoge","pojo":"{\"str\":\"fuga\",\"num\":100}"}


おお!!無事に fluentd さんにイベントが転送されました。※本来ならこれを収集サーバに forward すればいいと思う。

でも、やっぱりvalueにpojoはだめか。まぁ複雑にしないでkey-valueの方が使いやすいからこれは問題ないかな。これで accesslog 以外にもいろいろ使えることはおk。実際のロジックと集計などは別に分離したいよね。


2013年3月2日土曜日

RabbitMQ インストール

ども、仕事で使ったRabbitMQについて簡単にまとめとこうとおもいます。
ちなみに RabbitMQ は、AMQP(Advanced Message Queuing Protocol)を使用したメッセージングミドルウェアで vmware さんの提供しているオープンソース。erlang で動いているらしいです。サポートされるプラットフォームは、Window系/Linux/Solaris/MacOSなど多くサポートされているみたい。
また、商用サポートを受けたい場合、VMware社が提供していてのサポートやトレーニング、コンサルティングを受けられるようです。

サイトから自分の環境にあうものをダウンロードする。Windows / Linux / Mac などあり、EC2 のためのものもある。またクライアントとして本家からは Java、.NET/C#、Erlang(RabbitMQがErlangで開発されているみたい)が提供されているみたいだけど、ここ から Ruby や Python、Perl、C、C++… とたくさんあるようです。

とりあえず、Erlang は必須なので インストール します。ここは割愛。

ダウンロードした RabbitMQ を解答する。

[horiga@local]: tar zxvf rabbitmq-server-generic-unix-3.0.1.tar.gz 
x rabbitmq_server-3.0.1/
x rabbitmq_server-3.0.1/LICENSE-APL2-Stomp-Websocket
x rabbitmq_server-3.0.1/plugins/
x rabbitmq_server-3.0.1/plugins/rabbitmq_management-3.0.1.ez
x rabbitmq_server-3.0.1/plugins/webmachine-1.9.1-rmq3.0.1-git52e62bc.ez
x rabbitmq_server-3.0.1/plugins/eldap-3.0.1-gite309de4.ez
x rabbitmq_server-3.0.1/plugins/mochiweb-2.3.1-rmq3.0.1-gitd541e9a.ez
x rabbitmq_server-3.0.1/plugins/rabbitmq_federation_management-3.0.1.ez
x rabbitmq_server-3.0.1/plugins/README
x rabbitmq_server-3.0.1/plugins/rabbitmq_shovel_management-3.0.1.ez
x rabbitmq_server-3.0.1/plugins/rabbitmq_management_visualiser-3.0.1.ez
x rabbitmq_server-3.0.1/plugins/rfc4627_jsonrpc-3.0.1-git7ab174b.ez
x rabbitmq_server-3.0.1/plugins/rabbitmq_jsonrpc_channel-3.0.1.ez
x rabbitmq_server-3.0.1/plugins/sockjs-0.3.3-rmq3.0.1-git92d4ba4.ez
x rabbitmq_server-3.0.1/plugins/rabbitmq_management_agent-3.0.1.ez
 :
 :
 :
[horiga@local]: 

とりあえず
[horiga@local]: ln -s rabbitmq_server-3.0.1 rabbitmq

これだけ。超カンタンだな。

で、RabbitMQ のサーバ起動は
[horiga@rabbitmq]: sudo sbin/rabbitmq-server -detached
[horiga@sbin]: ps aux | grep rabbit
horiga 1160 0.0 0.0 2432768 584 s001 R+ 1:22AM 0:00.00 grep rabbit
root 979 0.0 0.4 2510708 30308 ?? S 1:15AM 0:02.63 /usr/local/lib/erlang/erts-5.9.3.1/bin/beam.smp -W w -K true -A30 -P 1048576 -- -root /usr/local/lib/erlang -progname erl -- -home /Users/horiga -- -pa sbin/../ebin -noshell -noinput -s rabbit boot -sname rabbit@hiroyuki-no-MacBook-Air -boot start_sasl -kernel inet_default_connect_options [{nodelay,true}] -sasl errlog_type error -sasl sasl_error_logger false -rabbit error_logger {file,"sbin/../var/log/rabbitmq/rabbit@hiroyuki-no-MacBook-Air.log"} -rabbit sasl_error_logger {file,"sbin/../var/log/rabbitmq/rabbit@hiroyuki-no-MacBook-Air-sasl.log"} -rabbit enabled_plugins_file "sbin/../etc/rabbitmq/enabled_plugins" -rabbit plugins_dir "sbin/../plugins" -rabbit plugins_expand_dir "sbin/../var/lib/rabbitmq/mnesia/rabbit@hiroyuki-no-MacBook-Air-plugins-expand" -os_mon start_cpu_sup false -os_mon start_disksup false -os_mon start_memsup false -mnesia dir "sbin/../var/lib/rabbitmq/mnesia/rabbit@hiroyuki-no-MacBook-Air" -noshell -noinput
[horiga@sbin]: 

で、停止
[horiga@rabbitmq]: sudo sbin/rabbitmqctl stop
Password:
Stopping and halting node 'rabbit@hiroyuki-no-MacBook-Air' ...
…done.

うん。停止されてた。

※いろいろサーバ側の設定を行うのは、このあたりに書いてる

で localhost:55672にアクセスしてみると、
ログイン画面がでた!!
デフォルトは、guest/guest なのでログイン




はいできあがり。ここまで約30分かからない程度だった。