系统日志处理——shell和Nodejs的实践

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011170921/article/details/52165129

问题描述:

声明:仅供分享探讨,不喜勿喷,希望大神们多多指教!

我们的日志系统为分布式日志系统,记录IOS或Android的APP请求后台API的情况,有4台服务器进行日志记录工作。现在的工作就是每天定时得获取分布在服务器的日志然后处理,主要就是做统计工作,这里有两个指标。

PV:页面访问量,这里指的是一个API被访问的次数;

UV:用户访问量,这里指的是一个API被多少个用户不重复访问。

这里列出一条记录案例:

10.130.92.149 - - [07/Aug/2016:03:00:14 +0800] "POST /api/superBike/gpsTrack/uploadGps?access_token=14069e1dc20LWykljPD3r8sbBNj8jFcgIvnXOyjVBE2IT4HY9pem15KUenN447DcKENNKIqpQ&letvId=172434715 HTTP/1.1" 200 49 "-" "lesports"

关键步骤:

这里我将任务划分成3个阶段来实现:1)如何实现自动定时,自动部署触发;2)如何实现PV和UV的统计;3)结果的存储。

1)Jenkins构建定时任务

首先附上Jenkins的入门教程: Jenkins使用教程  Jenkins定是构建项目
服务器一般都是linux系统,所以可以使用linux下的crontab命令来部署定时任务,到设定时间自动跑脚本等(实际上我也没有用过,以后可以试试)。那我是如何实现定时任务的?答案就是——Jenkins(功能很强大,不过我只懂得皮毛,值得深度学习)。我们公司采用Jenkins工具来构建所有的项目,Jenkins整合了Git、Svn、Maven等主流工具,十分方便。具体使用过程:
1、新建Jenkins项目;
2、进入到设置勾选“Build periodically”;
3、编写“Schedule”,“Schedule”有5项,分别代表:分、时、日、月、年,用空格隔开,如下如“50 02 * * *” 表示每天的凌晨02时50分要自动构建这个项目,其中“*”表示任何时间定。

至此,一个自动化的定时任务就不熟成功了,之后就可以开展真正的工作了。

2)shell脚本处理文本,统计日志

有人会问,我建好Jenkins项目后,设置完定时后该怎么用,上一小节中的教程链接应该多少介绍一些,那我就说说我这里的用处,仅仅是跑脚本而已(应该是大材小用了)。

这就是我这个项目的全部内容了跑了两段脚本分别是Shell脚本和Nodejs脚本。当然Jenkins还支持很多脚本语言的,这里给个截图不赘述。

值得注意是,这里添加脚本的顺序就是脚本的执行顺序。好了该切入主题了,先上代码!
<pre name="code" class="javascript"><pre name="code" class="javascript">set -x
# rm * -rf

ip_list=(<strong>远程服务器的IP地址,用空格隔开</strong>)
for ip in ${ip_list[@]}
do
    scp ${ip}:/home/nginx/nginx/logs/access.log <strong>Jenkins的workspace目录</strong>/${ip}.log
done

log_list=(<strong>拷贝过来的日志文件,文件名为ip地址</strong>)

# 获取当前时间的前一天,切割日志时间是凌晨3点
# 所以处理的日志范围为前一天的02:50:00——今天的02:50:00
today=`date +"%d/%b/%Y" -d "-1days"`
# today="08/Aug/2016"
dateTime=`date +"%Y-%m-%d %H:%M:%S" -d "-1day"`
# 转化成unix时间戳,单位为秒不是毫秒
timestamp=`date -d "${dateTime}" +%s`

# 获取昨天和今天的访问日志
# 即昨天的00:00:00——今天的02:50:00
today=${today//\//\\/}
for file in ${log_list[@]}
do
    sed -n '/'${today}'/,$p' ${file} >> access.log
done

# 提取ip和api
# transform:返回用户访问的时间戳
# 然后与昨天的当前时间比较,保留昨天02:50:00后的日志
awk '
function transform(date){
    day=substr(date,1,2);
    month=substr(date,4,3);
    if(month=="Jan"){
        month=01
    }
    if(month=="Feb"){
        month=02
    }
    if(month=="Mar"){
        month=03
    }
    if(month=="Apr"){
        month=04
    }
    if(month=="May"){
        month=05
    }
    if(month=="Jun"){
        month=06
    }
    if(month=="Jul"){
        month=07
    }
    if(month=="Aug"){
        month=08
    }
    if(month=="Sep"){
        month=09
    }
    if(month=="Oct"){
        month=10
    }
    if(month=="Nov"){
        month=11
    }
    if(month=="Dec"){
        month=12
    }
    year=substr(date,8,4);
    hour=substr(date,13,2);
    min=substr(date,16,2);
    second=substr(date,19,2);
    time=year" "month" "day" "hour" "min" "second;
    return mktime(time)
}
{
    dateTime=substr($4,2);
    split($7,a,"?");
    api=a[1];
    time=transform(dateTime)
}
{
    if (time > '$timestamp')
    print $1,api,time
}
' access.log > temp

awk --posix '
$2 ~ /\/[0-9]{15}\//{
    gsub(/\/[0-9]{15}\//,"/{id}/",$2)
}
$2 ~ /\/[0-9]{15}$/{
    gsub(/\/[0-9]{15}$/,"/{id}",$2)
}
$2 ~ /\/[a-zA-Z0-9]{24}\//{
    gsub(/\/[a-zA-Z0-9]{24}\//,"/{id}/",$2)
}
$2 ~ /\/[a-zA-Z0-9]{24}$/{
    gsub(/\/[a-zA-Z0-9]{24}$/,"/{id}",$2)
}
$2 ~ /\/[a-zA-Z0-9]{10}\//{
    if($2 !~ /\/[a-zA-Z]{10}\//)
    gsub(/\/[a-zA-Z0-9]{10}\//,"/{id}/",$2)
}
$2 ~ /\/[a-zA-Z0-9]{10}$/{
    if($2 !~ /\/[a-zA-Z]{10}$/)
    gsub(/\/[a-zA-Z0-9]{10}$/,"/{id}",$2)
}
{
    print $1,$2,$3
}' temp > ip_api

# 计算pu
cat ip_api | sort -t " " -k2 | awk 'a[$2]!=$1{a[$2]=$1;b[$2]++}END{for (i in b)print i" "b[i]}' | sort -t " " -k1 > uv_

# 计算uv
cut -d" " -f2 ip_api | sort | uniq -c | awk '{print $2,$1}' > pv_

# 合并pv和uv,切过滤掉以js,css,png,jpg,gif,cgi,ico,gch结尾,http开头,包含"\"的API
paste -d " " pv_ uv_ | awk '$0 ~ /^\/api/ && $1 !~ /css$|js$|png$|jpg$|igf$|cgi$|ico$|gch|^http|.*\\.*|400/{print $1,$2,$4}' | sort -t " " -k2 -n -r > result.txt

# # 删除中转文件
rm temp ip_api uv_ pv_ -rf
 
  
 是不是感觉很乱,但我只能说Shell太强大了,以上的代码可以总结以下几个linux关键工具,相关提供链接: 
 
1、sed  Linux之sed用法
4、合并文件paste  Linux paste命令使用PS:awk的版本分很多种,不同的版本支持的正则表达式可能会不一样,gawk就不支持/[0-9]{15}/的匹配,这时需要在awk 命令后 加上--posix,这貌似是某个标准,不过还是不太了解,以后有机会在研究一下。
这里再推荐一个linux命令行学习网站 菜鸟教程

3)Nodejs实现连接数据库,保存结果

首先还是上代码;
var fs = require('fs');
var MongoClient = require('mongodb').MongoClient;

//创建ApiLog数组
var apiLogs = new Array();
//获取当前时间戳
var timestamp = new Date().getTime() - 18000000;
//日志文件名
var filename = 'result.txt';
var data = fs.readFileSync(filename).toString();
var lines = data.split('\n');
for (var i = 0; i < lines.length; i++){
    var line = lines[i];
    if(line != null && line != ''){
        var api = lines[i].split(' ');
        console.log(api);
        //封装成Json对象
        var url = api[0];
        var pv = api[1];
        var uv = api[2];
        apiLogs.push({"url":url, "pv": pv, "uv":uv, "visitTime":timestamp});
    }
}
var DB_CONN_STR = 'mongodb://用户名:密码@IP地址:端口号/数据库名';
var selectData = function(db, apiLogs, callback) {
    //连接到表  
    var collection = db.collection("apiLog");
    collection.insert(apiLogs, function(err, result) {
        if(err){
            console.log('Error:'+ err);
            return;
        }
        callback(result);
    });
}

MongoClient.connect(DB_CONN_STR, apiLogs, function(err, db) {
    console.log("连接成功!");
    selectData(db, apiLogs, function(result) {
        // console.log(result.length);
        db.close();
    });
});
上面的代码相对简单一下,大致的过程就是:1)读取shell脚本处理后的结果文件(result.txt),因为处理过后的文件不大所以一次性读取,之后判断换行遍历每行内容;2)切割每行内容分成3个字段:url、PV、UV,组合成json对象,然后在放到一个数组变量中;3)连接mongo数据库,把数组变量批量插入数据库中。这个过程需要注意以下几点:
1)nodejs是异步的,所以效率高,但是程序很多时候需要同步处理所以有时需要异步转化成同步,这个网上也有很多教程,这里不赘述。 我想说的是在读取文件的时候一定要同步读取,如果不是同步读取的话,那你最后得到的数组变量会为空,等同于你没有处理文本(实际上处理了,但是在连接数据库时还没有处理完)。
2)在nodejs代码的最上面可以看到require了两个模块:fs和mongodb,fs是文件系统模块,nodejs本身自带的。而mongodb模块是第三方模块,需要安装,安装命令如下:
npm install mongodb -g//npm是nodejs的一个模块管理工具,可以直接从网上下载模块,-g 是可选项表示是否全局,即在当前计算机环境中的任何位置都可以引用。有人会问我在本地写代码然后拷贝到Jenkins上,但是我没有权限进入服务器怎么安装办?答案就是先保存Jenkins上shell脚本(处理日志的代码),然后清空换上 npm install mongodb -g,然后构建一下项目就可以安装,之后再把shell改回去就行了。
PS:附上Nodejs的入门教程。 Node.js 教程

猜你喜欢

转载自blog.csdn.net/u011170921/article/details/52165129
今日推荐