【console调试与日志打印】+【前端编码规范场景】:从日志分级到结构化输出,彻底搞懂高效调试与干净log的最佳实践,避开垃圾日志与线上泄漏安全坑!

📑 文章目录
- 一、引言:日志不止是「打出来看看」
- 二、为什么要规范日志?
- [2.1 不规范的日志会带来什么?](#2.1 不规范的日志会带来什么?)
- [2.2 规范的日志能解决什么?](#2.2 规范的日志能解决什么?)
- [三、console 家族:谁该在什么时候用?](#三、console 家族:谁该在什么时候用?)
- [3.1 常用方法一览](#3.1 常用方法一览)
- [3.2 用对方法,事半功倍](#3.2 用对方法,事半功倍)
- [四、有效日志 vs 垃圾日志](#四、有效日志 vs 垃圾日志)
- [4.1 什么是「垃圾日志」?](#4.1 什么是「垃圾日志」?)
- [4.2 什么是「有效日志」?](#4.2 什么是「有效日志」?)
- [4.3 对比示例](#4.3 对比示例)
- 五、实战规范与最佳实践
- [5.1 统一日志前缀(强烈推荐)](#5.1 统一日志前缀(强烈推荐))
- [5.2 结构化输出:对象 + 说明](#5.2 结构化输出:对象 + 说明)
- [5.3 按环境开关日志(生产环境必备)](#5.3 按环境开关日志(生产环境必备))
- [5.4 出错时带上关键上下文](#5.4 出错时带上关键上下文)
- [5.5 用 time/timeEnd 做简单性能排查](#5.5 用 time/timeEnd 做简单性能排查)
- 六、常见踩坑场景
- [6.1 对象被「改过」之后再打印](#6.1 对象被「改过」之后再打印)
- [6.2 异步里的 log 顺序「乱了」](#6.2 异步里的 log 顺序「乱了」)
- [6.3 直接 log 大对象/长数组](#6.3 直接 log 大对象/长数组)
- [6.4 生产环境泄漏敏感信息](#6.4 生产环境泄漏敏感信息)
- 七、调试技巧补充
- [7.1 用 assert 做「条件不满足时报错」](#7.1 用 assert 做「条件不满足时报错」)
- [7.2 用 trace 查调用链](#7.2 用 trace 查调用链)
- [7.3 Chrome 开发者工具中的技巧](#7.3 Chrome 开发者工具中的技巧)
- [八、一个可复用的 Logger 示例](#八、一个可复用的 Logger 示例)
- 九、总结:记住这几条就够了
- [🔍 系列模块导航](#🔍 系列模块导航)
同学们好,我是 Eugene(尤金),一名多年中后台前端开发工程师。
(Eugene 发音 /juːˈdʒiːn/,大家怎么顺口怎么叫就好)
很多前端开发者都会遇到一个瓶颈:
代码能跑,但不够规范;功能能实现,但维护起来特别痛苦;一个人写没问题,一到团队协作就各种混乱、踩坑、返工。
想写出干净、优雅、可维护 的专业代码,靠的不是天赋,而是体系化的规范 + 真实实战经验。
这一系列《前端规范实战》,我会用大白话 + 真实业务场景,不讲玄学、不堆理论,只分享能直接落地的规范、标准与避坑指南。
帮你从「会写代码」真正升级为「会写优质、可维护、团队级别的代码」。
一、引言:日志不止是「打出来看看」
不少同学写代码时习惯随手 console.log(xxx),没问题就删掉,有问题就继续加。这种写法短期能跑通,但会给长期维护和线上排查带来麻烦。
本文围绕:日常怎么写 log、怎么选调试方式、常见坑在哪里,用「基础扫盲 + 实战规范」的方式,帮你建立一套可复用的调试与打印习惯。
[⬆ 返回目录](#⬆ 返回目录)
二、为什么要规范日志?
2.1 不规范的日志会带来什么?
- 大量无意义输出,淹没真正有用的信息
- 忘记删除或注释,泄漏敏感数据
- 到处乱打,难以按流程理解
- 线上环境仍疯狂输出,影响性能
- 多个 log 混在一起,不知道是谁打的
[⬆ 返回目录](#⬆ 返回目录)
2.2 规范的日志能解决什么?
- 快速定位是哪一步出问题
- 信息分级,重要信息容易一眼看到
- 方便按需开关,生产环境不乱打
- 能沉淀成团队可复用的调试规范
[⬆ 返回目录](#⬆ 返回目录)
三、console 家族:谁该在什么时候用?
3.1 常用方法一览
| 方法 | 用途 | 典型场景 |
|---|---|---|
console.log() |
普通信息 | 变量、流程节点 |
console.info() |
提示类信息 | 启动、初始化、重要节点 |
console.warn() |
警告 | 可工作但有隐患 |
console.error() |
错误 | 异常、失败、需要排查 |
console.debug() |
调试细节 | 开发期深度排查 |
console.table() |
表格展示 | 数组、对象列表 |
console.group() |
分组折叠 | 多步流程、复杂输出 |
console.trace() |
调用栈 | 找「谁在调用我」 |
console.time() / timeEnd() |
计时 | 性能分析 |
console.assert() |
断言 | 不满足条件时报错 |
先记住:log 默认、info 重要提示、warn 警告、error 错误,这是最基本的分级。
[⬆ 返回目录](#⬆ 返回目录)
3.2 用对方法,事半功倍
示例 1:普通 log vs warn vs error
js
// ❌ 混乱:都用 log
console.log('用户未登录');
console.log('请求失败了');
console.log('找到了 10 条数据');
// ✅ 清晰:按严重程度区分
console.warn('用户未登录,将跳转登录页');
console.error('请求失败', { url: '/api/user', status: 500 });
console.info('查询成功,共 10 条数据');
示例 2:用 table 看结构
js
const users = [
{ id: 1, name: '张三', role: 'admin' },
{ id: 2, name: '李四', role: 'user' },
];
// ❌ log 一大坨对象,不好看
console.log(users);
// ✅ table 一眼看出结构
console.table(users);
示例 3:用 group 整理复杂流程
js
console.group('📦 订单提交流程');
console.log('1. 校验表单');
console.log('2. 调用提交接口');
console.log('3. 更新本地状态');
console.group('接口返回');
console.log('orderId:', 'ORD-001');
console.log('status:', 'success');
console.groupEnd();
console.groupEnd();
[⬆ 返回目录](#⬆ 返回目录)
四、有效日志 vs 垃圾日志
4.1 什么是「垃圾日志」?
- 没有上下文,看不出是哪个模块、哪一步
- 只打一个变量名,不说明含义
- 到处散落,删都不知道删哪
- 永远不删,长期留在代码里
[⬆ 返回目录](#⬆ 返回目录)
4.2 什么是「有效日志」?
- 有前缀/标签,方便搜索和过滤
- 有描述 + 数据,含义明确
- 集中在关键流程,而不是每个角落
- 能通过环境变量或开关关闭,不污染生产
[⬆ 返回目录](#⬆ 返回目录)
4.3 对比示例
js
// ❌ 垃圾日志:无上下文、无说明
console.log(data);
console.log(res);
console.log(err);
console.log('here');
console.log('111');
// ✅ 有效日志:有标签、有说明、有结构
const LOG_TAG = '[UserService]';
console.info(`${LOG_TAG} 开始获取用户列表`);
console.info(`${LOG_TAG} 请求参数`, { page: 1, size: 10 });
console.info(`${LOG_TAG} 响应成功`, { total: 100, list: data });
console.error(`${LOG_TAG} 请求失败`, { err, url: '/api/users' });
[⬆ 返回目录](#⬆ 返回目录)
五、实战规范与最佳实践
5.1 统一日志前缀(强烈推荐)
用固定前缀区分模块,便于全局搜索和过滤。
js
// 按模块定义前缀
const LOG = {
user: '[User]',
order: '[Order]',
payment: '[Payment]',
};
// 使用
function fetchUser(id) {
console.info(`${LOG.user} 开始获取用户`, { id });
// ...
console.info(`${LOG.user} 获取成功`, user);
}
[⬆ 返回目录](#⬆ 返回目录)
5.2 结构化输出:对象 + 说明
推荐格式:说明 + 数据对象,方便在控制台展开、复制。
js
// ❌ 扁平化输出,难以复制
console.log('用户信息:', user.name, user.id, user.email);
// ✅ 结构化输出
console.info('用户信息', {
id: user.id,
name: user.name,
email: user.email,
});
[⬆ 返回目录](#⬆ 返回目录)
5.3 按环境开关日志(生产环境必备)
生产环境应尽量减少或关闭调试日志。
js
// 方式一:环境变量
const isDev = import.meta.env?.DEV ?? process.env.NODE_ENV === 'development';
function devLog(...args) {
if (isDev) {
console.log('[DEV]', ...args);
}
}
// 方式二:带级别的 Logger
const LOG_LEVEL = import.meta.env?.DEV ? 'debug' : 'error';
const levels = { debug: 0, info: 1, warn: 2, error: 3 };
function log(level, ...args) {
if (levels[level] >= levels[LOG_LEVEL]) {
console[level](...args);
}
}
// 使用
log('debug', '[User] 详细调试信息'); // 生产环境不会输出
log('error', '[User] 请求失败', err); // 任何环境都会输出
[⬆ 返回目录](#⬆ 返回目录)
5.4 出错时带上关键上下文
js
try {
const res = await fetchUser(userId);
return res;
} catch (err) {
// ❌ 只打 err,缺少上下文
console.error(err);
// ✅ 带上接口、参数、用户等关键信息
console.error('[User] 获取用户失败', {
userId,
api: '/api/user',
err: err.message,
stack: err.stack,
});
throw err;
}
[⬆ 返回目录](#⬆ 返回目录)
5.5 用 time/timeEnd 做简单性能排查
js
console.time('fetchUserList');
const users = await fetchUserList();
console.timeEnd('fetchUserList');
// 控制台会输出:fetchUserList: 234.5ms
[⬆ 返回目录](#⬆ 返回目录)
六、常见踩坑场景
6.1 对象被「改过」之后再打印
js
const obj = { a: 1 };
console.log(obj); // 点开看到的是 a: 2
obj.a = 2;
原因:控制台是延迟展开的,点开时看到的是「当前」值。
解决:需要看「打印那一刻」的值,用 JSON 或快照。
js
console.log(JSON.parse(JSON.stringify(obj)));
// 或者
console.log({ ...obj });
[⬆ 返回目录](#⬆ 返回目录)
6.2 异步里的 log 顺序「乱了」
js
console.log('1');
setTimeout(() => console.log('2'), 0);
console.log('3');
// 输出:1 3 2
这不是 bug,而是事件循环的执行顺序。排查时注意「同步先于异步」。
[⬆ 返回目录](#⬆ 返回目录)
6.3 直接 log 大对象/长数组
js
const list = Array(10000).fill({ name: 'test' });
console.log(list); // 卡顿、难展开
建议:只打关键信息,或打长度 + 前几条。
js
console.info('列表长度', list.length, '前 3 条', list.slice(0, 3));
[⬆ 返回目录](#⬆ 返回目录)
6.4 生产环境泄漏敏感信息
js
console.log('用户 token:', token); // ❌ 危险
console.log('请求体', { password: pwd }); // ❌ 危险
生产环境务必不要打印 token、密码等敏感字段,可以考虑在打包时移除 console 或使用专门的日志库做脱敏。
[⬆ 返回目录](#⬆ 返回目录)
七、调试技巧补充
7.1 用 assert 做「条件不满足时报错」
js
function divide(a, b) {
console.assert(b !== 0, '除数不能为 0', { a, b });
return a / b;
}
[⬆ 返回目录](#⬆ 返回目录)
7.2 用 trace 查调用链
js
function inner() {
console.trace('谁在调用我?');
}
function outer() {
inner();
}
outer();
// 会打印完整调用栈
[⬆ 返回目录](#⬆ 返回目录)
7.3 Chrome 开发者工具中的技巧
- 右键 log →「Store as global variable」:把对象存到临时变量,方便在控制台继续操作
- 条件断点:在断点上加条件,只在满足时停下
- 使用
debugger:需要时在代码里加debugger,会自动在 DevTools 打开时停住(生产环境注意删除)
[⬆ 返回目录](#⬆ 返回目录)
八、一个可复用的 Logger 示例
下面是一个可直接复用的简易 Logger,支持分级、前缀、生产环境控制:
js
/**
* 简易 Logger:支持分级、前缀、生产环境开关
*/
const LOG_LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
const isDev = import.meta.env?.DEV ?? process.env.NODE_ENV === 'development';
const currentLevel = isDev ? 'debug' : 'warn';
function createLogger(prefix) {
return {
debug(...args) {
if (LOG_LEVELS[currentLevel] <= LOG_LEVELS.debug) {
console.debug(`[${prefix}]`, ...args);
}
},
info(...args) {
if (LOG_LEVELS[currentLevel] <= LOG_LEVELS.info) {
console.info(`[${prefix}]`, ...args);
}
},
warn(...args) {
if (LOG_LEVELS[currentLevel] <= LOG_LEVELS.warn) {
console.warn(`[${prefix}]`, ...args);
}
},
error(...args) {
console.error(`[${prefix}]`, ...args); // error 始终输出
},
};
}
// 使用
const log = createLogger('UserService');
log.debug('调试信息', data); // 仅开发环境
log.info('请求成功', res);
log.error('请求失败', err);
[⬆ 返回目录](#⬆ 返回目录)
九、总结:记住这几条就够了
- 分清 log / info / warn / error,按严重程度选择方法。
- 统一前缀,方便搜索和过滤。
- 输出结构化数据,用对象,少用零散字符串。
- 生产环境关闭或降级 debug,避免性能和安全问题。
- 出错时带上上下文(接口、参数、用户等)。
- 避免垃圾日志:不打的就不打,打的要有用、好删、好找。
调试和日志规范不是「玄学」,而是日常习惯。按上述方式坚持一段时间,你会发现定位问题更快、代码也更干净。
[⬆ 返回目录](#⬆ 返回目录)
🔍 系列模块导航
📝 编码语法规范
《一、JS/TS 编码规范实战:Vue 场景变量 / 函数 / 类型标注避坑|编码语法规范篇》
《二、async/await 规范:错误处理 / 避免嵌套 / 防重复请求,异步代码更优雅|编码语法规范篇》
《三、前端 utils 工具函数规范:拆分 / 命名 / 复用全指南,避开全局污染等高频坑|编码语法规范篇》
《四、前端 console 日志规范实战:高效调试 / 垃圾 log 清理与线上安全避坑|编码语法规范篇》
《五、JS 函数单一职责实战:拆分逻辑 / 告别面条代码,写出可维护团队级代码|编码语法规范篇》
《六、TypeScript+Vue 实战:告别 any 滥用,统一接口 / Props / 表单类型,实现类型安全|编码语法规范篇》
👉 跟着系列慢慢学,把技术功底扎扎实实地打牢~
📚 系列总览
前端规范实战系列目前正在持续更新中,当该系列完结之后我会整理出一篇《前端规范实战系列全系列目录导航》,届时会附上文章简介以及跳转链接,方便同学们按顺序体系化的学习~
更新中,敬请期待~
[⬆ 返回目录](#⬆ 返回目录)
技术成长,从来不是比谁写得快,而是比谁写得稳、规范、可维护。
哪怕每次只吃透一条规范,长期下来,差距会非常明显。
后续我会持续更新前端规范、工程化、可维护代码相关实战干货,帮你告别面条代码、维护噩梦,在开发与面试中更有底气。
觉得有用欢迎 点赞 + 收藏 + 关注,不错过每一篇实战内容。
我是 Eugene,与你一起写规范、写优质代码,我们下篇干货见~