前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Console 模块解读及简单实现

Console 模块解读及简单实现

作者头像
coder_koala
发布于 2019-10-17 13:36:02
发布于 2019-10-17 13:36:02
1.2K00
代码可运行
举报
运行总次数:0
代码可运行

Console 模块提供了简单的调试功能,这在一些测试调试中有时也是使用最方便、用的最多的,它和浏览器中的 console 类似,但是在浏览器中它是同步的,在 Node.js 中,就有个疑问了是同步还是异步?

本文主要参考了官方源码,实现了一个简单版的 Console 模块,介绍了一些基本使用,同时提出了一些常见的问题,供大家学习参考,具体看以下正文介绍。

作者简介:五月君,Nodejs Developer,热爱技术、喜欢分享的 90 后青年,公众号「Nodejs技术栈」,Github 开源项目 https://www.nodejs.red

快速导航

  • Logger 模块实现
    • 实现步骤
    • 创建 logger.js 文件
  • Logger 模块基本使用
    • 日志输出至终端 log、info、error、warn、clear
    • 日志输出至文件
    • trace 打印错误堆栈
    • dir 显示一个对象的所有属性和方法
    • time 和 timeEnd 计算程序执行消耗时间
  • 面试指南
    • console是同步的还是异步的?,参考:#
    • 如何实现一个console.log?,参考:#
    • 为什么console.log()执行完后就退出?,参考:#

Logger 模块实现

实现步骤

  1. 初始化 Logger 对象
  2. 对参数进行检验,当前对象是否为 Logger 实例,是否为一个可写流实例
  3. 为 Logger 对象定义 stdout,stderr 等属性
  4. 将原型方法上的属性绑定到 Logger 实例上
  5. 实现 log、error、warning、trace、clear 等方法

创建logger文件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const util = require('util');
/** * 初始化Logger对象 * @param {*} stdout * @param {*} stderr */function Logger(stdout, stderr){    // step1 检查当前对象是否为Logger实例    if(!(this instanceof Logger)){        return new Logger(stdout, stderr);    }
    //检查是否是一个可写流实例    if(!stdout || !(stdout.write instanceof Function)){        throw new Error('Logger expects a writable stream instance');    }
    // 如果stderr未指定,使用stdout    if(!stderr){        stderr = stdout;    }
    //设置js Object的属性    let props = {        writable: true, // 对象属性是否可修改,flase为不可修改,默认值为true        enumerable: false, // 对象属性是否可通过for-in循环,flase为不可循环,默认值为true        configurable: false, // 能否使用delete、能否需改属性特性、或能否修改访问器属性、,false为不可重新定义,默认值为true    }
    // Logger对象定义_stdout属性    Object.defineProperty(this, '_stdout', Object.assign(props, {        value: stdout,    }));
    // Logger对象定义_stderr属性    Object.defineProperty(this, '_stderr', Object.assign(props, {        value: stderr,    }));
    // Logger对象定义_times属性    Object.defineProperty(this, '_times', Object.assign(props, {        value: new Map(),    }));
    // 将原型方法上的属性绑定到Logger实例上    const keys = Object.keys(Logger.prototype);
    for(let k in keys){        this[keys[k]] = this[keys[k]].bind(this);    }}
//定义原型Logger的log方法Logger.prototype.log = function(){    this._stdout.write(util.format.apply(this, arguments) + '\n');}
Logger.prototype.info = Logger.prototype.log;
// 定义原型Logger的warn方法Logger.prototype.warn = function(){    this._stderr.write(util.format.apply(this, arguments) + `\n`);}
Logger.prototype.error = Logger.prototype.warn;
// 返回当前调用堆栈信息Logger.prototype.trace = function trace(...args){    const err = {        name: 'Trace',        message: util.format.apply(null, args)    }
    // 源自V8引擎的Stack Trace API https://github.com/v8/v8/wiki/Stack-Trace-API    Error.captureStackTrace(err, trace);
    this.error(err.stack);}
// 清除控制台信息Logger.prototype.clear = function(){
    // 如果stdout输出是一个控制台,进行clear 否则不进行处理    if(this._stdout.isTTY){        const { cursorTo, clearScreenDown } = require('readline');        cursorTo(this._stdout, 0, 0); // 移动光标到给定的 TTY stream 中指定的位置。        clearScreenDown(this._stdout); // 方法会从光标的当前位置向下清除给定的 TTY 流    }}
//直接输出某个对象Logger.prototype.dir = function(object, options){    options = Object.assign({ customInspect: false }, options);
    /**     * util.inspect(object,[showHidden],[depth],[colors])是一个将任意对象转换为字符串的方法,通常用于调试和错误的输出。     * showhidden - 是一个可选参数,如果值为true,将会输出更多隐藏信息。     * depth - 表示最大递归的层数。如果对象很复杂,可以指定层数控制输出信息的多少。     * 如果不指定depth,默认会递归3层,指定为null表示不限递归层数完整遍历对象。     * 如果color = true,输出格式将会以ansi颜色编码,通常用于在终端显示更漂亮的效果。     */    this._stdout.write(util.inspect(object, options) + '\n');}
// 计时器开始时间Logger.prototype.time = function(label){
    // process.hrtime()方法返回当前时间以[seconds, nanoseconds] tuple Array表示的高精度解析值, nanoseconds是当前时间无法使用秒的精度表示的剩余部分。    this._times.set(label, process.hrtime())}
// 计时器结束时间Logger.prototype.timeEnd = function(label){    const time = this._times.get(label);
    if (!time) {        process.emitWarning(`No such label '${label}' for console.timeEnd()`);        return;    }
    const duration = process.hrtime(time);    const ms = duration[0] * 1000 + duration[1] / 1e6; // 1e6 = 1000000.0 1e6表示1*10^6    this.log('%s: %sms', label, ms.toFixed(3));    this._times.delete(label);}
module.exports = new Logger(process.stdout, process.stderr);
module.exports.Logger = Logger;

Logger 模块基本使用

日志输出至终端

无特殊说明,日志都是默认打印到控制台,在一些代码调试中也是用的最多的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const logger = reuqire('logger');
logger.log('hello world') // 普通日志打印logger.info('hello world') // 等同于logger.loglogger.error('hello world') // 错误日志打印logger.warn('hello world') // 等同于logger.errorlogger.clear() // 清除控制台信息

日志输出至文件

定义要输出的日志文件,实例化我们自定义的 Logger 对象

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const fs = require('fs');const output = fs.createWriteStream('./stdout.txt');const errorOutput = fs.createWriteStream('./stderr.txt');const { Logger } = require('./logger');
const logger = Logger(output, errorOutput);
logger.info('hello world!'); // 内容输出到 stdout.txt 文件logger.error('错误日志记录'); // 内容输出到 stderr.txt 文件

版本问题

将日志信息打印到本地指定文件,这里要注意版本问题,以下代码示例在 nodev10.x 以下版本可以,nodev10.x 及以上的版本这块有改动,可能会报错如下,具体原因参见 https://github.com/nodejs/node/issues/21366

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
TypeError: Console expects a writable stream instance    at new Console (console.js:35:11)    at Object.<anonymous> (/Users/ryzokuken/Code/temp/node/console/21366.js:11:16)    at Module._compile (module.js:652:30)    at Object.Module._extensions..js (module.js:663:10)    at Module.load (module.js:565:32)    at tryModuleLoad (module.js:505:12)    at Function.Module._load (module.js:497:3)    at Function.Module.runMain (module.js:693:10)    at startup (bootstrap_node.js:188:16)    at bootstrap_node.js:609:3

trace打印错误堆栈

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
logger.trace('测试错误');
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Trace: 测试错误    at Object.<anonymous> (/Users/qufei/Documents/mycode/Summarize/test/console-test.js:7:8)    at Module._compile (module.js:624:30)    at Object.Module._extensions..js (module.js:635:10)    at Module.load (module.js:545:32)    at tryModuleLoad (module.js:508:12)    at Function.Module._load (module.js:500:3)    at Function.Module.runMain (module.js:665:10)    at startup (bootstrap_node.js:201:16)    at bootstrap_node.js:626:3

dir显示一个对象的所有属性和方法

depth - 表示最大递归的层数。如果对象很复杂,可以指定层数控制输出信息的多少。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const family = {    name: 'Jack',    brother: {        hobby: ['篮球', '足球']    }}
logger.dir(family, {depth: 3});
// { name: 'Jack', brother: { hobby: [ '篮球', '足球' ] } }

计算程序执行消耗时间

logger.time 和 logger.timeEnd 用来测量一个 javascript 脚本程序执行消耗的时间,单位是毫秒

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 启动计时器logger.time('计时器');
// 中间写一些测试代码for(let i=0; i < 1000000000; i++){}
// 停止计时器logger.timeEnd('计时器');
// 计时器: 718.034ms

Interview1

console 是同步的还是异步的?

console 既不是总是同步的,也不总是异步的。是否为同步取决于链接的是什么流以及操作系统是 Windows 还是 POSIX:

注意: 同步写将会阻塞事件循环直到写完成。有时可能一瞬间就能写到一个文件,但当系统处于高负载时,管道的接收端可能不会被读取、缓慢的终端或文件系统,因为事件循环被阻塞的足够频繁且足够长的时间,这些可能会给系统性能带来消极的影响。当你向一个交互终端会话写时这可能不是个问题,但当生产日志到进程的输出流时要特别留心。

  • 文件(Files): Windows 和 POSIX 平台下都是同步
  • 终端(TTYs): 在 Windows 平台下同步,在 POSIX 平台下异步
  • 管道(Pipes): 在 Windows 平台下同步,在 POSIX 平台下异步

Interview2

如何实现一个 console.log?

实现 console.log 在控制台打印,利用 process.stdout 将输入流数据输出到输出流(即输出到终端),一个简单的例子输出 hello world

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
process.stdout.write('hello world!' + '\n');

Interview3

为什么 console.log() 执行完后就退出?

这个问题第一次看到是来自于朴灵大神的一次演讲,涉及到 EventLoop 的执行机制,一旦产生事件循环,就会产生一个 While(true) 的死循环,例如定时器 setInterval,但是 console.log 它没有产生 watch、handlers 在事件循环中执行了一次就退出了。同时另一个疑问开启一个 http server 为什么进程没有退出?参考下文章 Node.js 为什么进程没有 exit?

Reference

  • http://nodejs.cn/api/console.html
  • http://nodejs.cn/api/process.html#processanoteonprocessio
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-10-16,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
无情面试官:Node.js源码里的console.log怎么实现的?
最近一直在研究微前端、devops,写这篇文章仅是一个玩笑+简单的源码探究,面试时候不要拿我的文章出来问面试者,不然我怕你会被人身攻击(这个月我会出一篇硬核到头皮发麻的文章)
Peter谭金杰
2020/05/09
2.1K0
无情面试官:Node.js源码里的console.log怎么实现的?
鸿蒙开发:console日志输出
可以说,任何一种语言,都少不了日志,正因为有了日志,让开发者开发功能,排查问题,才显得游刃有余,一段代码执行的是否正确,某个结果是否匹配一致,等等,简简单单,一个日志打印,便可轻松知晓。
程序员一鸣
2025/03/15
1770
鸿蒙开发:console日志输出
console有趣的用法
1.修改控制台输出内容样式 2.重写控制台输出格式(追加出错日期) 3.以表格的形式显示控制台输出内容 4.如何统计一个函数在程序中被调用的总次数 5.以便于阅读的形式输出打印内容 6.统计程序执行的时间 7.分组显示控制台输出内容 8.获取当前代码在堆栈中的调用路径 9.清除控制台显示的所有内容 10.断言输出
酷走天涯
2019/06/11
1.1K0
console有趣的用法
你了解Console对象嘛 ? ? 快来瞅瞅吧
你还只是知道Console.log方法嘛,Console对象其实还有很多方法。下面带大家一起来深入了解下Console对象的各个方法。
用户9914333
2022/07/21
2700
你了解Console对象嘛 ? ? 快来瞅瞅吧
10个很少使用的JavaScript Console 方法
你一定听说过 console.log() ,而且可能一直在使用它。它非常流行,在集成开发环境中键入时,Visual Studio Intellicode 等工具通常会在其他控制台方法之前推荐使用它。
前端小智@大迁世界
2023/09/07
3200
10个很少使用的JavaScript Console 方法
Node.js 全局对象(上)
JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。
陈不成i
2021/07/12
1.9K0
Node入门教程(5)第四章:global 全局变量
global - 全局变量 全局对象(global object),不要和 全局的对象( global objects )或称标准内置对象混淆。这里说的全局的对象是说在全局作用域里的内的对象。全局作用域包含了全局对象的属性,还有它继承来的属性。 注意浏览器下的全局对象跟 nodejs 中的全局对象不一致 浏览器环境下的全局对象就是window Node 的全局对象是 global JS 语言标准的全局的内置对象 JS 语言规范中的全局的内置对象在 Nodejs 中都有效,以下简单过一下,不熟悉请查MDN 文
老马
2018/04/16
1.3K0
用console画条龙?
console一定是各位前端er最熟悉的小伙伴了,无论是console控制台,还是console对象,做前端做久了,打开一个网页总是莫名自然的顺手打开控制台,有些调皮的网站还会故意在控制台输出一些有意思的东西,比如招聘信息,像百度的:
街角小林
2022/06/15
6470
用console画条龙?
console实用调试技巧console实用调试技巧
这里将后面的变量赋值给了前面的占位符的位置,他们是一一对应的。这种写法在复杂的输出时,能保证模板和数据分离,结构更加清晰。不过如果是简单的输出,就没必要这样写了。在console.log中,支持的占位符格式如下:
贺贺V5
2021/11/24
1.1K0
console实用调试技巧console实用调试技巧
用 console 画条龙?
console一定是各位前端er最熟悉的小伙伴了,无论是console控制台,还是console对象,做前端做久了,打开一个网页总是莫名自然的顺手打开控制台,有些调皮的网站还会故意在控制台输出一些有意思的东西,比如招聘信息,像百度的:
winty
2021/07/27
8590
12种 console 相关的方法,帮你快速提高调试效率!(建议收藏)
我现在在调试代码的时候基本用的都是console.log() 调试法,虽然低级,但好用呀。当然,遇到复杂点的就会借助其它工具,但日常开发中 console.log() 基本够用了。
前端小智@大迁世界
2022/06/15
1.3K0
12种 console 相关的方法,帮你快速提高调试效率!(建议收藏)
深入解析 Node.js 的 console.log[每日前端夜话0x73]
当你开始用 JavaScript 进行开发时,可能学到的第一件事就是如何用 console.log 将内容记录到控制台。如果你去搜索如何调试 JavaScript,会发现数百篇博文和 StackOverflow 文章都会简单的告诉你用 console.log。因为这是一种很常见的做法,我们甚至会在代码中使用像 no-console 这样的 linter 规则来确保不会留下意外的日志信息。但是如果我们真的想要去记录某些内容呢?
疯狂的技术宅
2019/05/29
2.1K0
一文读懂NodeJs知识体系和原理浅析
Node.js 是一个 JS 的服务端运行环境,简单的来说,它是在 JS 语言规范的基础上,封装了一些服务端的运行时对象,让我们能够简单实现非常多的业务功能。
coder2028
2022/10/17
1.3K0
详解Nodejs中命令执行原型链污染等漏洞
Nodejs特例 大小写转换函数 toUpperCase(): 将小写转换为大写的函数 toLowerCase(): 将大写转换为小写的函数 注意: 前者可以将ı转换为I, 将ſ转为为S 后者可以将İ转换为i, 将K转换为k 数组 a && b && a.length===b.length && a!==b && md5(a+flag)===md5(b+flag) 通过调试: 可以传入数组绕过。 命令执行 eval eval() 函数可计算某个字符串,并执行其中的的 JavaScr
FB客服
2023/04/26
2.1K0
详解Nodejs中命令执行原型链污染等漏洞
让js调试更简单—console
console上述的集中度支持printf的占位符格式,支持的占位符有:字符(%s)、整数(%d或%i)、浮点数(%f)和对象(%o):
超然
2018/08/03
2.5K0
让js调试更简单—console
前端-结合源码分析 Node.js 模块加载与运行原理
Node.js 的出现,让 JavaScript 脱离了浏览器的束缚,进入了广阔的服务端开发领域。而 Node.js 对 CommonJS 模块化规范的引入,则更是让 JavaScript成为了一门真正能够适应大型工程的语言。
grain先森
2019/03/29
2.3K0
前端-结合源码分析 Node.js 模块加载与运行原理
Node.js进阶之进程与线程
进程与线程在服务端研发中是一个非常重要的概念,如果您在学习的时候对这一块感到混乱或者不是太理解,可以阅读下本篇内容,本篇在介绍进程和线程的概念之外,列举了很多 Demo 希望能从实战角度帮助您更好的去理解。
五月君
2019/07/12
1.2K0
Node.js进阶之进程与线程
nodejs之模块系统源码分析(上)
nodejs的模块分为几种,有内置的c++模块,内置的js模块,还有用户自定义的模块。下面我们先分析内置模块。然后在分析用户定义的模块。
theanarkh
2019/03/19
1.2K0
提高 DevTools 控制台调试 console 的 12 种方法
使用最多的 console.log() 对于在代码运行时输出值非常有用,通常可以帮助查明错误。
夜尽天明
2023/03/15
8390
提高 DevTools 控制台调试 console 的 12 种方法
Node.js 的错误处理机制
Node.js的API主要有两种风格,同步和异步,如何区分呢,大部分异步API一般都有一个回调函数 callback 作为其参数,而大部分同步API则不会,例如:
conanma
2022/01/05
2.2K0
推荐阅读
相关推荐
无情面试官:Node.js源码里的console.log怎么实现的?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验