-->

2011-07-07

syslog-ngのログをmysqlに記録する

syslog-ng.confの修正。
必要な場合、tcp経由で記録する設定を追加する。
mysqlのINSERT文をログフォーマットにする。1秒ごとに個別のファイルに残す。
フィルターでloggerコマンドのデフォルト値などの場合のみに記録するようにする。
src, kernsrc, net のログをフィルターを通した後にINSERT文のフォーマットで記録する。
$ rcsdiff -r1.15 /etc/config-archive/etc/syslog-ng/syslog-ng.conf,v /etc/syslog-ng/syslog-ng.conf
===================================================================
RCS file: /etc/config-archive/etc/syslog-ng/syslog-ng.conf,v
retrieving revision 1.15
diff -r1.15 /etc/syslog-ng/syslog-ng.conf
23c23
< source net { udp(); };
---
> source net { udp(); tcp(port(4800) keep-alive(yes) max_connections(256)); };
60a61,64
> destination database { file("/var/log/dblog/fulllog.$YEAR.$MONTH.$DAY.$HOUR.$MIN.$SEC"
> template("INSERT INTO logs$YEAR$MONTH (crtm, host, addr, facility, level, pri, program, pid, msghdr, msg) VALUES('$ISODATE', '$HOST', INET_ATON('$SOURCEIP'), '$FACILITY_NUM', '$LEVEL_NUM', '$PRI', '$PROGRAM', '$PID', '$MSGHDR', '$MSG');\n")
> owner(root) group(root) perm(0600) dir_perm(0700) create_dirs(yes) template-escape(yes)); };
>
91a96,97
> filter f_database { facility(user) and level(notice) };
>
117a124,125
>
> log { source(src); source(kernsrc); source(net); filter(f_database); destination(database); };

mysqlの設定。
データベース作成、専用書き込みユーザー作成、テーブルとトリガー(update,deleteを邪魔する)作成。
$ cat bin/syslog.sql
-- CREATE DATABASE syslog;
-- GRANT SELECT,INSERT ON syslog.* TO syslogappend@localhost IDENTIFIED BY 'syslogappendpasswd';

DROP TABLE IF EXISTS logs201107;
CREATE TABLE logs201107 (
id       INT UNSIGNED      NOT NULL AUTO_INCREMENT,
crtm     TIMESTAMP         NOT NULL,
host     VARBINARY(63)     NOT NULL,
addr     INT UNSIGNED      NOT NULL,
facility TINYINT           NOT NULL,
level    TINYINT           NOT NULL,
pri      INT               NOT NULL,
program  VARBINARY(31)     NOT NULL,
pid      SMALLINT UNSIGNED NOT NULL,
msghdr   VARBINARY(63)     NOT NULL,
msg      BLOB              NOT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM DEFAULT CHARSET=binary;

DROP TRIGGER IF EXISTS trigger_logs201107_before_update;
DROP TRIGGER IF EXISTS trigger_logs201107_before_delete;
DELIMITER |
CREATE TRIGGER trigger_logs201107_before_update BEFORE UPDATE ON logs201107
  FOR EACH ROW BEGIN
    CALL ERROR_UPDATE_NOT_ALLOWED();
  END;
|
CREATE TRIGGER trigger_logs201107_before_delete BEFORE DELETE ON logs201107
  FOR EACH ROW BEGIN
    CALL ERROR_DELETE_NOT_ALLOWED();
  END;
|
DELIMITER ;

ログをDBに書きこむスクリプト。
$ cat bin/syslog.sh
#!/bin/sh

LANG=C
LC_ALL=C
PATH="/bin:/usr/bin"

SLEEP=1

while true
do
    FILE=$(find /var/log/dblog/ -name "fulllog*" | sort | head -n1)
    if [ -f "$FILE" ]; then
        echo $FILE"を処理します。"
        cat $FILE | mysql -usyslogappend -psyslogappendpasswd syslog
        if [ $? -ne 0 ]; then
            echo "失敗"
            exit 1
        fi
        echo $FILE"を消します。"
        rm -f $FILE;
    else
        echo $SLEEP"秒休みます。"
        sleep $SLEEP
    fi
done

テスト。書き込みスクリプトの起動。
$ sudo sh bin/syslog.sh
1秒休みます。
...
1秒休みます。
/var/log/dblog/fulllog.2011.07.07.01.06.12を処理します。
/var/log/dblog/fulllog.2011.07.07.01.06.12を消します。
1秒休みます。
1秒休みます。
/var/log/dblog/fulllog.2011.07.07.01.06.14を処理します。
/var/log/dblog/fulllog.2011.07.07.01.06.14を消します。
1秒休みます。
/var/log/dblog/fulllog.2011.07.07.01.06.15を処理します。
/var/log/dblog/fulllog.2011.07.07.01.06.15を消します。
1秒休みます。
...

別コンソールでログを書きこむ。
$ logger "hello1"
$ logger "hello2"
$ logger "hello3"

確認する。
mysql> select *,INET_NTOA(addr) from logs201107 order by id desc limit 10;
+----+---------------------+-----------+------------+----------+-------+-----+---------+-----+----------+--------+-----------------+
| id | crtm                | host      | addr       | facility | level | pri | program | pid | msghdr   | msg    | INET_NTOA(addr) |
+----+---------------------+-----------+------------+----------+-------+-----+---------+-----+----------+--------+-----------------+
|  3 | 2011-07-07 01:06:15 | amdgentoo | 2130706433 |        1 |     5 |  13 | logger  |   0 | logger:  | hello3 | 127.0.0.1       |
|  2 | 2011-07-07 01:06:14 | amdgentoo | 2130706433 |        1 |     5 |  13 | logger  |   0 | logger:  | hello2 | 127.0.0.1       |
|  1 | 2011-07-07 01:06:12 | amdgentoo | 2130706433 |        1 |     5 |  13 | logger  |   0 | logger:  | hello1 | 127.0.0.1       |
+----+---------------------+-----------+------------+----------+-------+-----+---------+-----+----------+--------+-----------------+
3 rows in set (0.00 sec)

全部記録する場合 syslog-ng.conf の filter(f_database); の部分を消します。
記録が追いつかない場合、ログをファイルに書きこむやり方以外の方法もあります。
名前付きパイプ、プログラムに渡す、など。
もしくは別の言語などで複数の書き込み用のプロセスを起動します。
何かおかしい場合などは公式のマニュアルかソースを見ると古い情報を観る場合が減ります。



以下は詳細。

リモートの場合、日付はどうなる?
$ sudo /etc/init.d/syslog-ng reload
syslog-ng       |Your configuration file uses an obsoleted keyword, please update your configuration; keyword='use_time_recvd', change='Use R_ or S_ prefixed macros in templates or keep_timestamp(no)'
syslog-ng       |Error parsing main, syntax error, unexpected KW_USE_TIME_RECVD, expecting '}' in /etc/syslog-ng/syslog-ng.conf at line 13, column 9:
syslog-ng       |
syslog-ng       |        use_time_recvd (yes);
syslog-ng       |        ^^^^^^^^^^^^^^
syslog-ng       |
syslog-ng       |syslog-ng documentation: http://www.balabit.com/support/documentation/?product=syslog-ng
syslog-ng       |mailing list: https://lists.balabit.hu/mailman/listinfo/syslog-ng
syslog-ng       | * Configuration error. Please fix your configfile (/etc/syslog-ng/syslog-ng.conf)                     [ !! ]

keep_timestamp(no) で $ISODATE などを全体的に変更する。古い場合 use_time_recvd(yes);
または $R_ISODATE, $S_ISODATE など手動で指定する。

古い場合 $MSG => $MSGONLY など。

$HOST or $SOURCEIP だけにするなど。$SOURCEIPは文字が良いなど。
($FACILITY_NUM, $LEVEL_NUM) or $PRI だけにするなど。もしくは$TAGにするなど。
($PROGRAM, $PID) or $MSGHDR だけにするなど。

フィルター変更
filter f_database { ... };
or
フィルター無しなど。
log { source(src); source(kernsrc); source(net); destination(database); };

タブ区切りなどにして、insertする側で色々加工するなど。$MSGからindex対象の取り出し、文字コード統一など。

ログの取得方法変更 => 1秒ごとにファイル => 名前付きパイプ、デーモンの指定など。

ドキュメント。
/usr/share/doc/syslog-ng-3.2.4/syslog-ng.conf.doc.bz2
syslog-ng-3.2.4/contrib/syslog-ng.conf.doc
など。

他の変数。
./lib/templates.c
./lib/logmsg.c
など。

テスト用設定の例。
template("FACILITY          = '$FACILITY'\nFACILITY_NUM      = '$FACILITY_NUM'\nPRIORITY          = '$PRIORITY'\nLEVEL             = '$LEVEL'\nLEVEL_NUM         = '$LEVEL_NUM'\nTAG               = '$TAG'\nTAGS              = '$TAGS'\nBSDTAG            = '$BSDTAG'\nPRI               = '$PRI'\nDATE              = '$DATE'\nFULLDATE          = '$FULLDATE'\nISODATE           = '$ISODATE'\nSTAMP             = '$STAMP'\nYEAR              = '$YEAR'\nYEAR_DAY          = '$YEAR_DAY'\nMONTH             = '$MONTH'\nMONTH_WEEK        = '$MONTH_WEEK'\nMONTH_ABBREV      = '$MONTH_ABBREV'\nMONTH_NAME        = '$MONTH_NAME'\nDAY               = '$DAY'\nHOUR              = '$HOUR'\nMIN               = '$MIN'\nSEC               = '$SEC'\nWEEKDAY           = '$WEEKDAY'\nWEEK_DAY          = '$WEEK_DAY'\nWEEK_DAY_ABBREV   = '$WEEK_DAY_ABBREV'\nWEEK_DAY_NAME     = '$WEEK_DAY_NAME'\nWEEK              = '$WEEK'\nTZOFFSET          = '$TZOFFSET'\nTZ                = '$TZ'\nUNIXTIME          = '$UNIXTIME'\nR_DATE            = '$R_DATE'\nR_FULLDATE        = '$R_FULLDATE'\nR_ISODATE         = '$R_ISODATE'\nR_STAMP           = '$R_STAMP'\nR_YEAR            = '$R_YEAR'\nR_YEAR_DAY        = '$R_YEAR_DAY'\nR_MONTH           = '$R_MONTH'\nR_MONTH_WEEK      = '$R_MONTH_WEEK'\nR_MONTH_ABBREV    = '$R_MONTH_ABBREV'\nR_MONTH_NAME      = '$R_MONTH_NAME'\nR_DAY             = '$R_DAY'\nR_HOUR            = '$R_HOUR'\nR_MIN             = '$R_MIN'\nR_SEC             = '$R_SEC'\nR_WEEKDAY         = '$R_WEEKDAY'\nR_WEEK_DAY        = '$R_WEEK_DAY'\nR_WEEK_DAY_ABBREV = '$R_WEEK_DAY_ABBREV'\nR_WEEK_DAY_NAME   = '$R_WEEK_DAY_NAME'\nR_WEEK            = '$R_WEEK'\nR_TZOFFSET        = '$R_TZOFFSET'\nR_TZ              = '$R_TZ'\nR_UNIXTIME        = '$R_UNIXTIME'\nS_DATE            = '$S_DATE'\nS_FULLDATE        = '$S_FULLDATE'\nS_ISODATE         = '$S_ISODATE'\nS_STAMP           = '$S_STAMP'\nS_YEAR            = '$S_YEAR'\nS_YEAR_DAY        = '$S_YEAR_DAY'\nS_MONTH           = '$S_MONTH'\nS_MONTH_WEEK      = '$S_MONTH_WEEK'\nS_MONTH_ABBREV    = '$S_MONTH_ABBREV'\nS_MONTH_NAME      = '$S_MONTH_NAME'\nS_DAY             = '$S_DAY'\nS_HOUR            = '$S_HOUR'\nS_MIN             = '$S_MIN'\nS_SEC             = '$S_SEC'\nS_WEEKDAY         = '$S_WEEKDAY'\nS_WEEK_DAY        = '$S_WEEK_DAY'\nS_WEEK_DAY_ABBREV = '$S_WEEK_DAY_ABBREV'\nS_WEEK_DAY_NAME   = '$S_WEEK_DAY_NAME'\nS_WEEK            = '$S_WEEK'\nS_TZOFFSET        = '$S_TZOFFSET'\nS_TZ              = '$S_TZ'\nS_UNIXTIME        = '$S_UNIXTIME'\nSDATA             = '$SDATA'\nMSGHDR            = '$MSGHDR'\nSOURCEIP          = '$SOURCEIP'\nSEQNUM            = '$SEQNUM'\nMSG               = '$MSG'\nMESSAGE           = '$MESSAGE'\nHOST              = '$HOST'\n\nHOST          = '$HOST'\nHOST_FROM     = '$HOST_FROM'\nMESSAGE       = '$MESSAGE'\nPROGRAM       = '$PROGRAM'\nPID           = '$PID'\nMSGID         = '$MSGID'\nSOURCE        = '$SOURCE'\nLEGACY_MSGHDR = '$LEGACY_MSGHDR'\n---separate---\n")

結果。
$ logger "debug"
$ sudo cat /var/log/dblog/fulllog.2011.07.07.01.30.48
FACILITY          = 'user'
FACILITY_NUM      = '1'
PRIORITY          = 'notice'
LEVEL             = 'notice'
LEVEL_NUM         = '5'
TAG               = '0d'
TAGS              = '.source.src'
BSDTAG            = '5B'
PRI               = '13'
DATE              = 'Jul  7 01:30:48'
FULLDATE          = '2011 Jul  7 01:30:48'
ISODATE           = '2011-07-07T01:30:48+09:00'
STAMP             = 'Jul  7 01:30:48'
YEAR              = '2011'
YEAR_DAY          = '188'
MONTH             = '07'
MONTH_WEEK        = '1'
MONTH_ABBREV      = 'Jul'
MONTH_NAME        = 'July'
DAY               = '07'
HOUR              = '01'
MIN               = '30'
SEC               = '48'
WEEKDAY           = 'Thu'
WEEK_DAY          = '5'
WEEK_DAY_ABBREV   = 'Thu'
WEEK_DAY_NAME     = 'Thursday'
WEEK              = '27'
TZOFFSET          = '+09:00'
TZ                = '+09:00'
UNIXTIME          = '1309969848'
R_DATE            = 'Jul  7 01:30:48'
R_FULLDATE        = '2011 Jul  7 01:30:48'
R_ISODATE         = '2011-07-07T01:30:48+09:00'
R_STAMP           = 'Jul  7 01:30:48'
R_YEAR            = '2011'
R_YEAR_DAY        = '188'
R_MONTH           = '07'
R_MONTH_WEEK      = '1'
R_MONTH_ABBREV    = 'Jul'
R_MONTH_NAME      = 'July'
R_DAY             = '07'
R_HOUR            = '01'
R_MIN             = '30'
R_SEC             = '48'
R_WEEKDAY         = 'Thu'
R_WEEK_DAY        = '5'
R_WEEK_DAY_ABBREV = 'Thu'
R_WEEK_DAY_NAME   = 'Thursday'
R_WEEK            = '27'
R_TZOFFSET        = '+09:00'
R_TZ              = '+09:00'
R_UNIXTIME        = '1309969848'
S_DATE            = 'Jul  7 01:30:48'
S_FULLDATE        = '2011 Jul  7 01:30:48'
S_ISODATE         = '2011-07-07T01:30:48+09:00'
S_STAMP           = 'Jul  7 01:30:48'
S_YEAR            = '2011'
S_YEAR_DAY        = '188'
S_MONTH           = '07'
S_MONTH_WEEK      = '1'
S_MONTH_ABBREV    = 'Jul'
S_MONTH_NAME      = 'July'
S_DAY             = '07'
S_HOUR            = '01'
S_MIN             = '30'
S_SEC             = '48'
S_WEEKDAY         = 'Thu'
S_WEEK_DAY        = '5'
S_WEEK_DAY_ABBREV = 'Thu'
S_WEEK_DAY_NAME   = 'Thursday'
S_WEEK            = '27'
S_TZOFFSET        = '+09:00'
S_TZ              = '+09:00'
S_UNIXTIME        = '1309969848'
SDATA             = ''
MSGHDR            = 'logger: '
SOURCEIP          = '127.0.0.1'
SEQNUM            = ''
MSG               = 'debug'
MESSAGE           = 'debug'
HOST              = 'amdgentoo'

HOST          = 'amdgentoo'
HOST_FROM     = 'amdgentoo'
MESSAGE       = 'debug'
PROGRAM       = 'logger'
PID           = ''
MSGID         = ''
SOURCE        = 'src'
LEGACY_MSGHDR = 'logger: '
---separate---

テーブル名をどうするか?
全部logs, logsYYYYMM, 名前はlogsでmysqlパーティショニングなど。
全部logsの場合でdeleteする場合myisamは重い。
logsYYYYMMなどは検索が面倒かもしれない。手動で検索する場合。
パーティショニングは手動インストールが必要かもしれない。
mysql> SHOW VARIABLES LIKE '%partition%';
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| have_partitioning | NO    |
+-------------------+-------+
1 row in set (0.00 sec)

24ヶ月先までテーブル作成。(seq 0 24 で今月分も含む。存在する場合、初期化される)
$ for num in $(seq 1 24); do cat bin/syslog.sql | sed "s/logs201107/logs$(date --date "${num} months" "+%Y%m")/g" | mysql -uroot syslog; done
mysql> show tables;
+------------------+
| Tables_in_syslog |
+------------------+
| logs201107       |
| logs201108       |
| logs201109       |
| logs201110       |
| logs201111       |
| logs201112       |
| logs201201       |
| logs201202       |
| logs201203       |
| logs201204       |
| logs201205       |
| logs201206       |
| logs201207       |
| logs201208       |
| logs201209       |
| logs201210       |
| logs201211       |
| logs201212       |
| logs201301       |
| logs201302       |
| logs201303       |
| logs201304       |
| logs201305       |
| logs201306       |
| logs201307       |
+------------------+
25 rows in set (0.00 sec)

追記: OS起動時に起動する例。
$ ll /usr/local/bin/sqlsyslogd
-rwxr-xr-x 1 root root 507 2011-07-07 05:35:50 /usr/local/bin/sqlsyslogd*

ファイルは上記の例とおなしshスクリプト。
$ head -n1 /usr/local/bin/sqlsyslogd
#!/bin/sh

起動してバックグラウンドに移動させる。
$ cat /etc/local.d/sqlsyslogd.start
/usr/local/bin/sqlsyslogd > /dev/null 2>&1 &

確認する。
$ pstree -pal 9743
sqlsyslogd,9743 /usr/local/bin/sqlsyslogd
  └─sleep,10672 1

ディストリビューションごとに書く場所が違う。
$ cat /etc/rc.local
#!/bin/sh
#
# This script will be executed *after* all the other init scripts.
# You can put your own initialization stuff in here if you don't
# want to do the full Sys V style init stuff.

touch /var/lock/subsys/local

ダイレクトにデータベースに入れる機能があるように見えます。
これが良さそうなので、このページの例は使わないほうがいいかもしれません。
$ find ./syslog-ng-3.2.4/ -iname "*sql*"
./syslog-ng-3.2.4/tests/functional/test_sql.py
./syslog-ng-3.2.4/modules/afsql
./syslog-ng-3.2.4/modules/afsql/afsql.c
./syslog-ng-3.2.4/modules/afsql/afsql-grammar.h
./syslog-ng-3.2.4/modules/afsql/afsql-grammar.ym
./syslog-ng-3.2.4/modules/afsql/afsql.h
./syslog-ng-3.2.4/modules/afsql/afsql-grammar.c
./syslog-ng-3.2.4/modules/afsql/afsql-parser.c
./syslog-ng-3.2.4/modules/afsql/afsql-grammar.y
./syslog-ng-3.2.4/modules/afsql/afsql-parser.h
./syslog-ng-3.2.4/modules/afsql/afsql-plugin.c

$ grep -nriP "mysql|postgresql" ./syslog-ng-3.2.4/modules/afsql
./syslog-ng-3.2.4/modules/afsql/afsql.c:1146:  self->type = g_strdup("mysql");

0 件のコメント: