互联网后台服务器流水日志系统设计

在互联网后台服务器中常需要记录互联网软件的流水日志,日志服务器和入库工具则是处理此类功能。

日志服务器会接受逻辑服务器发送的日志消息,将其写入本地日志文件。每隔一段时间,再由日志入库服务器将日志文件导入数据库。

1. 日志服务器

日志服务器接收其他服务器服务器发送的json格式日志消息,并写入到日志文件。

日志服务器不需要解析消息内容,定时批量写入日志文件。

日志文件包含日志描述文件和日志数据文件。

1.2. 日志消息

日志服务器接收其他服务器发来的消息,消息头跟一般消息一致,命令类型为服务器指定命令。

消息体为json格式,日志命令为独立命令,根据日志解析配置来填写。内容如下:

{

    "log_cmd": 1,

    "fields": {

        "uid": 10000,

        "guid": "10001",

        "nickname": "张三",

        "mac": "64-00-6A-05-3B-DD",

        "ip": "192.168.10.124",

        "login_type": "1",

        "login_time": 1447816170,

        "logout_time": 1447816270,

        "logout_type": 1,

        "online_time": 100,

        "log_time": 1447816270

    }

}

日志命令log_cmd为1,需要记录的字段在fields内。

1.3 日志描述文件

日志描述文件会记录数据记录的个数、文件的标识和版本,以及保留字段。日志文件头为以后的日志拓展和简要分析提供依据。日志描述文件的命名方式为*.fb

日志文件头格式如下:

struct LogHeader  

{  

    LogHeader() 

    {  

        memset(this, 0, sizeof(LogHeader));  

    }  

    //日志文件固定标识为: 'L', 'O', 'G', 0分别占uint32的一个字节 

    uint32 nIdent;  

    //日志文件格式版本号

    uint32 nVersion;  

    //日志文件中存储的记录数量  

    uint32 nRecordCount;  

    //保留字节  

    char sReserves[20];  

};

1.4 日志数据文件  

日志数据文件存储日志数据记录,包含数据记录头和数据记录体。日志数据文件命名方式为*.fd

日志数据记录存储格式如下:

数据记录头数据记录体数据记录头数据记录体2...

1.5 数据记录头

数据记录头包含本条日志的简要信息。消息内容变动时修改数据记录头的数据版本号。

可以一次写入多个日志消息到日志文件,以提高写入效率。在内存中以消息队列的方式来存储将要写文件的日志消息。

数据记录头格式:

struct LogDataHeader  

{  

uint16 nCmdType;//命令类型  

uint16 nDataVersion;//数据版本号

uint16 nBodySize;//数据记录体大小  

};

数据记录头初始化,例如:

struct LogDataHeader logDataHeader;

logDataHeader.nCmdType = 1;//日志表类型  

logDataHeader.nDataVersion = 1;//数据版本号

logDataHeader.nBodySize = fieldsSize;//数据记录体大小,为日志消息中消息体的fields的长度(即实际日志内容的长度)  

 

1.6 数据记录体

数据体内容为具体的消息内容,一个数据记录体包含一个日志消息的消息体,不需要包含消息头,格式为json格式的消息体,具体格式参考日志解析。

数据记录体跟logic服务器发来的消息的fields字段的内容一致,例如:

{

        "uid": 10000,

        "guid": "10001",

        "nickname": "张三",

        "mac": "64-00-6A-05-3B-DD",

        "ip": "192.168.10.124",

        "login_type": "1",

        "login_time": 1447816170,

        "logout_time": 1447816270,

        "logout_type": 1,

        "online_time": 100,

        "log_time": 1447816270

}

1.7 日志处理

接收日志后,存储在内存的日志队列。定时器定时检查日志队列。每隔一段时间写一次日志到最新的日志文件,每隔一段时间创建新的日志文件。

配置日志目录和日志备份目录,日志消息队列最大长度,写日志时间间隔,创建新日志文件时间间隔。配置如下:

{

"datalog_path":"datalog/logfile/",

"datalog_path_bak":"datalog/logfilebak/",

"log_queue_num":10,

"logtime_write_interval":10.0,

"logtime_create_log_interval":120.0

}

2 日志入库工具

日志入库服务器定时检查日志文件,并读取日志文件,解析之后根据不同的表分别写入不同的db后缀文件,然后采用load data 方式入库。入库成功完成后将移动已读日志文件到日志备份目录,和把已入库的db后缀文件移动到db后缀文件备份目录

2.1 日志解析

读取非最新的日志文件,并根据配置文件信息,将读取的日志内容进行解析,然后分别写入临时文件。

日志入库服务器在启动时加载日志解析配置,并保存各个命令的对应的转换后的入库格式。在解析日志文件时,将读取数据记录头的命令类型,和数据记录体的日志消息体的数据内容,根据日志表配置的入库格式来解析日志消息体数据,然后写入到db后缀文件

1.2.2. 日志表和日志表配置

日志表是预先创建在mysql的一系列与日志有关的表。存储在一个单独的日志库中。日志表都以日志时间为索引,不分表,在容量扩大后可以分区。

每张日志表都需要配置在日志解析配置文件中,包括表指定的类型数字,表名和表字段。

日志解析配置文件(LogTables.json),只使用英文和数字,格式如下:

{

" logcmds":[log_cmd1],

    " log_cmd1": {

        "table": "tablename1",

        "fields": [

            "field1",

            "field2"

        ]

    }

}

Logcmds为需要加载的表的列表

log_cmd 为日志类型,为数字,每个表有一个表类型,定义在程序中。

table为数据库表名,为字符串和数字组合。

fields为需要插入的字段名的列表。注意字段列表的字段顺序需要跟数据表的字段顺序是完全一致的。

field为字段名。配置字段名跟数据表的字段的名字完全一样。

例如,对于用户登录日志,配置如下

{

    " logcmds":[1],

    "1": {

        "tablename": "tb_userlogin_log",

        "fields": [

            "uid",

            "guid",

            "nickname",

            "mac",

            "ip",

            "login_type",

            "login_time",

            "logout_time",

            "logout_type",

            "online_time",

            "log_time"

        ]

    }

}

其中,日志命令类型为1,表名为tb_userlogin_log,字段名为"uid","guid","nickname","mac","ip","login_type","login_time","logout_time","logout_type","online_time","log_time"


1.2.3. 日志入库处理和入库程序配置

把解析后的日志文件写入到db文件,并备份日志文件到日志文件备份目录。导入db文件到mysql数据库,然后备份db文件到db文件备份目录。

配置数据库连接信息,日志文件目录和日志备份文件目录、db文件目录和备份目录。配置如下

{

"dbip":"192.168.18.22",

    "dbport":3306,

    "dbuser":"im",

    "dbpwd":"im",

    "dbname":"db_im3_log",

    "dbcharacterset":"utf8",

"datalog_path":"datalog/logfile/",

"datalog_path_bak":"datalog/logfilebak/",

"dblog_path":"datalog/dbfile/",

"dblog_path_bak":"datalog/dbfilebak/"

}

1.2.4. db后缀文件

db后缀文件以表名来命名,tablename.db(db为文件拓展名,保存在db文件目录,简称db文件分别存储将要入库到不同表的日志数据,需要写入的表跟db文件同名(也是tablename),采用各个文件分别加载的方式。手动提前创建需要的表,分区规则之后按需求根据时间段手动创建。

db文件为导入数据库文件,文件格式为制表符'\t'分开各个字段,每行表示一条记录,行末尾为换行符LF (’\n’)表示。

db文件中的各个字段跟配置文件中的字段需要一一对应,由于消息版本的不一致导致字段对不上的(包括表字段修改,字段减少或增加),以空字段补上,加载入数据表时会被替换成默认值。

db文件字段分隔符采用制表符'\t',日志内容中的字符串若使用制表符,比如含有tab键,则必须在前面加上转义符号'\',否则在同时出现单引号'和制表符(如' ,后面的为tab键)时会发生错位;字符串中可以出现字符'\t'。

1.2.5. 入库方式

采用mysql load方式来入库,加载需指定插入的表的字段。语句如下:

load data local infile "D:/ tb_userlogin_log.log" into table db. tb_userlogin_log fields terminated by '\t' enclosed by '\'' lines terminated by '\n' (uid, guid, nickname,mac,ip,login_type, login_time,logout_time,logout_type,online_time,log_time)

加载语句的字段生成来自于配置文件。表添加新字段时,对旧的日志消息可以兼容,如果表字段减少了,则不保存被减少;如果字段修改了,则不保存被修改字段,或者根据版本号进行修改。

 

1.2.6. 脚本定义

编写脚本start_logtodbserver.sh来运行程序入库程序logtodbserver

命令 start 开启

命令 stop 关闭

不带命令则默认执行start命令

#!/bin/bash

ASYNC_SERVER_PATH=`pwd`

ASYNC_BIN=${ASYNC_SERVER_PATH}/bin

ASYNC_CONF=${ASYNC_SERVER_PATH}/conf

logtodbwork()

{

if test $( pgrep  "LogToDBServer_" | wc -l ) -eq 0

then #don't has this service 

${ASYNC_BIN}/LogToDBServer ${ASYNC_CONF}/LogToDBServer.json

else

#it has been started

    sleep 1

fi

}

 

logtodbstopwork()

{

#echo "stopservice:LogToDBServer"

#ps -ef |grep LogToDBServer

pkill -9 LogToDBServer -u `whoami`

}

 

case $1 in 

start)

logtodbwork

;;

stop)

logtodbstopwork 

sleep 2

;;

*)

logtodbwork

;;

esac


1.2.7. crontab使用

采用crontab 定时运行shell脚本来执行程序。

crontab -e  编辑用户的cron配置文件保存退出,系统会自动就存放于/var/spool/cron/目录中,文件以用户名命名

linuxcron服务是每隔一分钟去读取一次/var/spool/cron,/etc/crontab,/etc/cron.d下面所有的内容

Example of job definition:

 .---------------- minute (0 - 59)

 |  .------------- hour (0 - 23)

 |  |  .---------- day of month (1 - 31)

 |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...

 |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat

 |  |  |  |  |

 *  *  *  *  * user-name command to be executed

 *  *  *  *  *  command

分 时 日 月 周   命令

1列表示分钟159, 每分钟用*或者 */1表示

2列表示小时123(0表示0)

3列表示日期131

4列表示月份112

5列标识号星期06(0表示星期天)

6列要运行的命令

crontab [ u user ] 文件 

crontab [ u user ] { l | r | e }

-u:指定某一用户

e:执行文字编辑器来设定用户(当前用户或指定用户)时程表,内定的文字编辑器是vi.

r:删除用户时程表.

l:列出用户时程表.

如 * * * * * /bin/usershell  每天每分钟执行一次/bin/usershell

0-12 * * * * /bin/usershell  每天每小时从012分钟每分钟执行一次/bin/usershell

* */2 * * * /bin/usershell  每天每2小时执行一次/bin/usershell

* 1,3,5,7 * * * /bin/usershell  每天每逢1,3,4,7点执行一次/bin/usershell

1.2.8. 脚本定时运行

暂定每30分钟执行一次入库脚本。

执行日志入库工具(LogToDBServer)准备:

编辑crontab 

crontab -e

*/30 * * * * /work/chenjiayi/imdev/im3/start_logtodbserver.sh

/sbin/service crond start

 


你可能感兴趣的:(架构)