そろそろKVSを本格導入したいなーと思ったので、TokyoCabinetを少しさわってみた。
とりあえず、DSASの中の人が公開しているmod_syslogを改変して、テーブルデータベースにログを出力するモジュールを、ものすごくいい加減に実装。
#include "apr_hooks.h" #include "ap_config.h" #include "mod_log_config.h" #include "http_config.h" #include <tcutil.h> #include <tctdb.h> #include <stdlib.h> #include <stdbool.h> #include <stdint.h> module AP_MODULE_DECLARE_DATA tctlog_module; static const char syslog_filter_name[] = "LOG_TCT"; static APR_OPTIONAL_FN_TYPE(ap_log_set_writer_init) *set_writer_init; static APR_OPTIONAL_FN_TYPE(ap_log_set_writer) *set_writer; static ap_log_writer_init *prev_log_writer_init = NULL; static ap_log_writer *prev_log_writer = NULL; typedef struct tctlog_config_t { } tctlog_config_t; char dummy[16]; #define PREFIX_TCTLOG "tct:" #define PREFIX_TCTLOG_LENGTH 4 static void *ap_tctlog_writer_init(apr_pool_t *p, server_rec *s, const char* name) { if (strncasecmp(PREFIX_TCTLOG, name, PREFIX_TCTLOG_LENGTH) == 0) { return &dummy[0]; } if (prev_log_writer_init) { return prev_log_writer_init(p, s, name); } return NULL; } static apr_status_t ap_tctlog_writer(request_rec *r, void *handle, const char **strs, int *strl, int nelts, apr_size_t len) { if (handle == dummy) { char *str, *s; int i; TCTDB *tdb; TCMAP *cols; str = apr_palloc(r->pool, len + 1); for (i = 0, s = str; i < nelts; i++) { memcpy(s, strs[i], strl[i]); s += strl[i]; } str[len] = '\0'; tdb = tctdbnew(); tctdbsetmutex(tdb); if(!tctdbopen(tdb, "/tmp/access.tdb", TDBOWRITER | TDBOCREAT)) { return OK; } for (i = 0, s = str; i < len; i++, s++) { if (*s == ' ') { *s = '\0'; s++; break; } } tctdbput3(tdb, str, s); tctdbclose(tdb); tctdbdel(tdb); return OK; } if (prev_log_writer) { return prev_log_writer(r, handle, strs, strl, nelts, len); } return OK; } static int tctlog_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp) { if (!set_writer_init) { set_writer_init = APR_RETRIEVE_OPTIONAL_FN(ap_log_set_writer_init); set_writer = APR_RETRIEVE_OPTIONAL_FN(ap_log_set_writer); } if (!prev_log_writer_init) { void *f; f = set_writer_init(ap_tctlog_writer_init); if (f != ap_tctlog_writer_init) { prev_log_writer_init = f; } f = set_writer(ap_tctlog_writer); if (f != ap_tctlog_writer) { prev_log_writer = f; } } return OK; } static void tctlog_register_hooks(apr_pool_t *p) { static const char *pre[] = { "mod_log_config.c", NULL }; ap_hook_pre_config(tctlog_pre_config, pre, NULL, APR_HOOK_REALLY_LAST); } module AP_MODULE_DECLARE_DATA tctlog_module = { STANDARD20_MODULE_STUFF, NULL, /* create per-dir config structures */ NULL, /* merge per-dir config structures */ NULL, /* create per-server config structures */ NULL, /* merge per-server config structures */ NULL, /* table of config file commands */ tctlog_register_hooks /* register hooks */ };
LogFormat "%{%s}t status\t%>s\tmsec\t%D\turl\t%U" tct CustomLog tct:foo tct
ログはこんな感じ。
[root@andLinux tctlog]# tctmgr list -pv /tmp/access.tdb
1258980571 status 200 msec 0 url /index.html
1258980573 status 304 msec 0 url /index.html
1258980576 status 304 msec 0 url /index.html
1258980579 status 404 msec 0 url /xxx
[root@andLinux tctlog]# tctmgr search -pv /tmp/access.tdb status STREQ 304
1258980573 status 304 msec 0 url /index.html
1258980576 status 304 msec 0 url /index.html
[root@andLinux tctlog]# tctmgr search -pv /tmp/access.tdb url STRINC index
1258980571 status 200 msec 0 url /index.html
1258980573 status 304 msec 0 url /index.html
1258980576 status 304 msec 0 url /index.html
ギガバイト単位のファイルをgrepしなくていいなら、本格的に作り込んでもいいかも。
圧縮もできるし。
あと所感。
- リクエスト単位でopen/closeは良くなと思うけど、どうなんだろう?
- データベース開きっぱなしにして別プロセスからアクセスしたら、ログが見れなかった
- flushが必要なのかも
- Apache終了をフックするための関数が見つからないよー
- データベース開きっぱなしにして別プロセスからアクセスしたら、ログが見れなかった
- strsって、ログって分割されてる?
- こーゆー実装の場合でもtctdbsetmutex()を呼んだ方がいいんだろうか?
- プライマリーキーを自動で付けてくれると便利なのになー