通过分析Nginx服务器日志统计用户流量并预警

作者:友人A,立志于用最简单代码实现最复杂的业务逻辑,不断学习努力中。

看一个月的nginx日志 nginx 日志统计_看一个月的nginx日志

01


概述

业务背景

Nginx日志对于统计、系统服务排错很有用。Nginx日志主要分为两种:access_log(访问日志)和error_log(错误日志)。通过访问日志我们可以得到用户的IP地址、浏览器的信息,请求的处理时间等信息。错误日志记录了访问出错的信息,可以帮助我们定位错误的原因。

Nginx日志介绍

日志格式

log_format combined '$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"';

    如你所见,log_format配置指令的默认值就是这个名为"combined"的日志格式,这个日志格式中记录了上述变量,那么这些变量分别代表了什么意思呢?我们来总结一下(此处先大致了解一下,不用纠结细节):

$remote_addr变量:记录了客户端的IP地址(普通情况下)。
$remote_user变量:当nginx开启了用户认证功能后,此变量记录了客户端使用了哪个用户进行了认证。
$time_local变量:记录了当前日志条目的时间。
$request变量:记录了当前http请求的方法、url和http协议版本。
$status变量:记录了当前http请求的响应状态,即响应的状态码,比如200、404等响应码,都记录在此变量中。
$body_bytes_sent变量:记录了nginx响应客户端请求时,发送到客户端的字节数,不包含响应头的大小。
$http_referer变量:记录了当前请求是从哪个页面过来的,比如你点了A页面中的超链接才产生了这个请求,那么此变量中就记录了A页面的url。
$http_user_agent变量:记录了客户端的软件信息,比如,浏览器的名称和版本号。

日志打印

203.209.240.10 - - [14/Oct/2020:16:00:19 +0800] "POST /XXServlet HTTP/1.1" 200 4782 "-" "Jakarta Commons-HttpClient/3.0"

    这些变量并非一定会有对应的值,如果变量没有对应的值,那么日志中会使用 "-" 作为默认值进行占位。

0

2

流量统计

前言

之前有同事写过用Python脚本分析access_log日志统计用户流量。由于服务器不能连接外网环境,手动安装Python依赖包的过程过于繁琐,而且Linux系统版本过于老旧,导致Python脚本运行时不断报错。最终此方案只能被我放弃。于是我开始寻求最原始的解决办法,使用shell脚本去分析Nginx的access_log日志。

先上脚本:

#!/bin/bash
#将数字日期格式化成英文
function mothToEnglish(){
monthStr=""
j=0
month=$1
for i in Jan Feb Mar Apr May June July Aug Sep Oct Nov Dec
    do
        let j+=1;
        if [[ $month = $j ]];then
            monthStr=$i
        else
            continue
        fi
    done
}
#获取的数据时间间隔(分钟)
frequency=5
#获取本机的IP地址(分钟)
ips=`ip address |sed -rn '/state UP/{n;n;s#^ *inet (.*)/.*$#\1#gp}'`
#数据发送地址
url=http://baidu.com
#切割日志的时间处理Start
ihour=$(date "+%H")
imin=$(date "+%M")
isec=$(date "+%S")
iyear=$(date "+%Y")
imonth=$(date "+%m")
iday=$(date "+%d")
ayear=$(date -d "${frequency} minute ago" "+%Y")
amonth=$(date -d "${frequency} minute ago" "+%m")
aday=$(date -d "${frequency} minute ago" "+%d")
ahour=$(date -d "${frequency} minute ago" "+%H")
amin=$(date -d "${frequency} minute ago" "+%M")
asec=$(date -d "${frequency} minute ago" "+%S")
#调用函数将日期转化
mothToEnglish $amonth
amonthStr=$monthStr
mothToEnglish $imonth
imonthStr=$monthStr
#生成切割的开始时间和结束时间
logStartStr=${aday}"/"${amonthStr}"/"${ayear}":"${ahour}":"${amin}":"${asec}
logStartEnd=${iday}"/"${imonthStr}"/"${iyear}":"${ihour}":"${imin}":"${isec}
#logStartStr=13/Oct/2020:09:42:41
#logStartEnd=13/Oct/2020:09:49:41
#切割日志的时间处理End
#获取指定日期内的日志并生成文件
cat access.log | awk '$4>="['$logStartStr'" && $4 <="['$logStartEnd'"' >access_tmp.log
#将接口的数据截出来
cat access_tmp.log|grep xxServlet | awk '{print $4}' | awk 'sub("\\[","",$0)'|uniq -c |sort -n -k 4 -r -o access_tmp_result.log
#判断文件是否存在并不为空
if test -s access_tmp_result.log; then
#将本机的IP地址追加到文件中
ipsStr="|"$ips
sed -i 's/$/'$ipsStr';/' access_tmp_result.log
#将数据文件发送到后台系统
curl -X PUT -d @access_tmp_result.log $url
fi
#删除临时文件
rm -rf access_tmp.log
rm -rf access_tmp_result.log


    作用: 统计指定接口5分钟之内的每秒的用户访问流量,并将数据发送到后台系统。

切割数据

首先我们要做的第一步将一定时间的段的数据截出来。这时我们就需要两个参数,切割日志的开始时间和结束时间。

    我的Nginx 用$(date +%Y%b%d)获取到的当前时间的格式为202010月25,并不符合我们要的格式,没关系,我们写个函数mothToEnglish将这个时间转换一下。

   截取指定时间段日志

cat access.log | awk '$4>="['$logStartStr'" && $4 <="['$logStartEnd'"' >access_tmp.log

这样我们切割的开始时间和结束时间就获取到了,定义两个变量,将他传入上面的命令中,这样我们就获取到了一个5分钟之内所有日志的文件access_tmp.log。

    刚才的截取命令中用到了一个很关键的命令awk,下面我们就简单介绍一下这个命令。

awk简介

awk是一个强大的文本分析工具,与grep(查找)、sed(编辑)一并称为“文本处理三剑客”。awk最强大的功能是对数据分析并生成报告。

工作原理

    一次读取一行文本,按输入分隔符进行切片,切成多个组成部分,将每片直接保存在内建的变量中,$1,$2,$3….,引用指定的变量,可以显示指定断,或者多个断。如果需要显示全部的,需要使用$0来引用。可以对单个片断进行判断,也可以对所有断进行循环判断。其默认分隔符为空格。

语法格式

awk [options] ‘program’ FILE……
[options]–F:指明输入时的分隔符

字符串处理

sub(r,s,[t]) :对t字符串进行搜索r 表示的模式匹配的内容,并将第一个匹配的内容替换为s

printf命令:格式化输出

print 会在每个输出之后自动加入一个换行符;而 printf 是标准格式输出命令,并不会自动加入换行符,如果需要换行,则需要手工加入换行符。在 awk 中可以识别 print 输出动作和 printf 输出动作,但是在 Bash 中只能识别标准格式化输出命令 printf。printf 命令格式如下:
 printf '输出类型输出格式' 输出内容

    获取指定接口的数据,并根据时间进行分组,统计数量,并生成临时文件。

cat access_tmps.log|grep xxServlet | awk '{print $4}' | awk 'sub("\\[","",$0)'|uniq -c |sort -n -k 4 -r -o acc_tmps2.log

    执行完命令,我们得到的结果是:

4 20/Oct/2020-20:48:00
      4 20/Oct/2020-20:47:53
      4 20/Oct/2020-20:47:39
      3 20/Oct/2020-20:48:51
      3 20/Oct/2020-20:48:48
      3 20/Oct/2020-20:48:43
      3 20/Oct/2020-20:48:41

这样每秒产生的日志数量,输出在了第一排,以从高到低的,方式排列。这个方法也可以统计访问最多的IP地址,更改对应参数即可。

下面再来介绍一下uniq 和sort的用法。

用法:uniq [选项]... [文件]

从输入文件或者标准输入中筛选相邻的匹配行并写入到输出文件或标准输出。

不附加任何选项时匹配行将在首次出现处被合并。

长选项必须使用的参数对于短选项时也是必需使用的。       

简写

全名称

说明

 -c,

--count 

在每行前加上表示相应行目出现次数的前缀编号

用法:sort [选项]... [文件]...

串联排序所有指定文件并将结果写到标准输出。

排序选项:     

简写

全名称

说明

-n

--numeric-sort

根据字符串数值比较

-r

--reverse

逆序输出排序结果

其他选项:

-k,

--key

位置1[,位置2] 在位置1 开始一个key,在位置2 终止(默认为行尾)

-t,

--field-separator=分隔符

使用指定的分隔符代替非空格到空格的转换

-o,

-o, 将排序后的结果存入指定的文件。

    由于生产环境是两台nginx服务器,现在我们需要知道是哪台服务的日志,所以我们需要服务器地址。

获取本机IP

curl -X PUT -d @access_tmp_result.log http://baidu.com

追加到文件末位

ipsStr=“|”:$ips
sed -i 's/$/'$ipsStr';/' access_tmp_result.log

   最终我们得到了要发送给服务器的数据文件access_tmp_result.log。

数据的传输

curl命令

    linux curl是一个利用URL规则在命令行下工作的文件传输工具。它支持文件的上传和下载,所以是综合传输工具,但按传统,习惯称url为下载工具。

简写

含义

说明

-X,

--request [GET|POST|PUT|DELETE|…]

使用指定的 http method 例如 -X POST

-H

--header

设定 request里的header 例如 -H "Content-Type: application/json"

-d

--data

设定 http body 默认使用 content-type application/x-www-form-urlencoded (H)

curl -X PUT -d @access_tmp_result.log http://baidu.com

我们通过curl命令将文件中的数据发送到后台接口,后台接口通过spilt函数,切割指定字符串,就能获取到我们的数据,存库就好了。

    最后将创建的临时文件删除。整个sh脚本完工。

rm -rf access_tmp.log
rm -rf access_tmp_result.log

定时任务

最后我们通过设置linux的crontab定时任务去执行这个脚本。

看一个月的nginx日志 nginx 日志统计_看一个月的nginx日志_02

          */5    *  *  *  *   /weblogic/openresty/openresty/nginx/logs/test.sh    

    每隔5分钟执行一次test.sh脚本

03


后台处理

后台java代码就比较简单了,从request作用域中获取字符流。根据我们定义的分隔符;split取出每一条数据。For循环去遍历这些数据。再根据分隔””和“|”取出访问流量,访问时间和服务器地址。我们获取最大的访问量与数据库中定义的报警预设值进行比较,超过预设值则调用企业微信通知接口,发送消息给指定人员进行报警。其他数据存入数据库,用作记录。

我们再来看一看执行效率,我们生产环境的access日志,每天都进行切割,一天打印的日志文件大概50MB左右,以5分钟的时间间隔切分日志,大概1s左右完成,发送数据到后台入库完成大约3-4秒。

尝试过切1天的日志,进行统计入库,大约4分钟左右完成。主要耗时在数据入库,执行效率还可以。

04


流量统计收到的效果

统计流量能使我们清晰的认识到系统接口每天的访问情况,对于了解服务器压力情况,以及为后续针对接口的限流操作,提供数据支持。

流量预警主要是对生产环境服务器访问流量有一个即时的监控,当流量发生突然变化,我们可以及时感知到,并作出相应的处理。

我们后来还实现了请求失败数量的预警,当请求不能以200(成功)的状态返回时,我们统计这一部分数据,也是超过临界值报警提示。

05

总结

1. linux的Awk命令很强大,文中只是针对用到的方法进行了说明。更多用法可以参考:。

2. Nginx的日志参数timelocal打印的时间是nginx处理完成打印日志的时间,不是请求发出的时间。用这个时间进行统计,只能反映出一段时间的流量,并不能统计出接口每秒的并发数。

历史文章

解决方案:

加密与认证技术

【双十一钜惠:科技人的专属保障】移动端交互速度保证  ——影像上传优化

【双十一钜惠:科技人的专属保障】移动端认证保障  ——非对称加密及jwt应用

系统安全建设小经验

公共组件-需求自动录入TAPD

Python初体验

HTML5实现自定义键盘及使用

Sentinel限流

基于公有云实时音视频通道的构建音视频组件方法与实战经验

分布式定时任务调度探讨

阿里云OSS JavaAPI分享

Jmeter压测的使用

API接口安全优化浅谈

基于idea将传统应用maven化改造实践

安全漏洞预扫小工具

Nacos作为注册中心,配置中心部署方案

Rancher+K8S简化学习环境搭建方案

Excel大数据量解析优化方案

Sharding-JDBC个性化分表方案

基于JWT的接口权限认证

Spring Cloud Gateway实现限流

公网发布的通用技术解决方案

Nginx结合Lua实现限流

Mysql数据同步Canal方案

Weblogic部署基于Spring Cloud项目应用实例

智能验证码解析java语言实现

数据库的演绎与变迁-下架最后一台小型机

关于struts架构通过引入spring包实现dubbo调用测试的未来式

架构系列:

双十一钜惠:科技人的专属保障】如何保障移动端活动的稳定运转?——移动端基础平台建设的经验分享

【双十一钜惠:一个有温度的技术公众号】科技人需要什么样的保障

架构师入门系列:架构三板斧“缓存,消息,异步”

监控&运维

redis监控工具-redislive

微服务链路追踪工具 Apache  SkyWalking部署与实践

快速发现生产环境SQL问题

Grafana-业务数据监控实践

自动化运维能力提升

服务器监控及问题定位排除

基于Spring Boot Admin告警之应用状态告警

Ansible在自动化运维中的使用

自助迁发分支工具

大数据

基于CDH集群的大数据项目的优化总结

Oracle实时同步大数据平台解决方案

实时流关联维表解决方案

中间件

浅谈Elasticsearch的资源分配

Apache Kafka使用规范Elasticsearch安装及使用

SSDB介绍及使用

RabbitMq使用简单介绍

Spring Cloud常用组件介绍

前端

ESLint + Prettier 规范前端代码

Vue.js对WinXP系统对兼容性

关于采集操作系统、浏览器版本的设计方案及一些浏览器兼容性问题处理

安卓动态加载SO文件

对浏览器的理解及兼容移动端H5本地化方案调研和实践

自适应网页设计

短信/浏览器唤起APP方案

基础知识

Sonar简介

JVM基础系列1:JVM内存模型介绍

JVM基础系列2:JVM配置参数和Java内存模型

JVM基础系列3  虚拟机字节码执行引擎

开发规范

Java异常处理规范

战略方向

金融保险系统开源浪潮下机遇与挑战

项目实战

“用户思维+中台架构”快速搭建金融宝app

多维分保的应用探索

用文化指引自身的成长之路  -2020年技术成长心得分享

从工作中探寻自身的成长之路

从《平凡的荣耀》到记一次兼容性改造优化后的技术感悟

客户为中心,统筹协调,把控风险

让我们的管理看的见



看一个月的nginx日志 nginx 日志统计_看一个月的nginx日志_03