当前位置:网站首页>04. 项目博客之日志
04. 项目博客之日志
2022-07-06 05:17:00 【John是橘红】
项目博客之日志
系统没有日志,就等于人没有眼睛
第一,访问日志 access log(server 端最重要的日志)
第二,自定义日志(包括自定义事件、错误记录等)
1. nodejs 文件操作
文件读取
基本的文件读取方法:
const fs = require("fs");
const path = require("path");
// 获取当前文件夹的 data.txt 所在路径
const filePath = path.resolve(__dirname, "data.txt");
// 读取文件内容
fs.readFile(filePath, (err, data) => {
if (err) {
console.error(err);
return;
}
// data 是二进制类型,需要转换为字符串
console.log(data.toString());
});
一口气读取了文件里的内容然后进行输出。这就带来了风险,如果要读取的这个文件特别大,内存吃不消。
文件写入
基本的文件写入方法:
// 写入文件
const content = "这是新写入的内容\n";
const opt = {
flag: "a",
};
// writeFile 四个参数:
// filePath: 目标文件路径 content: 要写入的内容
// opt: 写入方式 'a' 代表追加写入,覆盖用 'w'
// 最后一个参数是回调函数
fs.writeFile(filePath, content, opt, (err) => {
if (err) {
console.error(err);
}
});
每次写入的时候都得获取文件路径然后打开进行写入,这样就十分耗内存。另外,如果要写入一个特别大的内容,整个内容要存入内存里,内存吃不消。
判断文件是否存在
// 同步检查是否存在
console.log(fs.existsSync(filePath)); // true
// 异步检查是否存在
fs.exists(filePath, (exist) => {
console.log(exist);
});
2. stream
2.1 IO 操作的性能瓶颈
- IO 包括”网络 IO“和”文件 IO“
- 相比于 CPU 计算和内存读写,IO 的突出特点就是慢
- 如何在有限的硬件资源下提高 IO 操作效率
2.2 stream 通俗介绍
stream 就是流动,之前一口气读取文件内容出来就好比搬水桶,直接把整个水桶抬走了。但是大多数人并没有那力气。更好的方式是,接个水管,将水稳定地通过管道转移到其他地方,降低了成本(小孩子都能够完成)。stream 就类似于此,极大降低了硬件资源要求。
2.1 stream 演示
标准输入输出
process.stdin.pipe(process.stdout);
终端输入的内容将通过管道持续输出到终端中。
请求内容输出到响应内容
const http = require('http')
const server = http.createServer((req, res) => {
if (req.method === 'POST') {
req.pipe(res) // 最主要
}
})
server.listen(8000)
现在,req 和 res 通过 pipe 连接,一旦 req 接收到了东西,就会立刻稳定地输出到 res 中。
stream 拷贝文件
通过 fs.createReadStream
创建一个读取文件的 stream 对象,fs.createWriteStream
创建一个写入文件的 stream 对象。通过 pipe 连接使得数据流动。
// 两个文件路径
const fileName1 = path.resolve(__dirname, 'data.txt')
const fileName2 = path.resolve(__dirname, 'data-bak.txt')
// 读取文件的 stream 对象
const readStream = fs.createReadStream(fileName1)
// 写入文件的 stream 对象
const writeStream = fs.createWriteStream(fileName2)
// 通过 pipe 执行拷贝
readStream.pipe(writeStream)
// 可以监听数据传输时的内容
readStream.on('data', chunk => {
console.log(chunk.toString())
})
// 监听到读取完成后,执行回调
readStream.on('end', () => {
console.log('copy done')
})
stream 读取文件内容
发起一个 get 请求来读取文件,只要读取文件流通过管道输出到 response 里即可。
const http = require('http')
const fs = require('fs')
const path = require('path')
const fileName1 = path.resolve(__dirname, 'data.txt')
const server = http.createServer((req, res) => {
if (req.method === 'GET') {
const readStream = fs.createReadStream(fileName1)
readStream.pipe(res)
}
})
server.listen(8000)
3. 写日志
3.1 创建写日志的相关方法
在项目里创建 logs 文件夹,里边存储日志文件。同时在 src 目录创建 utils 用于放常用的工具方法,里边创建 log.js,里面写了写日志相关方法。
const fs = require("fs");
const path = require("path");
// 写日志
const writeLog = (writeStream, log) => {
// 写入信息并换行
writeStream.write(log + "\n");
};
// 生成 writeStream
const createWriteStream = (fileName) => {
const fullFileName = path.resolve(__dirname, "../", "../", "logs", fileName);
const writeStream = fs.createWriteStream(fullFileName, {
// 追加写入
flags: "a",
});
return writeStream;
};
// 写访问日志,error log 和 event log 同理
const accessWriteStream = createWriteStream("access.log");
const access = (log) => {
writeLog(accessWriteStream, log);
};
module.exports = {
access,
};
3.2 每次接收到请求后写入日志
app.js 里的 serverHandle 每次调用的时候便是接收到请求的时候,此时写入访问日志即可。其他的日志,例如 error 和 event 在合适的时候写入即可。
const serverHandle = async (req, res) => {
// 记录访问信息
access(
`${
req.method} -- ${
req.url} -- ${
req.headers["user-agent"] } -- ${
Date.now()}`
);
......其他主要代码
}
实现效果,访问的时候成功写入了 log 文件:
4. 日志拆分
- 日志内容会慢慢积累,放在一个文件中不好处理
- 按时间划分日志文件,方便定位
- 实现方式:Linux 的 crontab 命令,即定时任务
crontab
- 设置定时任务格式:
(分钟) (小时) (日期) (月份) (星期) command
,不需要填的用星号 * 代替 - 当时间到了,将 access.log 拷贝并重命名为当前时间的 log,如
- 清空 access.log 文件,继续积累日志
4.1 实现方法
以下流程适用于 Linux 和 macOS。
写 sh 脚本
写一个脚本 copy.sh 用于产生具有日期标注的日志,命令行执行 sh coppy.sh
查看效果:
#!/bin/sh
cd logs文件夹的绝对路径
# 复制文件
cp access.log $(date +%Y-%m-%d).access.log
# 清空 access.log
echo "" > access.log
理想情况是,生成新的带有日期的日志文件,并且 access.log 里的内容被清空。
使用 crontab
在命令行中执行 crontab -e
进入编辑模式,写入以下内容,意思为每天的第 0 小时执行 sh 命令。
* 0 * * * sh copy.sh的绝对路径
查看 crontab 任务
crontab -l
可以查看当前系统的定时任务。
5. 日志分析
- 如针对 access.log 日志,分析 Chrome 的占比
- 日志是按行存储的,一行就是一条日志
- 使用 nodejs 的 readline 一行一行读取(基于 stream,效率高)
比如,如下的日志内容,前四个是 Safari 浏览器跑的,后两个是 Chrome 浏览器。
GET -- /api/blog/detail?id=1 -- Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.5 Safari/605.1.15 -- 1655271842104
GET -- /favicon.ico -- Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.5 Safari/605.1.15 -- 1655271843024
GET -- /api/blog/detail?id=1 -- Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.5 Safari/605.1.15 -- 1655271853955
GET -- /api/blog/detail?id=2 -- Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.5 Safari/605.1.15 -- 1655271854350
GET -- /api/blog/detail?id=1 -- Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.61 Safari/537.36 -- 1655271857708
GET -- /api/blog/detail?id=1 -- Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.61 Safari/537.36 -- 1655271859548
可以看出,Safari 浏览器发送请求时,里面没有 Chrome 标记。就可以依据这个来判别 Chrome 的占比(当然比较不严谨,演示用)
计算 Chrome 占比的脚本如下:
// utils/readline.js
const fs = require("fs");
const path = require("path");
const readline = require("readline");
// 文件路径
const fullFileName = path.resolve(
__dirname,
"../",
"../",
"logs",
"access.log"
);
// 创建 readStream
const readStream = fs.createReadStream(fullFileName);
// 创建 readline 对象
const rl = readline.createInterface({
input: readStream,
});
let chromeNum = 0;
let sum = 0;
// 逐行读取
rl.on("line", (lineData) => {
if (!lineData) {
return;
}
// 记录总行数
sum++;
const [method, url, userAgent] = lineData.split(" -- ");
if (userAgent?.includes("Chrome")) {
// 累加 Chrome 数量
chromeNum++;
}
});
// 监听读取完成
rl.on("close", () => {
console.log(`Chrome 占比:${
chromeNum / sum}`);
});
运行结果:
Chrome 占比:0.3333333333333333
6. 总结
- 日志对 server 端的重要性,相当于人的眼睛
- IO 性能瓶颈,使用 stream 提高性能
- 使用 crontab 拆分日志文件,使用 readline 分析日志内容
边栏推荐
- [noip2008 improvement group] stupid monkey
- Questions d'examen écrit classiques du pointeur
- Ora-01779: the column corresponding to the non key value saving table cannot be modified
- Unity gets the width and height of Sprite
- [leetcode] 18. Sum of four numbers
- Class inheritance in yyds dry inventory C
- Hometown 20 years later (primary school exercises)
- Quelques conseils communs sur l'inspecteur de l'unit é, généralement pour les extensions d'éditeur ou d'autres
- Compilation et connexion de shader dans games202 - webgl (comprendre la direction)
- Codeforces Round #804 (Div. 2)
猜你喜欢
【torch】|torch.nn.utils.clip_grad_norm_
The ECU of 21 Audi q5l 45tfsi brushes is upgraded to master special adjustment, and the horsepower is safely and stably increased to 305 horsepower
ISP learning (2)
GAMES202-WebGL中shader的編譯和連接(了解向)
JS quick start (II)
Why does MySQL need two-phase commit
Codeforces Round #804 (Div. 2) Editorial(A-B)
Flody的应用
趋势前沿 | 达摩院语音 AI 最新技术大全
剑指 Offer II 039. 直方图最大矩形面积
随机推荐
[noip2008 improvement group] stupid monkey
Codeforces Round #804 (Div. 2)
剑指 Offer II 039. 直方图最大矩形面积
Pickle and savez_ Compressed compressed volume comparison
2021 robocom world robot developer competition - undergraduate group (semi-finals)
Leetcode dynamic planning day 16
GAMES202-WebGL中shader的編譯和連接(了解向)
指针经典笔试题
Driver development - hellowdm driver
Implementing fuzzy query with dataframe
On the solution of es8316's audio burst
Yolov5 tensorrt acceleration
Crazy God said redis notes
初识CDN
yolov5 tensorrt加速
F12 solve the problem that web pages cannot be copied
idea一键导包
CUDA11.1在线安装
Excel转换为Lua的配置文件
EditorUtility.SetDirty在Untiy中的作用以及应用