前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >如何在 Node.js 中正确的使用日志对象

如何在 Node.js 中正确的使用日志对象

作者头像
coder_koala
发布于 2021-08-26 01:52:46
发布于 2021-08-26 01:52:46
1K00
代码可运行
举报
运行总次数:0
代码可运行
作者:张挺(作者授权转载)

地址:https://mp.weixin.qq.com/s/Pb51aYdrxAALM_wR4asDgg

日志,是开发者排查问题的非常重要的手段,有时候甚至是唯一的,所以如何合理并正确的打印日志,成了开发时的重中之重。

Node.js 中打日志的方式,一般有几种:

  • 1、主动展示
  • 2、被动记录

这两种方式都可以由不同的模块来实现,我们接下去就来看看怎么选择。

常见的主动展示

一般来说,主动一般发生在开发期,不确定状态的时候,我们会打印一些消息,比如常见的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
console.log('hello world');

这就是最简单的主动打印的例子。

但是大多数场景下,我们都不会使用 console 来进行打印,毕竟除了内置之外,在性能和功能方面没有特别的优势。

除了大众都知道的 console 模块,在 Node.js 领域还有一个较为知名的 debug 模块。

可以根据命名空间打印出不同颜色的输出,但是最最有用的,则是他的环境变量控制能力。

默认情况下(不包含任何环境变量),控制台不会有任何输出,而当 DEBUG 环境变量被赋值的时候,对应的命名空间的输出才会被打印到 stdout。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ DEBUG=* node app.js

由于 debug 模块由 TJ 出品,并且在非常早的时候就投入,使用过于广泛,至今仍有非常多的模块使用了它。

Node.js 官方一直希望能够内置一个 debug 模块。从 v0.11.3 开始,终于加上了一个 util.debuglog 方法。

它的功能和 debug 模块类似,同时是内置的模块,所以逐步也有一些模块开始过渡到它。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const util = require('util');
const debuglog = util.debuglog('foo');

debuglog('hello from foo [%d]', 123);

它的开关也类似,使用的是 NODE_DEBUG 环境变量,应该是特意和 debug 模块做了区分。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ NODE_DEBUG=foo node app.js

被动记录的方式

除了上面提到的类 console 等方式,我们常见的就是各种日志库默认记录的日志,由于这些日志平时只是默默的记录,并不会过多关注,只会在特殊需要的时候(比如差错,定位,计算时)才会查看,所以我们归类为 “被动的方式”。

大多的三方库都有类似的功能,比如 log4j,winston,pino 等等。

这些库的核心功能一般是:

  • 1、将日志输出到不同的渠道(比如控制台、文本文件)
  • 2、日志格式的自定义(文本或者 JSON
  • 3、日志的输出等级(warn,debug,error)
  • 4、其他的一些能力,比如切割和文件轮转,压缩等等

这些库用起来一般就比较简单,获取实例,调用方法输出即可。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
logger.info('hello world');

注意,这里我们会观察到输出有一些不一样的地方。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
2021-07-22 14:50:59,388 INFO 7739 [xxx] hello world

整个日志是安装上面类似标准的结构来进行输出的,计算是 Error,也是相同的类似格式,那么这个结构包含了哪几部分东西呢?

日志格式

其实整个日志格式追溯,可以到很久以前,不管是 JAVA 默认的 Simple Logger 结构还是类似 nginx 等反向代理服务器的日志,都会包含一些固定的字段,这些固定的字段长久以来形成了一种输出约定,将这些字段组合起来,形成了当今的日志格式。

当前的日志格式一般会包括几个部分。

  • 时间戳
  • 日志等级
  • 进程id(node)
  • 日志的标签(label,from xxx class)
  • 消息体(字符串或者 error stack)

除此之外,可能还有一些自定义的内容,比如执行消耗的时间,用户 id,文本长度等等内容。

在文本结构的输出中,这些字段将被空格(space)分隔,以换行符作为结尾(\n),这样可以方便外部的日志采集系统采集,比如阿里云的 SLS 等等。

每个公司会有自己的日志采集和输出规范,所以一般常见的库都会支持自定义的日志格式,但是不管如何变化,基础的字段(上述)都还会存在。

随着系统的迭代,先进使用 JSON 格式来记录日志的方式也逐步出现,以 Logstash 为首的一些数据(日志)采集分析一体的工具,也逐步的成熟,对结构化的数据支持的也很好,所以现在常见的库也会同步支持 JSON 格式输出。

正确的打日志

在了解了基本的日志库和体系之后,我们来具体看一看真正打日志的问题。

比如一个简单调用远端服务:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
async invokeRemoteAPI() {
  const result = await remoteService.got();
  return {
    result
  };
}

一般,我们会有意识的加上错误处理。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
async invokeRemoteAPI() {
  
  try {
    const result = await remoteService.got();
  } catch(err) {
    logger.error('got a error, err=', err);
    throw err;
  }
  
  return {
    result
  };
}

按照上面的标准格式,这个 logger 还需要其他的一些额外信息,比如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
async invokeRemoteAPI() {
  
  const pid = process.pid;
  const startTime = Date.now();
  
  try {
    const result = await remoteService.got();
  } catch(err) {
    const endTime = Date.now();
    logger.error('pid=%s, rt=%s, got a error, err=', pid, Date.now() - startTime, err);
    throw err;
  }
  
  return {
    result
  };
}

如果每个代码都这么写,就会变得无比冗余,所以,我们会提前将日志的输出格式定义完毕,这样,在实际输出的时候就可以简化,比如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const logger = new CustomLogger({
  format: '${timestamp} ${level} ' + process.pid + ${rt}'
});

async invokeRemoteAPI() {
  const startTime = Date.now();
  
  try {
    const result = await remoteService.got();
  } catch(err) {
    const endTime = Date.now();
    logger.error('got a error, err=', err, {
      rt: Date.now() - startTime
    });
    throw err;
  }
  
  return {
    result
  };
}

所以在特定场景下,如果有固定的日志字段,在日志库允许自定义的情况下,可以先定义好固定的日志格式。

上下文日志

除了最简单的通用日志输出之外,还有一种相对复杂的日志,我们称之为和上下文(请求)绑定的日志,这类日志会输出上下文相关联的数据,比如之前示例中的响应时间,用户请求的 ip,请求的路由,甚至是链路的唯一 ID 等等。

比如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
2021-07-22 14:50:59,388 INFO 7739 [-/127.0.0.1/-/0ms GET /] hello world

这种情况下,再用普通日志的方式加入参数就不合适了。

当然,有些同学会说,我们直接定义一个新的,比如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class CustomCtxLogger extends CustomLogger {
  constructor(ctx, format) {
    this.ctx = ctx;
    this.format = format;
  }
  
  error(...args) {
    //xxx
  }
  
  info(...args) {
    //xxx
  }
}

这样的做法,每次都会让基类做初始化,会影响部分性能。我们使用另一种方式来减少性能影响,代理传统日志。

我们来看看最简单的实现方式,以 koa 为例。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 普通日志
const logger = new CustomLogger();

class CtxLogger {
  constructor(ctx, logger) {
    this.ctx = ctx;
    this.logger = logger;
  }
  
  format() {
    return '${timestamp} ${level} ' + process.pid + '[${ctx.refer} ${ctx.rt}]'
  }
}

app.use(async (ctx, next) => {
  // 代理原始日志
  const ctxLogger = new CtxLogger(ctx, logger);
  ctx.logger = ctxLogger;
  
  await next();
});

类似这种通过代理原始日志的方式,即减少了每次初始化新日志时的性能问题,又解决了 ctx 上字段透传的问题。

这也是常见的上下文日志的实践。

简单总结一下

我们了解了常用的日志库以及和日志打印的关系,也简单的实现了日志库以及上下文日志的实现,是不是现在对日志打印了有了一个基本的了解?

这样一套下来,相信你对 Node.js 打印日志的方式更加的了解,也在排错时游刃有余了。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-08-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员成长指北 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
node.js基础入门
node.js是一个基于Google V8引擎的、跨平台的JavaScript运行环境,不是一个语言
黄啊码
2022/06/20
7830
iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 记录日志
沪江CCtalk视频地址:https://www.cctalk.com/v/15114923883523 log 日志中间件 最困难的事情就是认识自己。 在一个真实的项目中,开发只是整个投入的一小部分,版本迭代和后期维护占了极其重要的部分。项目上线运转起来之后,我们如何知道项目运转的状态呢?如何发现线上存在的问题,如何及时进行补救呢?记录日志就是解决困扰的关键方案。正如我们每天写日记一样,不仅能够记录项目每天都做了什么,便于日后回顾,也可以将做错的事情记录下来,进行自我反省。完善的日志记录不仅能够还原问题
iKcamp
2018/03/30
2K0
iKcamp|基于Koa2搭建Node.js实战(含视频)☞ 记录日志
前端Node.js面试题
Node.js 是一个开源与跨平台的 JavaScript 运行时环境。在浏览器外运行 V8 JavaScript 引擎(Google Chrome 的内核),利用事件驱动、非阻塞和异步输入输出模型等技术提高性能。我们可以理解为:Node.js 就是一个服务器端的、非阻塞式I/O的、事件驱动的JavaScript运行环境。
xiangzhihong
2021/12/30
1.5K0
前端Node.js面试题
Node.js 搭建一个 API 接口服务(实战)
因为最近打算自己搭建一个自己的博客系统,用来记录日常的学习和提升一下写作水平,所以能就打算自己搭建一下前后端项目。在网上找了下,也没有找到合适(现成)的项目,所以就打算自己动手来搭建一下。这篇文章主要描述如何搭建一个node的API接口服务。
五月君
2021/01/27
8.7K0
Node.js 搭建一个 API 接口服务(实战)
Egg.js 笔记二 目录结构和内置对象
Application 是全局应用对象,在一个应用中,只会实例化一个,它继承自 Koa.Application,在它上面我们可以挂载一些全局的方法和对象。我们可以轻松的在插件或者应用中扩展 Application 对象。
tonglei0429
2019/07/22
1.3K0
Node.js 常见面试题速查
process 是一个全局变量,它提供当前 Node.js 进程的有关信息,而 process.argv 属性则返回一个数组,数组中的信息包括启动 Node.js 进程时的命令行参数
Cellinlab
2023/05/17
8330
Koa日志中间件封装开发
Koa日志中间件开发封装 对于一个服务器应用来说,日志的记录是必不可少的,我们需要使用其记录项目程序每天都做了什么,什么时候发生过错误,发生过什么错误等等,便于日后回顾、实时掌握服务器的运行状态,还原问题场景。 ---- 日志的作用 记录服务器程序运行状态; 帮助开发者快速捕获错误,定位以及决解故障。 ---- 日志中间件开发工具log4js 在node当中没有自带的日志模块,所以需要使用第三方模块 使用模块:log4js 安装: npm i log4js -S logsjs官方文档 日志分类:
keyWords
2019/03/20
1.4K0
Koa日志中间件封装开发
Nest.js 从零到壹系列(四):使用中间件、拦截器、过滤器打造日志系统
上一篇介绍了如何使用 JWT 进行单点登录,接下来,要完善一下后端项目的一些基础功能。
一只图雀
2020/04/07
6.6K0
关于Node.js,一定要学这个10+万Star项目 !!
给大家分享一个关于 Node.js 的宝藏项目,目前已经有 10+万 Star,非常值得学习。
沉浸式趣谈
2025/04/02
780
Koa入门(二)搭建 Koa 程序
安装 mkdir koa-demo && cd koa-demo && npm init -y && npm i koa --save && code .
测不准
2021/04/10
8120
关于 Node.js 调试,你需要了解的一切
Node.js 是一种颇具人气的 JavaScript 运行时,与谷歌 Chrome 浏览器一样采用同款 V8 引擎。
深度学习与Python
2023/09/08
5370
关于 Node.js 调试,你需要了解的一切
Nest.js 实战 (十):使用 winston 打印和收集日志记录
日志记录在后台服务的重要性不言而喻,它可以帮助开发者调试和故障排查、性能监控、审计和安全、监控和警报等。
白雾茫茫丶
2024/08/30
4300
Nest.js 实战 (十):使用 winston 打印和收集日志记录
一篇文章构建你的 Node.js 知识体系
最近读《重学前端》,开篇就是让你拥有自己的知识体系图谱,后续学的东西补充到相应的模块,既可以加深对原有知识的理解,又可以强化记忆,很不错的学习方案。
五月君
2020/08/28
1.9K0
一篇文章构建你的 Node.js 知识体系
2024年 Node.js 精选:50款工具库集锦,项目开发轻松上手(一)
首先,我们需要了解NPM的重要性。NPM不仅仅是一个包管理器,它还是一个强大的工具,能够帮助开发者在全球范围内共享和重用代码。通过NPM,你可以轻松地安装、更新和管理依赖关系,使得项目开发更加高效和系统化。
前端达人
2024/02/23
9750
2024年 Node.js 精选:50款工具库集锦,项目开发轻松上手(一)
第六十九期:聊一聊Node程序调试(二)
当我们将NODE_DEBUG设置为timer时,第一条日志消息表明它正在创建一个长度为120000的列表。我们的代码传递120000作为传递给setTimeout的第二个参数,在内部,第一个参数(超时回调)被添加到一个回调队列中,该队列应在120000毫秒后运行。接下来,我们看到一条消息,timeout callback 5000,这意味着现在将调用每5000毫秒的超时回调。
terrence386
2022/07/15
3760
第六十九期:聊一聊Node程序调试(二)
v15.x 新 feature — Node.js timers 模块引入 setInterval 异步迭代器
作者简介:五月君,Software Designer,公众号「Nodejs技术栈」作者。
五月君
2021/03/16
9430
基于 log4js 做全链路日志
提到监控或应用观测,经常出现三个词:「链路(Tracing)」、「指标(Metric)」和「日志(Logging)」。
joefu
2022/01/20
2.6K1
基于Egg框架的日志链路追踪实践
实现全链路日志追踪,便于日志监控、问题排查、接口响应耗时数据统计等,首先 API 接口服务接收到调用方请求,根据调用方传的 traceId,在该次调用链中处理业务时,如需打印日志的,日志信息按照约定的规范进行打印,并记录 traceId,实现日志链路追踪。
五月君
2019/07/12
2K0
Node.js 多进程/线程 —— 日志系统架构优化实践
1. 背景   在日常的项目中,常常需要在用户侧记录一些关键的行为,以日志的形式存储在用户本地,对日志进行定期上报。这样能够在用户反馈问题时,准确及时的对问题进行定位。   为了保证日志信息传输的安全、缩小日志文件的体积,在实际的日志上传过程中会对日志进行加密和压缩,最后上传由若干个加密文件组成的一个压缩包。   为了更清晰的查看用户的日志信息。需要搭建一个用户日志管理系统,在管理系统中可以清晰的查看用户的日志信息。但是用户上传的都是经过加密和压缩过的文件,所以就需要在用户上传日志后,实时的对用户上传的日志
用户1097444
2022/06/29
1.4K0
Node.js 多进程/线程 —— 日志系统架构优化实践
推荐一个基于 Node.js 的表单验证库 [每日前端夜话0x23]
API 在执行过程中的一个基本任务是数据验证。 在本文中,我想向你展示如何为你的数据添加防弹验证,同时返回风格良好的格式。
疯狂的技术宅
2019/03/27
2.8K0
推荐阅读
相关推荐
node.js基础入门
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验