# 基础调试 console
30 秒速记
console是开发期的调试工具:log/info写stdout,error/warn/trace写stderr- 常用全家桶:
log、error、dir(控制展开深度)、time/timeEnd(计时)、trace(打调用栈)、table、assert new console.Console(stdout, stderr)可以把输出导到文件或任意可写流- 写文件和管道时
console可能是同步的,高频打印会拖慢服务;线上日志用pino这类结构化日志库 - 别直接打
req、token、用户数据,对象太大还会刷屏、带出敏感字段
console 是 Node 里最顺手的调试工具,但它定位是「开发时看一眼」,不是生产日志方案。 它分两条输出通道:普通信息走标准输出,错误和警告走标准错误,这个区分在重定向和日志采集时很有用,比如 node app.js 2> err.log 就能只把错误存下来。除了 log,dir 看深层对象、time 测耗时、trace 查是谁调的这个函数,都是排查问题的利器。线上要的是带级别、时间戳、请求 ID 的 JSON 日志,那就交给专门的日志库。
理解「基础调试 console」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。
工程中还要验证退出码、资源清理和异常输入。属于 module 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。
console 在 Node 里其实是 Console 类的一个实例,绑定了 process.stdout 和 process.stderr 两个流。记住哪个方法写到哪个流,排查「日志去哪了」就很快:
| 方法 | 输出流 | 用途 |
|---|---|---|
console.log / console.info / console.debug |
stdout |
普通信息 |
console.error / console.warn |
stderr |
错误、警告 |
console.trace |
stderr |
带调用栈的信息 |
console.dir |
stdout |
按指定深度展开对象 |
console.table |
stdout |
数组、对象以表格展示 |
console.time / console.timeEnd |
stdout |
计时 |
// demo.js
console.log('normal');
console.error('oops');
console.table([{ a: 1, b: 2 }, { a: 3, b: 4 }]);
node demo.js 2>/dev/null
# 只看到 normal 和表格,oops 被丢掉了,因为它在 stderr
开发时 console 足够用;服务上线后,日志要能被检索和告警,至少需要时间、级别、请求 ID、事件名和上下文字段,这时候换成 pino、winston 之类的结构化日志库,输出 JSON 交给日志平台采集。
面试官追问
追问 1node app.js > out.log,为什么报错信息还是打在终端上?
> 只重定向标准输出,console.error 走的是标准错误。要一起存就写 node app.js > out.log 2>&1。
追问 2高并发接口里每个请求打好几行 console.log,压测时 RT 明显变高,为什么?
stdout 指向文件或管道时,写入可能是同步的,大量打印会占住主线程;而且 util.inspect 格式化大对象本身就耗 CPU。线上换 pino 这类异步、JSON 格式的日志库,调试日志按级别关掉。
追问 3想打印一个请求对象排查问题,console.log(req) 有什么风险?
一是太大,IncomingMessage 上挂着 socket 等一堆内部对象,刷屏还看不清;二是会把 Cookie、Authorization 头这些敏感信息写进日志。挑需要的字段打:console.log({ url: req.url, method: req.method })。
追问 4进程最后一行 console.log('done') 打出来了,能说明任务成功了吗?
不能,只说明代码跑到了那一行。看 echo $? 拿退出码,失败分支要 process.exitCode = 1,调度器和 CI 是按退出码判断成败的。
追问 5console.log 和 process.stdout.write 有啥区别?
console.log 会用 util.format 格式化参数、支持 %s/%d/%o 占位符,并且自动加换行;process.stdout.write 原样写字符串,不加换行。做进度条这种需要覆盖同一行的输出就用后者配合 \r。
# 模块概览
30 秒速记
console是全局对象,直接用;最常用的是console.log()和console.error()- 两条通道:
log→stdout,error→stderr,重定向和日志采集时能分开处理 Console类可以new出自己的实例,把输出写到文件或任意可写流- 浏览器的
console和Node的长得像,但Node版本质是往两个流里写字符串
console 模块就是 Node 自带的调试输出工具,全局可用,常用的就是 log 和 error 两个。 它和浏览器的 console 用法几乎一样,区别在于 Node 里输出最后是写到进程的标准输出和标准错误两个流里。log 和 error 看起来都是在终端打一行字,但走的是不同通道,部署时可以把错误单独重定向、单独告警。另外 console 本身是 Console 类的实例,你可以自己 new 一个,把调试信息写进文件,这在没有日志库的小脚本里很方便。
console模块提供了基础的调试功能。使用很简单,常用的API主要有 console.log()、console.error()。
此外,可以基于Console类,方便的扩展出自己的console实例,比如把调试信息打印到文件里,而部署输出在控制台上。
直接看例子。
面试官追问
追问 1console.log 和 console.error 在终端里看着一样,随便用行吗?
不行。2> err.log 重定向、PM2 的 error_file、Docker 日志驱动都能区分这两条流,错误写成 log 就进不了错误日志,告警也抓不到。
追问 2小脚本想把调试信息写进文件,又不想装日志库,怎么做?
const logger = new console.Console(fs.createWriteStream('./debug.log')),之后用 logger.log()。只传一个流时,error 也会写到这个流里。
追问 3PM2 跑的服务,console.error 的内容去哪了?
PM2 默认把 stdout 和 stderr 分开存,在 ~/.pm2/logs/ 下的 xxx-out.log 和 xxx-error.log。用 pm2 logs 能同时看两边。
追问 4能不能把全局 console.log 改写一下,统一加时间戳?
能,比如 const raw = console.log; console.log = (...a) => raw(new Date().toISOString(), ...a)。小项目临时这么干没问题,正经项目还是用日志库,改全局对象会影响所有依赖包的输出。
# 基础例子
30 秒速记
console.log普通信息 →stdout;console.error错误信息 →stderrconsole.info是log的别名,console.warn是error的别名,Node里输出没有区别- 多个参数用空格拼接;第一个参数里可以写占位符:
%s字符串、%d数字、%jJSON、%o对象 - 占位符多出来的参数接在后面,不够的占位符原样输出
Node 里 console 的打印方法其实就两类:写标准输出的 log/info,写标准错误的 error/warn。 在 Node 里 info 和 log 完全一样,warn 和 error 完全一样,不像浏览器控制台会用颜色和级别区分,所以选哪个更多是代码语义问题。参数可以一个个传,console.log('user', name) 会用空格拼起来;也可以用占位符,console.log('user: %s, age: %d', name, age),底层都是 util.format 处理的。
无特殊说明,日志都是默认打印到控制台。最常用的是console.log()、console.error()两个方法。
- console.log(msg):普通日志打印。
- console.error(msg):错误日志打印。
- console.info(msg):等同于console.log(msg)
- console.warn(msg):等同于console.error(msg)
例子如下:
console.log('log: hello');
console.log('log: hello', 'chyingp');
console.log('log: hello %s', 'chyingp');
