【老板要你啥都会系列】| 前端晋升全栈--项目日志

简介: 日志包含什么访问人数啊、峰值啊、 bug 啊什么的,如果没有日志那么很容易失控。 访问日志可以参考我们 http-server ,每次访问都会有这些东西。

1.开篇

  日志包含什么访问人数啊、峰值啊、bug 啊什么的,如果没有日志那么很容易失控。 访问日志可以参考我们 http-server,每次访问都会有这些东西。自定义日志是则并不是每次访问都打印的,根据情况或自己的需要,有点像 console.log。

日志:

  • 系统没有日志,就等于人没有眼睛——抓瞎
  • 第一,访问日志 access log ( server端最重要的日志)
  • 第二,自定义日志(包括自定义事件、错误记录等)

开发的话自然是打印在控制台,而上线了当然是要把日志保存到文件中。 如果不用 stream 的话,直接操作文件会很大的消耗 cpu 和内存。

目录

  • nodejs文件操作,nodejs stream
  • 日志功能开发和使用
  • 日志文件拆分,日志内容分析

日志文件很大自然不可能放到 redis(占超级多内存而且也是异步的,没必要马上记日志)mysql 的话需要表结构(B树)才比较合适(而且文件每个地方都可以访问,不必要安 装 mysql 文件)。

nodejs文件操作

  1. 日志要存储到文件中
  2. 为何不存储到mysql中?
  3. 为何不存储到redis中?

2.nodejs文件操作

  新建一个文件夹 file-test ,直接建一个 test1.js ,引入两个 node.js 自带的 fs 和 path 库(因为 Linux 和 windows 的文件目录路径是不一样的,需要这个 path 来统一)。 再建一个 data.text ,随便写点什么作为我们测试的文件。 回到 test1 ,利用 resolve 方法, __dirname 代表当前文件的目录获取到 data 我们就去读取文件, readFile 方法,异步的 (注意获取的是数据其实是二进制,记得转换成字符串)。

 但是按照目前这种写法,虽然可以打印出 data ,倘若这个 data 有 5 个 G 这么大,我们还这样搞,那么势必是比较浪费性能和内存(一个进程最大也就 3G )的。

  先看怎么写入文件,定义写入的文件,定义 option(flag 为 a 代表 append 追加写入,而 w 是直接覆盖)。然后就可以调用 writeFile(包含四个参数:要被写入的文件、写入的内容、 选项、回调函数)。同理,写入也是有耗费性能的情况(每次写入一行都 writeFile,一直 writeFile,就不停的打开文件,而且也很难写入非常大的数据)。再看看另一个,判断文件是否存在,调用 exists 方法,接收文件名和回调,会返回一个布尔值,注意也是异步。

const fs = require('fs' ) 
const path = require( 'path')
const fileName = path.resolve(_dirname, 'data.txt') //拿到要操作的文件
//读取文件
fs.readFile(fileName,(err ,data) => {
  if (err) {
    console. log(err)
    return 
}
    console. log(data.tostring()) //注意拿到的是二进制文件,需要转换成字符串!
})
//写入文件
const content ='三星阿卡丽\n'   //随便写入点东西
const opt =
   flag:'a'
} //定义写入的形式,a代表追加写入
fs .writeFile(fileName, content, opt, (err) => {
   if (err) {
       console.log(err) 
      return
}
})

3.stream

   IO 就是输入输出(input 和 output)。网络 IO 常见于视频播放几个 G,不可能直接下载到客户端再看,一来占用内存,二来带宽问题。IO 实在是慢,读写文件发送网络请求什么的。

IO操作的性能瓶颈:

  1. IO包括"网络IO”和"文件IO"
  2. 相比于CPU计算和内存读写, IO的突出特点就是:慢!
  3. 如何在有限的硬件资源下提高I0的操作效率?

借助下图来说,我们之前操作文件就是直接把整个桶给搬起来到到另一个桶,这就要你力气大(硬件资源超好,带宽超强)。但是在资源有限的情况下,那个管子插进去慢慢流就好,慢慢加载。

2345_image_file_copy_502.jpg

stream

实际上 request response 都是继承了 stream 的一些特性或者说本身就是 stream

4.stream演示

新建一个 stream-test 文件夹,新建一个 test1.js 。其实这个 pipe 就是 Linux 里面的标准输入出,输入啥打印啥,不用管,std 就是标准的意思, in 就是输入嘛, out 就是输出咯。就演示一下管道,输入就是流入嘛,输出就是流出嘛。接下来演示那个直接返回 request 数据,这里我们 post 的 request 内容就是 response 展示的内容, request 和 response 是桶,用管道连接,是流的关系。当然要是输入的很多,就 会一点点的流过去。 接下来是操作文件对象。

var fs = require('fs')
var path = require( 'path')
//两个文件名
var fileName1 = path.resolve(_dirname,
data. txt')
var fileName2 = path.resolve(_dirname, 'data-
bak. txt')
//读取文件的stream 对象
var readStrekm = fs. createReadStream( fi leName1)
//写入文件的stream 对象
var writeStream = fs.createWr iteSt ream(f i LeName2 )
//执行拷贝,通过pipe
readSt ream. pipe (writeSt ream )
//数据读取完成,即拷贝完成
readStream. on( 'end', function () {
console. log('拷贝完成')
})

创建写入文件和读取文件两个对象(桶)读取文件的话是从 stream 传给返回值,这些都是变成桶与桶然后流转过去,效率非常高。

//直接返Erequest数据
const http = require( http' )
const server = http .createserver((req, res) => {
    if (req.method === 'POST') {
   req.pipe(res) //从我们发送的request波到传回来的response,显示在返回结果那里
}
})
server.listen(8008)

创建两个随便的数据文件 data.txt data-bak.txt ,导入这两 个文件,分别创建读与写两个 stream 对象(桶)。这个同样可以监听 data end(这样子就知道确实是一点点读取文件的,而传统的操作是一下子都给拿出来)

//复制文件
const fs = require('fs' )
const path = require('path')
const fileName1 = path.resolve(_ dirname, 'data.txt' )
const fileName2 = path. resolve(_ dirname, 'data-bak.txt')
const readstream = fs .createReadstream(fileName1)
const writestream = fs.createwritestream(fileName2) //拿到对应的文件且创建读写对象
readstream.pipe(writestream) //将读到的文件流入写入的文件(也就是拷贝了)
readstream.on('data', chunk => { 
    console. log(chunk.tostring())
})
readstream.on('end', () => { //监听拷贝完成
console.log('copy done ' )
})
// http请求 文件
const fs = require('fs' )
const path = require('path')
const http = require('http')
const fileName1 = path.resolve(_dirname,'data.txt')
const readstream = fs .createReadstream(fileName1 )
const server = http.createserver((req, res) => {
   if (req.method === 'GET') {
        readstream.pipe(res) //直接将读取到的文件一点点传到返回值那里
}
})
server.listen(8000 )

5.写日志

  在 blog-1 下建一个 logs 文件夹,下面建三个文件:access.log、 event.log、 error.log 。 在 src 下建一个 utils 文件夹,里面建一个 log.js 。定义一个 createWriteStream 函数,就是生成右边的桶被写入),由 于只是传入 xxx.log ,所以还需要再拼接一下路径,在当前目 录(utils)上翻一层再上翻一层(blog-1),找到下面的 logs 文件夹,再找到传入的文件,就能拿到真正的地址, 根据这个地址去创建流对象(追加方式)然后返回整个对象即可。

 

   将 access.log 传入建一个对应的流对象,然后再定义一个要传给外面的函数 access (传入 log 参数)用来写访问日志, 写日志我们统一定义成一个函数 writeLog ,接收流对象和 log,直接调对象的 write 方法,传入需要写的内容即可记得换行),在 access 调用这个方法即可,其它的日我

们就不管了,也是一样的道理。 那 么 怎 么 去 用 呢 ? 回 到 aoo.js 获 取 access 方 法 , 在serverHandle 使用,把 method 、 url 、 user-agent 、当前时间 戳传入。由于是通过流写入的,效率很高,每次访问都可以写入东西。

2345_image_file_copy_503.jpg

6.拆分日志

  • 日志内容会慢慢积累,放在一个文件中不好处理
  • 按时间划分日志文件,如2019-02-10.access.log
  • 实现方式: linux的crontab命令,即定时任务

这个由于服务器基本都是 Linux 和类 Linux 的,定时任务的话 windows 可能没办法实现,所以这一块可以了解下即可,主 要是运维的搞。 解释一下*代表什么意思,第一个*代表分钟(如果不写具体 的值保持*就代表忽略,比如 1 * * * *就代表每天的第一分钟执行这个命令)第二个*代表小时(12***代表每天的第二个小时的第 1 分钟执行)第三个是日期第四个是月份第五个是 星期。command 就是一个 shell 脚本。

crontab:

  • 设置定时任务,格式: **** * command
  • 将access.log拷贝并重命名为2019-02-10.access.log
  • 清空access.log文件,继续积累日志

这个完全不需要修改代码,所以肯定是运维来搞,不过了解也是可以的。Node.js 当然也能做这个,但是就比操作系统隔了一层,不如直接通过操作系统的 shell 脚本来操作更加便捷 和高效(也好分离代码)。 回到 utils ,建一个 copy.sh (即 shell 脚本),第一行是固定的即 shell 的执行文件,将 logs 的路径拷贝过去, cd 到 logs , 拷贝 access.log 重命名为当前时间的 access.log 。 echo 移动空字符串到 access.log 相当于清空 access.log 。然后在 Linux 下面执行就行了!

7.分析日志介绍

接下来就是使用 crontab 了,因为前面已经编写好了 shell 脚本,通过输入 crontab -e 进入编辑器,设置时间,执行脚本的时候需要将整个脚本的路径拷贝过去才行。

日志分析:

  1. 如针对access.log日志,分析chrome的占比
  2. 日志是按行存储的, - -行就是一条日志
  3. 使用nodejs的readline ( 基于stream , 效率高)

通过 readline 可以一行行的去查看日志,我们先去不同浏览器运行下不同的地址,创造出不一样的 logs。


8.readline

获取到不同浏览器的日志后,我们回到 utils 下面建一个readline.js,引入 fs 、 path 、 readline 。拿到 access.log ,并且 基于这个建一个 readStream (因为是读操作)。再调用 createInterface 创一个 readline 对象,输入就是流对象。 定义一个储存 chrome 数量的变量和储存总数的变量, on 监 听 line ,每读完一行就会触发。通过 -- 切割数据,第 3 个即数组[2] 就是有关浏览器信息的,看看否包含 Chrome 决定加减。

const fs = require( ' fs' )
const path = require( 'path' ) 
const readline = require('readline')
//创建流对象(读取)
const fileName = path. join(__ dirname, '../', ' ../', 'logs' , access.log' )
const readstream = fs.createReadstream( fileName)
//创建readLine对象
const rl = readline.Interface({
   input: readstream 
})
//定义存放浏览器类型的变量
let chromeNum = 0
letsum=e


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
9天前
|
前端开发 JavaScript
前端项目公共组件封装思想
该文介绍了如何封装通用组件,如表单搜索、表格展示和分页器。首先,通过抽离出公共部分,创建`TableContainer`组件,利用具名插槽插入`navbar`和`table`,并通过props传递标题。然后在父组件中使用具名插槽和子组件实现具体功能。文中还展示了更复杂的组件封装示例,通过props实现数据双向绑定,以`el-pagination`为例,利用`sync`修饰符和`computed`属性监听并更新父组件状态,实现分页功能。文章最后提到了内容来源于哪。
|
15天前
|
前端开发
Github项目分享——免费的画图工具drow,最新前端面试题整理
Github项目分享——免费的画图工具drow,最新前端面试题整理
|
17天前
|
资源调度 前端开发
编译第三方前端项目时候出现Syntax Error: TypeError: Cannot set properties of undefined (setting ‘parent‘)
编译第三方前端项目时候出现Syntax Error: TypeError: Cannot set properties of undefined (setting ‘parent‘)
61 0
|
4天前
|
前端开发 JavaScript
前端综合练手小项目
前端综合练手小项目
|
8天前
|
前端开发 网络架构
1天搞定SpringBoot+Vue全栈开发 (8)前端路由VueRouter(进行组件切换)
1天搞定SpringBoot+Vue全栈开发 (8)前端路由VueRouter(进行组件切换)
|
8天前
|
JavaScript 前端开发 NoSQL
构建基于Node.js的全栈应用:从前端到后端的完整指南
【5月更文挑战第24天】本文是关于使用Node.js构建全栈应用的指南,涵盖前端(React或Vue)、后端(Node.js + Express)和数据库(MongoDB)的选型与实现。文章介绍了项目结构、前端组件化开发、后端API接口编写、前后端联调及部署上线的注意事项,帮助读者掌握全栈开发流程。
|
14天前
|
前端开发 JavaScript
【前端面试】this的指向_不爱吃糖的程序媛夏天,web前端面试项目中的问题包括
【前端面试】this的指向_不爱吃糖的程序媛夏天,web前端面试项目中的问题包括
|
14天前
|
JavaScript 前端开发 IDE
TypeScript在前端项目的渐进式采用策略
该文介绍了渐进式采用TypeScript在前端项目中的策略。首先,通过将JS文件扩展名改为TS并添加类型注解,如在`utils.js`中添加类型。接着,配置`tsconfig.json`,包括目标版本、模块系统、输出目录等。高级配置涉及路径别名、JSON导入、库文件等。然后,集成TypeScript到构建流程,如Webpack,安装`ts-loader`并调整配置。利用类型定义,包括安装第三方库的类型定义包,自定义类型定义或使用社区定义。最后,逐步迁移其他模块至TypeScript,强化类型检查,并确保IDE支持。
11 0
|
14天前
|
前端开发 JavaScript Java
《浅谈架构之路:前后端分离模式》 - 山人行 - 博客园,前端开发新手项目
《浅谈架构之路:前后端分离模式》 - 山人行 - 博客园,前端开发新手项目
|
14天前
|
前端开发 JavaScript Java
Java网络商城项目 SpringBoot+SpringCloud+Vue 网络商城(SSM前后端分离项目)五(前端页面
Java网络商城项目 SpringBoot+SpringCloud+Vue 网络商城(SSM前后端分离项目)五(前端页面
Java网络商城项目 SpringBoot+SpringCloud+Vue 网络商城(SSM前后端分离项目)五(前端页面