使用Nginx访问日志统计PV和UV
一个网站当用户量增大时候,不可避免有统计pv和uv的需求。
- UV(Unique Visitor):独立访客,以cookie为依据区分不同访客,UV计算一天之内(00:00-24:00),访问网站的访客数量。
- PV(Page View):页面访问量,同一个用户对页面多次访问累计。
本文介绍一种通过分析nginx日志统计pv、uv的方法。
一、方案设计
如何根据Nginx的访问日志统计pv和uv呢?
我们可以通过分析nginx的访问网站页面的日志来统计参数,比如一个单页应用的博客网站,用户访问/
、/article_list
、/article_detail
都应该算作一次访问。
但是如果网站的路由不确定时候,就不好统计。当路由变化时候,需要更新统计脚本。而且,用户首次访问后才设置了cookie,所以首次页面请求是不带cookie的,这会导致漏报。另外,用cookie记录数据,由于是js写的cookie,所以需要保证同域访问,这就很不灵活。如果不是js写的cookie,那就说明依赖后端服务,也不够灵活。
所以我们采取的方法是前端上报页面访问事件。
首先前端生成一个uuid,向Nginx发起一个请求并携带uuid,Nginx会精确匹配这个请求,然后返回204,以减小数据传输量。
由于上报地址和页面是同域的,因此我们这里使用cookie保存uuid,如果不同域,还可以使用localStorage将uuid存在本地,然后在参数中将uuid带上。
Nginx收到上报后,根据我们指定的固定格式生成日志。我们还要设置定时任务,定期切割日志,以便分析日志时候以月和天为维度统计指标。
整体流程示意图如下:
【前端面试刷题网站:灵题库,收集大厂面试真题,相关知识点详细解析。】
二、上报访问事件
前端使用uuid
这个库生成uuid,使用js-cookie
对cookie进行读写,cookie有效期设置为30天,如果已经存在则不设置。
这里上报地址是“/report.gif”。为了避免上报请求被缓存,请求参数加一个时间戳。
// index.js
import Cookies from 'js-cookie';
import {v4 as uuidv4} from 'uuid';try {if (!Cookies.get('uuid')) {Cookies.set('uuid', uuidv4().replace(/-/g, ''), {expires: 30});}// 上报访问axios.get(`https://www.example.com/report.gif?t=${Date.now()}`);
}
catch (e) {}
Nginx需要配置响应
location =/report.gif {return 204;
}
三、Nginx配置日志格式
我们可以指定Nginx访问日志的格式,分析日志时候更方便。
注意,log_format指令只能用在http模块中,不能用在server模块中。
这里在http模块中通过log_format定义了一个格式,命名为main,然后在server模块中使用access_log定义访问日志的存放目录,并且引用main指定日志格式。server模块中还匹配了请求里面的cookie,取出uuid赋值给$uuid
变量以便写日志时候能够正常读取uuid。
http {log_format main '$remote_addr - [$time_local] "$request" '' - $status "uuid:$uuid" ';server {access_log /path/to/log/access443.log main;if ( $http_cookie ~* "uuid=([A-Z0-9]*)"){set $uuid $1;}}
}
我们会得到这样的日志
101.241.91.99 - [04/May/2022:09:36:34 +0800] "GET /assets/vendor.337922eb.js HTTP/1.1" - 304 "uuid:a27050e998864af89de0fbc7605d1548"
101.241.91.99 - [04/May/2022:09:36:34 +0800] "GET /assets/style.81f77c22.css HTTP/1.1" - 200 "uuid:a27050e998864af89de0fbc7605d1548"
101.241.91.99 - [04/May/2022:09:36:34 +0800] "GET /assets/index.9c0fae7c.js HTTP/1.1" - 304 "uuid:a27050e998864af89de0fbc7605d1548"
101.241.91.99 - [04/May/2022:09:36:34 +0800] "GET /assets/quiz.5e3bb724.js HTTP/1.1" - 304 "uuid:a27050e998864af89de0fbc7605d1548"
101.241.91.99 - [04/May/2022:09:36:34 +0800] "GET /report.gif?id=0&t=1651628194189 HTTP/1.1" - 204 "uuid:a27050e998864af89de0fbc7605d1548"
101.241.91.99 - [04/May/2022:09:36:34 +0800] "GET /assets/logo.c5f2dde3.jpeg HTTP/1.1" - 200 "uuid:a27050e998864af89de0fbc7605d1548"
101.241.91.99 - [04/May/2022:09:36:34 +0800] "GET /favicon.ico HTTP/1.1" - 200 "uuid:a27050e998864af89de0fbc7605d1548"
四、日志切割
为了方便统计我们希望把日志文件按时间分割,分割成这样的结构:
├── 2022
│ └── 05
│ └── 03.log
按照年、月、日分层,每天生成一个日志。
实现思路是,先建立一个日志存放目录,每天的凌晨0点1分,将前一天的日志按照日期移动到日志目录中。然后再重新创建一个日志文件供Nginx写入。
先写一个脚本实现这个功能
log_split.sh
#!/bin/bash
# 定位到脚本所在目录(注意我这里也是Nginx写访问日志的目录,当然这不是必须的)
log_base=$(cd `dirname $0`; pwd)
# 根据前一天的时间生成日志所在目录名
log_path=${log_base}/$(date -d yesterday +%Y)/$(date -d yesterday +%m)
# 创建日志目录
mkdir -p $log_path
# 将当前Nginx的日志移动到指定存放目录
mv $log_base/access443.log $log_path/$(date -d yesterday +%d).log
# 重新创建日志文件,给Nginx写日志用
touch $log_base/access443.log
# 给Nginx发送信号,注意你的Nginx目录可能不同
kill -USR1 `cat /www/server/nginx/logs/nginx.pid`
值得注意的是,虽然移动完日志,并且重新创建,但是Nginx的文件引用还是移走的那个,所以最后要给Nginx发送信号,让它写到新的日志文件中。
脚本写完,我们还要定时(每天0点1分执行切割任务),这用到了Linux的crontab
工具。
首先在控制台输入crontab -e
打开编辑界面。然后输入1 0 * * * sh /path/to/log/log_split.sh
。这个定时任务的意思是每天0点1分执行日志分割脚本,编辑完成后保存关闭,定时任务就生效了。
我们还可以通过crontab -l
查看当前的定时任务;通过crontab -r
移除当前的定时任务。
五、Nodejs脚本分析日志,统计PV、UV
有了日志,就很容易分析PV、UV。我们可以使用Linux命令分析,但我这次选择用Nodejs脚本来统计,原因是对JS更熟悉,另外相对Linux也更灵活。
分析的大概思路是根据每天的访问日志,过滤出report.gif
这个上报请求,上报次数就是PV,然后根据uuid去重,得到UV。
统计脚本如下:
// stats.js
const fs = require('fs');
const path = require('path');const args = process.argv.slice(2);const [year] = args;// 打印统计结果
function echo() {yearDir = year || '2022';const stats = statsYearLog(yearDir);Object.entries(stats).sort(([a], [b]) => a - b).forEach(([month, dateStats]) => {console.log(`${month}月`);Object.entries(dateStats).sort(([a], [b]) => a - b).forEach(([date, {pv, uv}]) => {console.log(' ', `${date}日`, `pv: ${pv}`, `uv: ${uv}`);});console.log('\n');});
}// 统计某一年的数据
function statsYearLog(year) {// 读取目录下的文件夹名字const dir = path.resolve(__dirname, year);const monthDirList = fs.readdirSync(dir);const logMap = monthDirList.reduce((result, monthDir) => {const monthStats = statsMonthLog(year, monthDir);result[monthDir] = monthStats;return result;}, {});return logMap;
}// 统计每个月的数据
function statsMonthLog(year, month) {const dir = path.resolve(__dirname, year, month);const dateLogList = fs.readdirSync(dir);const monthLogMap = dateLogList.reduce((result, dateLogFileName) => {const dateStats = statsDateLog(year, month, dateLogFileName);result[dateLogFileName.replace('.log', '')] = dateStats;return result;}, {});return monthLogMap;
}// 统计某天的数据
function statsDateLog(year, month, dateFile) {const logPath = path.resolve(__dirname, year, month, dateFile);const logText = fs.readFileSync(logPath, 'utf-8');const logList = logText.split('\n');const pvLogList = logList.filter((line) => {return /report.gif/.test(line)});const uvLogMap = pvLogList.reduce((result, line) => {const match = line.match(/uuid:(\S+)"/);if (match && match[1]) {result[match[1]] = 1;}return result;}, {});return {pv: pvLogList.length, uv: Object.keys(uvLogMap).length};
}// 执行打印统计结果
echo();
执行统计脚本node stats.js 2022
打印结果
05月03日 pv: 1 uv: 1
六、展望
后续可以考虑扩展现有能力,让Node实现日志切割的功能,并提供api和界面,可以可视化统计PV、UV。
使用Nginx访问日志统计PV和UV相关推荐
- nginx日志统计pv、uv命令
nginx 的access.log日志中统计访问量 统计pv 排除掉静态文件访问 grep "https://www.baidu.com/" access.log | grep - ...
- 通过MapReduce统计PV和UV
通过MapReduce统计PV和UV 一.pv(page view,页面浏览量) PV(page view),即页面浏览量:用户每1次对网站中的每个网页访问均被记录1次.用户对同一页面的多次访问,访问 ...
- nginx 访问日志分析工具 goacess
2019独角兽企业重金招聘Python工程师标准>>> 20150702 nginx 访问日志分析 goacess 1.安装GoAccess需要一些系统支持库 yum install ...
- Nginx访问日志分析
1.首先说明Nginx日志存放在系统的哪个位置,可以使用下列命令: 找到*/nginx/logs/access.log这个关键路径,就是Nginx的访问日志的位置.(其中*代表你电脑nginx文件夹前 ...
- Nginx访问日志、日志切割、静态文件不记录日志和过期时间
2019独角兽企业重金招聘Python工程师标准>>> 11月27日任务 12.10 Nginx访问日志 12.11 Nginx日志切割 12.12 静态文件不记录日志和过期时间 1 ...
- 12.10 Nginx访问日志 12.11 Nginx日志切割 12.12 静态文件不记录日志和过期时间
- 12.10 Nginx访问日志 - 12.11 Nginx日志切割 - 12.12 静态文件不记录日志和过期时间# 12.10 Nginx访问日志 - 日志的格式- vim /usr/local/ ...
- Nginx 访问日志轮询切割
Nginx 访问日志轮询切割脚本 -------------------------------------------------------------------- 注:如果你对python感兴 ...
- 6月8日任务(12.10 Nginx访问日志 12.11 Nginx日志切割 12.12 静态文件)
课程名称:12.10 Nginx访问日志 笔记内容: [root@localhost ~]# vim /usr/local/nginx/conf/nginx.conf 定义日志格式 combined_ ...
- NGINX访问日志和错误日志
Logs are very useful to monitor activities of any application apart from providing you with valuable ...
最新文章
- 入职大厂,我容易吗?
- 大数据及Linux安装
- 在ASP.NET中UrlRewrite的实现(能隐藏扩展名)之一
- html5的网络书店图书网站代码_【技能提升】10个编写HTML5的实用小技巧
- Lenovo E47A Ubuntu闪屏解决办法
- Qt之tcp的简单使用
- SQL Server 数据库之连接查询
- 微软笔试题-c语言-算法分析
- vue生命周期的快速记忆方法
- dedecms教程:单页制作教程
- 玩转华为ENSP模拟器系列 | 配置IS-IS负载分担示例
- 趣图:多线程的残酷真相(柯基版)
- 2018年度区块链安全报告
- 随机漫步的傻瓜:发现市场和人生中的隐藏机遇
- php x509certificate,ssl - .NET中的X509Certificate2和X509Certificate有什么区别?
- 智能仓库管理系统方案(一)
- 2021年中国FPS游戏市场趋势报告、技术动态创新及2027年市场预测
- 【Visual c++】+【EasyX】游戏组件1 移动的小人
- 【智能优化算法-灰狼算法】基于贪婪非分级灰狼优化器求解单目标优化问题附matlab代码
- 洛谷 P1726 上白泽慧音
热门文章
- 微信小程序 评论功能实现
- 深入理解计算机系统家庭作业第四章(4.43-4.54)
- 烽火2640路由器命令行手册-04-网络协议配置命令
- 计算机主板结构分为at,2.2.1 计算机主板结构(1)
- 做个grub的U盘启动盘,即将grub安装到U盘上面。
- Sketch 快捷键
- 启动tomcat时候报错(Error deploying web application directory)
- 天语3com.android.phon,万能遥控(com.duokan.phone.remotecontroller) - 6.0.6 - 应用 - 酷安
- 计算机学院运动会解说词,学校运动会解说词30篇
- SV学习(8)——随机约束和分布、约束块控制