2011年6月28日火曜日

SimpleDBにSyslogっぽいログを出力(保存)

スズキです。

"Auto Scaling"で起動しているEC2インスタンスは、勝手にターミネートしてしまうので、
ログはEC2インスタンスではなく別の場所に出力する必要があります。

ということでSimpleDBに出力してみました。せっかくなのでログのフォーマットは
Syslogっぽくして、Syslogラッパーにもつなげれれば、と思っています。

ちなみに、Syslogのフォーマットは、
timestamp, facility, priority, tag, message
といった感じで、さらにインスタンスIDも出力するようにしています。

ということで、かなり肥大化してしまった、いつもの共通部分です。
下記で紹介したやり方を応用してインスタンスIDを取得し、
EC2起動時に設定できる"User Data"をPHPで利用する
ログ出力用の関数(logger)を定義し、そこでSmpleDBに出力するようにしています。

▼ common.php
define("AWS_KEY"              , "AAAAAAAA");
define("AWS_SECRET_KEY"       , "SSSSSSSS");
define("CP_SQS_URL_CRAWL"     , "https://sqs.ap-northeast-1.amazonaws.com/00000000/crawl");
define("CP_AS_NAME"           , "crawl");
define("CP_SDB_DOMAIN_MESSAGE", "message");
define("CP_SDB_DOMAIN_LOG"    , "log");
date_default_timezone_set("Asia/Tokyo");

// インスタンスIDを取得
$curl = curl_init("http://169.254.169.254/1.0/meta-data/instance-id");
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
define("CP_EC2_INSTANCE", curl_exec($curl));
curl_close($curl);

// ログ出力関数
function logger($sdb, $facility, $priority, $tag, $message) {
    $time = time();
    $response = $sdb->put_attributes(CP_SDB_DOMAIN_LOG, CP_EC2_INSTANCE . "-" . $time . "-" . rand(), array(
        "instance"  => CP_EC2_INSTANCE,
        "timestamp" => $time,
        "facility"  => $facility,
        "priority"  => $priority,
        "tag"       => $tag,
        "message"   => $message
    ));
}

実際のログの出力と表示のコードはこんな感じです。

▼ put-and-get-log
require_once("/opt/cloudpack/bin/common.php");
require_once("/opt/aws/php/sdk.class.php");
$sdb = new AmazonSDB();
$sdb->set_region(AmazonSDB::REGION_APAC_NE1);

// ログの出力
logger($sdb, "facility", "priority", "tag", "message");

// ログを取得するクエリー
$query = <<<QUERY
SELECT *
FROM log
WHERE timestamp IS NOT NULL
ORDER BY timestamp DESC
LIMIT 10
QUERY;

// ログの表示
$response = $sdb->select($query);
$items = $response->body->SelectResult->Item;
foreach($items as $item) {
    foreach($item->Attribute as $attribute) {
        switch($attribute->Name) {
            case "instance":
                $instance  = $attribute->Value;
                break;
            case "timestamp":
                $timestamp = date("c", intval($attribute->Value));
                break;
            case "facility":
                $facility  = $attribute->Value;
                break;
            case "priority":
                $priority  = $attribute->Value;
                break;
            case "tag":
                $tag       = $attribute->Value;
                break;
            case "message":
                $message   = $attribute->Value;
                break;
        }
    }
    print("$instance $timestamp $facility $priority $tag $message\n");
}

実行すると、こんな感じです。

# ./put-and-get-log 
i-f4ed9ff5 2011-06-28T21:53:51+09:00 facility priority tag message
i-f4ed9ff5 2011-06-28T21:26:13+09:00 facility priority tag message
i-f4ed9ff5 2011-06-28T21:26:11+09:00 facility priority tag message
i-f4ed9ff5 2011-06-28T21:26:09+09:00 facility priority tag message
i-f4ed9ff5 2011-06-28T21:26:07+09:00 facility priority tag message
i-f4ed9ff5 2011-06-28T21:26:04+09:00 facility priority tag message
i-f4ed9ff5 2011-06-28T21:26:00+09:00 facility priority tag message
i-f4ed9ff5 2011-06-28T21:25:59+09:00 facility priority tag message
i-f4ed9ff5 2011-06-28T21:25:57+09:00 facility priority tag message
i-f4ed9ff5 2011-06-28T21:25:56+09:00 facility priority tag message

結構コード書き散らしちゃったから、そろそろ収束させないと...
--------
http://www.suz-lab.com

3 コメント:

HassyJones さんのコメント...

いつも参考にさせていただいてます。
貴重な情報、大変助かっています。

質問です。

もし、DBではなく、ログをファイルに書き出すとしたら、どこに書き出すのがよいものでしょうか。
頭を悩ませています。素直にDBに出力するのがいいのでしょうか?

suz-lab さんのコメント...

お役に立てて幸いです。

僕ならログ出力専用サーバ(インスタンス)を立てて、
それをNFSサーバとして、各インスタンスが
NFSマウントしてそこにログを出力するか、
Syslogを使って、そのログ出力サーバに、
全てのインスタンスのSyslogを集約させるような形にするのでは、
と思います。

HassyJones さんのコメント...

ご回答ありがとうございます!
早速試してみたいと思います!!