用 Node
写服务端的时候,比较头疼的是排查定位问题,因为不像 Chrome
浏览器中,我们有直接的报错提示在 dev tool
中,或者我们可以直接打断点调试。
经常我们会遇到在明明我在 test
环境可以啊,为什么 live
环境不行的问题。假如没有日志,对于这种问题,根据毫无头绪。
所以这篇文章,我们来聊聊如何在 Node
服务中写日志。
本文 demo 可以查看 Github[1]
先保证你全局安装了 koa2
:
npm i koa2 -g
然后执行:
koa2 -e node-log # 新建一个项目
cd node-log
npm i # 安装依赖
npm run start # 启动
基础的服务就搭建起来了。访问 http://localhost:3000/
,就可以看到以下的页面:
以上是一个快速搭建 koa
服务的方法。这个项目中内置了一个写日志的库——koa-logger
。我们先来看看它做了什么东西吧。
这个库比较简单,记录请求的基本信息,比如请求的方法、URl、用时等。作为中间件中使用,注意:推荐放在所有的中间件之前,这个跟 koa 的洋葱模型有关。假如不是第一个,计算时间会不准确。
var logger = require('koa-logger');
app.use(logger());
在我们访问响应的资源的时候,会在控制台输出相应的日志如下:
<-- GET /
GET / - 14
--> GET / 200 19ms 234b
<-- GET /stylesheets/style.css
GET /stylesheets/style.css - 1
--> GET /stylesheets/style.css 200 3ms 111b
<-- GET /favicon.ico
GET /favicon.ico - 1
--> GET /favicon.ico 404 1ms -
默认情况下,日志是通过 console
的方式直接输出到控制台中,假如我们需要对日志做自定义的操作,比如写入到日志文件中等。可以通过类似完成,比如我记录时间:
app.use(logger((str) => {
console.log(new Date() + str)
// redirect koa logger to other output pipe
// default is process.stdout(by console.log function)
}))
结果:
Mon Oct 11 2021 19:28:41 GMT+0800 (China Standard Time) <-- GET /
GET / - 10ms
Mon Oct 11 2021 19:28:41 GMT+0800 (China Standard Time) --> GET / 200 20ms 226b
Mon Oct 11 2021 19:28:41 GMT+0800 (China Standard Time) <-- GET /stylesheets/style.css
Mon Oct 11 2021 19:28:41 GMT+0800 (China Standard Time) --> GET /stylesheets/style.css 200 4ms 111b
koa-logger
比较轻量,也暴露了相对灵活的接口。但在实际业务中使用,我个人推荐使用 koa-log4js
。主要理由如下:
koa-logger
看起来只支持中间件的使用方式,而不支持上报特定日志的功能。koa-log4js[2] 对 log4js-node[3] 做了一层包装,从而支持 Koa
日志的中间件。它的配置和 log4js-node
是保持一致的。所以假如你用 log4js-node
的话,使用上应该是一致的。
安装:
npm i --save koa-log4
先来看使用,根目录新建一个文件夹 log
。并且新建一个文件夹 utils
,在其中新建文件 logger.js
。代码如下:
const path = require('path');
const log4js = require('koa-log4');
const RUNTIME_PATH = path.resolve(__dirname, '../');
const LOG_PATH = path.join(RUNTIME_PATH, 'log');
log4js.configure({
// 日志的输出
appenders: {
access: {
type: 'dateFile',
pattern: '-yyyy-MM-dd.log', //生成文件的规则
alwaysIncludePattern: true, // 文件名始终以日期区分
encoding: 'utf-8',
filename: path.join(LOG_PATH, 'access.log') //生成文件名
},
application: {
type: 'dateFile',
pattern: '-yyyy-MM-dd.log',
alwaysIncludePattern: true,
encoding: 'utf-8',
filename: path.join(LOG_PATH, 'application.log')
},
out: {
type: 'console'
}
},
categories: {
default: { appenders: [ 'out' ], level: 'info' },
access: { appenders: [ 'access' ], level: 'info' },
application: { appenders: [ 'application' ], level: 'all'}
}
});
// getLogger 传参指定的是类型
exports.accessLogger = () => log4js.koaLogger(log4js.getLogger('access')); // 记录所有访问级别的日志
exports.logger = log4js.getLogger('application');
简单解释一下,configure
是 log4js-node
的配置(后文会详解),通过 getLogger
函数传参为日志类型,比如 access
是访问级别日志。
然后在 app.js
中加入:
const { accessLogger, logger } = require('./utils/logger');
app.use(accessLogger())
以及 routes/index.js
中加入:
+ const { logger } = require('../utils/logger')
router.get('/', async (ctx, next) => {
+ logger.info('我是首页');
await ctx.render('index', {
title: 'Hello Koa 2!'
})
})
刷新,可以看到在 log
文件夹中输出两个文件:
分别记录了:
[2021-10-12T10:43:33.914] [INFO] access - ::1 - - "GET / HTTP/1.1" 200 226 "" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36"
[2021-10-12T10:43:34.065] [INFO] access - ::1 - - "GET /stylesheets/style.css HTTP/1.1" 200 111 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36"
[2021-10-12T10:43:33.902] [INFO] application - 我是首页
接下来我们来看看 log4js-node
的配置。
日志的分级,主要作用是更好的展示日志(不同颜色)、有选择的落盘日志,比如在生产中避免一些 debug
的敏感日志被泄露。log4js-node
默认有九个分级(你可以通过 levels
进行修改),如下:
<pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/fWJhv9xMFTsBbgTpL2X7Ue3PGoo1aKwwGUwBDs6uIs2YnZCUB4SDqKEGoUUYEz1IXefD7icztaAI6jNdCkPSovhZfMH8eYfZU/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">```
{<br></br> <span style="color: #d19a66;line-height: 26px;">ALL: <span style="color: #c678dd;line-height: 26px;">new Level(<span style="color: #e6c07b;line-height: 26px;">Number.MIN_VALUE, <span style="color: #98c379;line-height: 26px;">"ALL"),<br></br> <span style="color: #d19a66;line-height: 26px;">TRACE: <span style="color: #c678dd;line-height: 26px;">new Level(<span style="color: #d19a66;line-height: 26px;">5000, <span style="color: #98c379;line-height: 26px;">"TRACE"),<br></br> <span style="color: #d19a66;line-height: 26px;">DEBUG: <span style="color: #c678dd;line-height: 26px;">new Level(<span style="color: #d19a66;line-height: 26px;">10000, <span style="color: #98c379;line-height: 26px;">"DEBUG"),<br></br> <span style="color: #d19a66;line-height: 26px;">INFO: <span style="color: #c678dd;line-height: 26px;">new Level(<span style="color: #d19a66;line-height: 26px;">20000, <span style="color: #98c379;line-height: 26px;">"INFO"),<br></br> <span style="color: #d19a66;line-height: 26px;">WARN: <span style="color: #c678dd;line-height: 26px;">new Level(<span style="color: #d19a66;line-height: 26px;">30000, <span style="color: #98c379;line-height: 26px;">"WARN"),<br></br> <span style="color: #d19a66;line-height: 26px;">ERROR: <span style="color: #c678dd;line-height: 26px;">new Level(<span style="color: #d19a66;line-height: 26px;">40000, <span style="color: #98c379;line-height: 26px;">"ERROR"),<br></br> <span style="color: #d19a66;line-height: 26px;">FATAL: <span style="color: #c678dd;line-height: 26px;">new Level(<span style="color: #d19a66;line-height: 26px;">50000, <span style="color: #98c379;line-height: 26px;">"FATAL"),<br></br> <span style="color: #d19a66;line-height: 26px;">MARK: <span style="color: #c678dd;line-height: 26px;">new Level(<span style="color: #d19a66;line-height: 26px;">9007199254740992, <span style="color: #98c379;line-height: 26px;">"MARK"), <span style="color: #5c6370;font-style: italic;line-height: 26px;">// 2^53<br></br> <span style="color: #d19a66;line-height: 26px;">OFF: <span style="color: #c678dd;line-height: 26px;">new Level(<span style="color: #e6c07b;line-height: 26px;">Number.MAX_VALUE, <span style="color: #98c379;line-height: 26px;">"OFF")<br></br>}<br></br>
如下图所示:
![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/2021-10-16/8f80661d09b65038c0e304d5e5a11866)
只会输出级别相等或者级别高的日志。比如你配置了 `WARN`,就不会输出 `INFO` 的日志。可以在下面配置的 `categories` 中配置不同的类型日志的日志级别。
### categories
日志类别。必须配置默认日志类别,用于没有命中的情况下的兜底行为。该配置为一个对象,`key` 值为分类名称。比如上述 `demo` 中:
```
{
default: { appenders: [ 'out' ], level: 'info' },
access: { appenders: [ 'access' ], level: 'info' },
application: { appenders: [ 'application' ], level: 'all'}
}
```
```
其中每个类别都有两个配置 appenders
是一个字符串数组,是输出配置(后文中会详解),可以指定多个,至少要有一个。level
是上文日志级别。
appenders
解决了日志分级和分类,接下来是日志落盘,也就是输出日志的问题。对应的配置是 appenders
,该配置的 key
值为自定义的名称(可以给 categories
中的 appenders
使用),属性值为一个对象,配置输出类型。如下所示:
<pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/fWJhv9xMFTsBbgTpL2X7Ue3PGoo1aKwwGUwBDs6uIs2YnZCUB4SDqKEGoUUYEz1IXefD7icztaAI6jNdCkPSovhZfMH8eYfZU/640?wx_fmt=svg") 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;">```
<span style="color: #5c6370;font-style: italic;line-height: 26px;">// 日志的输出<br></br><span style="color: #d19a66;line-height: 26px;">appenders: {<br></br> <span style="color: #d19a66;line-height: 26px;">access: {<br></br> <span style="color: #d19a66;line-height: 26px;">type: <span style="color: #98c379;line-height: 26px;">'dateFile',<br></br> <span style="color: #d19a66;line-height: 26px;">pattern: <span style="color: #98c379;line-height: 26px;">'-yyyy-MM-dd.log', <span style="color: #5c6370;font-style: italic;line-height: 26px;">//生成文件的规则<br></br> <span style="color: #d19a66;line-height: 26px;">alwaysIncludePattern: <span style="color: #56b6c2;line-height: 26px;">true, <span style="color: #5c6370;font-style: italic;line-height: 26px;">// 文件名始终以日期区分<br></br> <span style="color: #d19a66;line-height: 26px;">encoding: <span style="color: #98c379;line-height: 26px;">'utf-8',<br></br> <span style="color: #d19a66;line-height: 26px;">filename: path.join(LOG_PATH, <span style="color: #98c379;line-height: 26px;">'access.log') <span style="color: #5c6370;font-style: italic;line-height: 26px;">//生成文件名<br></br> },<br></br> <span style="color: #d19a66;line-height: 26px;">out: {<br></br> <span style="color: #d19a66;line-height: 26px;">type: <span style="color: #98c379;line-height: 26px;">'console'<br></br> }<br></br>}<br></br>
其中,`out` 指的是通过 `console` 输出,这个可以作为我们的一个兜底。`access` 中 `type` 为 `dataFile`,指的是输出文件,然后配置文件的命名和输出路径。除了这个类型,详情可以看官网\[4\],比如 `SMTP` 通过邮件发送(这个需要 `nodemailer`)
### 总结配置
日志分级、日志分类以及日志落盘,配置上的关系如下:![](https://oss-cn-hangzhou.aliyuncs.com/codingsky/cdn/img/2021-10-16/4deb6a34b7e05af920fcef408ce614ac)
总结
--
日志对于我们服务端排查定位问题非常重要,本文通过 `koa-logger` 和 `koa-log4js` 讲解了如何上报日志。
`koa-logger` 比较轻量,记录请求的基本信息,也提供了一定的自定义能力。
`koa-log4js` 在日志分级、日志分类以及日志落盘上提供了一定的能力,个人认为基本这个更加适用于生产环境。
### 参考资料
\[1\]Github: *https://github.com/GpingFeng/node-log*
\[2\]koa-log4js: *https://www.npmjs.com/package/koa-log4*
\[3\]log4js-node: *https://github.com/log4js-node/log4js-node*
\[4\]官网: *https://log4js-node.github.io/log4js-node/appenders.html*
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/dRmB4kNbxCljIGZ7DBLy7g
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。