Apacheモジュールを作る「postgresでロギングする(3)」

続き

  • Apacheモジュールを作る「postgresでロギングする(2)」 の続き。
    (2)では、httpd.conf設定の読み込みまで行ったので、Postgresに実際に接続する。

ここでもPostgres関係で
PostgreSQL とやりとりする Apache のモジュールを作ってみた
を大いに参考にさせていただきました。

ここではDBデーモン、Postgresが既に稼動していることが前提です。

DB関連の操作

  • DBユーザ作成
    $ createuser apache_logger
    Shall the new role be a superuser? (y/n) n
    Shall the new role be allowed to create databases? (y/n) n
    Shall the new role be allowed to create more new roles? (y/n) n
    セキュリティの観点から権限は与えない方が良いでしょう。
  • DB作成
    $ createdb -O apache_logger -E Unicode apache_log_db
    オプションは-Oでオーナ指定、-Eで文字コード指定、最後にDB名を指定する。
  • 再起動 パスワード認証等の設定をした場合は、再起動する。
    $ vi pg_hba.conf 
    $ pg_ctl  -o "-i" restart
  • psqlからの接続 まずはpsqlからの接続を確認。
    $ psql -U apache_logger apache_log_db
    で入れればOK。以降、CSEかpsqlで内容を確認すると良い。
  • テーブル作成 重複しているところがあります。お好みで。プログラムの変更も必要ですが;
    CREATE TABLE t_apache_log
    (
    -- 後々プログラム側から利用する場合あった方がよいだろう
     log_id serial PRIMARY KEY,
    
    -- combineフォーマット
     remote_host VARCHAR(64),
     remote_logname VARCHAR(64),
     remote_user VARCHAR(64),
     request_line text,
     status int,
     bytes_sent int,
     referer text,
     user_agent text,
    
    -- 後はお好み
     request_method VARCHAR(16),
     content_type VARCHAR(32),
     child_pid int,
     request_query text,
     request_uri text,
    
    -- postgres時間を入れよう(ラクだから)
     request_time timestamp
    
    );

Makefileの編集

Postgresに接続するためにはPostgresのライブラリが必要だ。そこでMakefileを編集する。

#DEF=-Dmy_define=my_value
#INC=-Imy/include/dir
#LIB=-Lmy/lib/dir -lmylib
INC=-I/usr/local/pgsql/include
LIB=-L/usr/local/pgsql/lib

パスの部分は、人により異なると思うが、pg_configで調べた値を書けばよい。

DB関連ラッパー関数追加

Apacheとはあまり関係ない部分なので適当。 また各種定数をデファインしておく。

#include <libpq-fe.h>
#include <libpq/libpq-fs.h>
#define BUF_SIZE (4096)
  • 接続
    static PGconn *connect_db( request_rec *r, PGLogger_cfgs *cfg)
    {
        PGconn *conn;
        // 接続して
        conn = PQsetdbLogin(cfg->host, NULL, NULL, NULL,cfg->dbname, cfg->user, NULL);
        if(PQstatus(conn) == CONNECTION_BAD) {
             // ダメならNULLを返す
             ap_log_rerror(APLOG_MARK, APLOG_NOTICE, r, "db connection failed(%s)\n",__func__);
             return(NULL);
        }
        return(conn);
    } 
    
  • 切断
    static void disconnect_db(PGconn *conn)
    {
        PQfinish(conn);
        conn =(NULL);
    }
    
  • 挿入
    static int insert_data(request_rec *r,PGconn *conn,char *sql)
    {
       PGresult *res;
       res = PQexec(conn,sql);
       if (PQresultStatus(res) != PGRES_COMMAND_OK) {
           ap_log_rerror(APLOG_MARK, APLOG_NOTICE, r, "sql failed:%s(%s)\n",PQerrorMessage(conn),__func__);
           return(-1);
       }
       PQclear(res);
       return(1);
    }

ロガー処理関数

やっとメインの定義。といっても接続して、SQL作って、発行、切断するだけ。 値の取得は、apacheのソース内にある「mod_log_config.c」が非常に参考になる。 またPQescapeString?でエスケープしてSQLインジェクションを防ぐことが非常に重要だ。

static int pg_logger_transaction(request_rec *r)
{
    PGLogger_cfgs *cfg;
    PGconn *conn;
    PGresult *res;
    char *buf1;
    char *buf2;

    buf1=ap_pcalloc(r->pool,sizeof(char)*BUF_SIZE);
    buf2=ap_pcalloc(r->pool,sizeof(char)*BUF_SIZE);

    //     ログSQL
    char *sql;
    
    //     設定読み込み
    cfg = ap_get_module_config(r->per_dir_config, &pg_logger_module);

    //     クエリー生成 ここから
    sql = ap_psprintf(r->pool,"INSERT INTO %s (",cfg->table);

    sql = ap_pstrcat(r->pool,sql,"remote_host",NULL);

    //     〜省略〜
    sql = ap_pstrcat(r->pool,sql,",request_time",NULL);

    sql = ap_pstrcat(r->pool,sql,") VALUES(",NULL);

    buf1= ap_escape_logitem(r->pool,ap_get_remote_host(r->connection, r->per_dir_config,REMOTE_NAME));
    if(buf1==NULL){
         sql = ap_pstrcat(r->pool,sql,"'-',",NULL);
    }else{
         PQescapeString(buf2,buf1,BUF_SIZE);
         sql = ap_pstrcat(r->pool,sql,ap_psprintf(r->pool,"'%s',",buf2),NULL);
    }

    //     〜省略〜

    sql = ap_pstrcat(r->pool,sql,"current_timestamp",NULL);
    
    sql = ap_pstrcat(r->pool,sql,");",NULL);
    
    //     クエリー生成 ここまで
    
    //     SQLのデバッグ出力
    //ap_log_rerror(APLOG_MARK, APLOG_NOTICE, r, "%s",sql);

    //     接続
    if((conn = connect_db(r, cfg)) == NULL) {
         //     ログできなくてもひとまず返す
         return(OK);
    }
    //     挿入
    insert_data(r,conn,sql);
    
    //     切断
    disconnect_db(conn);
    
   return(OK);
}

コンテンツハンドラの削除

ここまでで正常にDBに入っていることが確認できたら、コンテンツハンドラを削除する。 なぜならこのモジュールはロガーであって、クライアントに返すわけではないからである。 モジュール構造体の

   pg_logger_handlers,    /* [#8] MIME-typed-dispatched handlers */

   NULL,                  /* [#8] MIME-typed-dispatched handlers */

に。 また、httpd.confも修正する。

LoadModule pg_logger_module /usr/local/apache/bin/pg_logger/mod_pg_logger.so
<Location /pg_logger>
 SetHandler pg_logger
 PGLoggerHost localhost
 PGLoggerDBname apache_log_db
 PGLoggerUser apache_logger
 PGLoggerTable t_apache_log
</Location>

から

LoadModule pg_logger_module /usr/local/apache/bin/pg_logger/mod_pg_logger.so
#SetHandler pg_logger
PGLoggerHost localhost
PGLoggerDBname apache_log_db
PGLoggerUser apache_logger
PGLoggerTable t_apache_log

へ。

あとがき

今回はモジュールの中でも若干特殊なロガーの実装だった(そうでもないか。)。やることは簡単だが、本がない中やるのは、WEBの情報が少ない現在、なかなか苦労した(半日以上を要した)。動作が若干不安なので、しばらくは通常ファイルのログと併用することになるだろう。

参考


トップ   編集 凍結解除 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2010-11-28 (日) 21:47:37 (2310d)