如何在 JavaScript 应用中进行日志记录和错误监控,以确保系统的可观测性?

各位靓仔靓女,晚上好!我是你们的老朋友,今天咱们来聊聊JavaScript应用中的日志记录和错误监控,这可是保证系统“耳聪目明”的关键所在!

想象一下,你的代码就像一辆在高速公路上狂奔的跑车。你肯定想知道:

  • 它跑得怎么样?速度多少?油耗如何?
  • 有没有遇到坑坑洼洼?哪个轮胎压力不足?
  • 万一抛锚了,得知道在哪儿抛的,以及怎么修!

日志记录和错误监控就是这辆跑车的车载电脑和维修手册,能帮你实时掌握情况,及时发现问题,并迅速修复。

第一部分:日志记录——给你的代码安个“录音机”

日志记录,简单来说,就是在代码的关键节点,记录一些信息,方便我们事后回溯和分析。

1. console.log()?别逗了!

console.log() 确实是最简单粗暴的日志记录方式,但它只适合调试阶段。一旦上线,满屏的 console.log() 会让你的控制台变成垃圾场,而且还会影响性能。

// 别这么干!
function calculate(a, b) {
  console.log("开始计算...");
  console.log("a 的值是:", a);
  console.log("b 的值是:", b);
  let result = a + b;
  console.log("结果是:", result);
  return result;
}

2. 分级日志:给日志穿上“制服”

更好的做法是使用分级日志,根据信息的严重程度,给日志穿上不同的“制服”,方便筛选和处理。常见的日志级别有:

日志级别 描述 颜色(只是举例,实际应用中可能不同)
debug 用于开发阶段的详细信息,例如变量的值、函数的调用顺序等。上线后一般关闭。 灰色
info 一般性信息,例如系统启动、用户登录等。 绿色
warn 警告信息,表示可能存在潜在的问题,但不影响系统正常运行。例如配置项缺失、API 调用频率过高等。 黄色
error 错误信息,表示系统出现了问题,可能导致部分功能无法正常使用。例如数据库连接失败、API 调用返回错误等。 红色
fatal 致命错误,表示系统出现了严重问题,无法继续运行。例如内存溢出、文件系统损坏等。 亮红色

你可以自己实现一个简单的日志记录器,或者使用现成的库,比如 winstonlog4js 等。

3. 手写一个简易日志记录器

const logLevels = {
  debug: 0,
  info: 1,
  warn: 2,
  error: 3,
  fatal: 4,
};

const logLevel = logLevels.info; // 设置日志级别

function log(level, message, ...args) {
  if (logLevels[level] >= logLevel) {
    const timestamp = new Date().toISOString();
    console.log(`[${timestamp}] [${level.toUpperCase()}] ${message}`, ...args);
  }
}

const logger = {
  debug: (message, ...args) => log('debug', message, ...args),
  info: (message, ...args) => log('info', message, ...args),
  warn: (message, ...args) => log('warn', message, ...args),
  error: (message, ...args) => log('error', message, ...args),
  fatal: (message, ...args) => log('fatal', message, ...args),
};

// 使用
logger.info("服务器启动...");
logger.debug("变量 x 的值是:", 10); // 这条日志不会显示,因为日志级别是 info
logger.warn("配置项 'apiKey' 缺失");
logger.error("API 调用失败");
logger.fatal("内存溢出!");

这个简易的日志记录器可以根据设置的日志级别,决定是否输出日志。上线后,你可以将日志级别设置为 warnerror,只记录重要的信息,避免噪音。

4. 使用 winston 库

winston 是一个功能强大的日志记录库,支持多种输出方式,例如控制台、文件、数据库等。

首先,安装 winston

npm install winston

然后,使用 winston

const winston = require('winston');

const logger = winston.createLogger({
  level: 'info', // 设置日志级别
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.printf(({ timestamp, level, message }) => {
      return `${timestamp} ${level}: ${message}`;
    })
  ),
  transports: [
    new winston.transports.Console(), // 输出到控制台
    new winston.transports.File({ filename: 'error.log', level: 'error' }), // 输出错误日志到文件
    new winston.transports.File({ filename: 'combined.log' }), // 输出所有日志到文件
  ],
});

// 使用
logger.info("服务器启动...");
logger.debug("变量 x 的值是:", 10); // 这条日志不会显示,因为日志级别是 info
logger.warn("配置项 'apiKey' 缺失");
logger.error("API 调用失败");
logger.fatal("内存溢出!");

winston 提供了更多的配置选项,例如日志格式、输出目标等,可以根据实际需求进行调整。

5. 日志格式:让日志更易读

日志格式很重要,清晰的日志格式可以让你更快地找到问题。常见的日志格式包括:

  • 文本格式: 简单易懂,但不利于机器解析。
  • JSON 格式: 方便机器解析,适合存储到数据库或发送到日志分析平台。

winston 支持自定义日志格式,你可以根据自己的需求选择合适的格式。

6. 日志存储:把日志“存起来”

日志不能只输出到控制台,还需要存储起来,方便后续分析。常见的日志存储方式包括:

  • 文件: 简单易用,但不利于大规模存储和查询。
  • 数据库: 适合大规模存储和查询,但需要维护数据库。
  • 日志管理平台: 提供专业的日志存储、分析和可视化功能,例如 ELK Stack(Elasticsearch、Logstash、Kibana)、Splunk 等。

选择哪种存储方式,取决于你的应用规模和预算。

第二部分:错误监控——给你的代码装个“警报器”

错误监控,就是在代码出错时,及时发出警报,让你第一时间知道问题所在。

1. try…catch:抓住“小错误”

try...catch 是最基本的错误处理方式,可以捕获代码块中发生的错误。

try {
  // 可能会出错的代码
  let result = 10 / 0;
  console.log(result);
} catch (error) {
  // 处理错误
  console.error("发生错误:", error.message);
}

try...catch 只能捕获同步代码中的错误,无法捕获异步代码中的错误。

2. Promise 的 reject:处理异步错误

Promise 提供了 reject 方法,可以处理异步操作中的错误。

function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // 模拟 API 调用失败
      reject(new Error("API 调用失败"));
    }, 1000);
  });
}

fetchData()
  .then(data => {
    console.log("数据:", data);
  })
  .catch(error => {
    console.error("发生错误:", error.message);
  });

3. window.onerror:捕获全局错误

window.onerror 可以捕获全局未处理的错误,包括语法错误、运行时错误等。

window.onerror = function(message, source, lineno, colno, error) {
  console.error("全局错误:", message, source, lineno, colno, error);
  // 可以将错误信息发送到服务器
  return true; // 阻止浏览器默认的错误处理
};

// 故意制造一个错误
console.log(undefinedVariable.toString());

window.onerror 只能捕获未处理的错误,无法捕获 try...catch 已经处理过的错误。

4. 错误监控平台:专业的“报警器”

try...catchPromise.rejectwindow.onerror 只能捕获错误,但无法提供完整的错误信息,例如用户环境、堆栈信息等。

专业的错误监控平台,例如 Sentry、Bugsnag、Raygun 等,可以提供更全面的错误监控功能:

  • 自动捕获错误: 自动捕获未处理的错误,无需手动编写 try...catch
  • 提供详细的错误信息: 包括用户环境、堆栈信息、浏览器信息等。
  • 错误分组和去重: 将类似的错误分组,避免重复报警。
  • 实时报警: 在错误发生时,立即发送邮件、短信等通知。
  • 错误分析: 提供错误趋势分析、影响用户数等统计信息。

5. 使用 Sentry 监控错误

Sentry 是一个流行的错误监控平台,提供了免费和付费版本。

首先,注册 Sentry 账号,并创建一个项目。

然后,安装 Sentry 的 JavaScript SDK:

npm install @sentry/browser @sentry/tracing

然后,初始化 Sentry:

import * as Sentry from "@sentry/browser";
import { Integrations } from "@sentry/tracing";

Sentry.init({
  dsn: "YOUR_SENTRY_DSN", // 替换为你的 Sentry DSN
  integrations: [
    new Integrations.BrowserTracing(),
  ],
  // Set tracesSampleRate to 1.0 to capture 100%
  // of transactions for performance monitoring.
  // We recommend adjusting this value in production
  tracesSampleRate: 0.1,
  release: "[email protected]", // 可选,设置版本号
  environment: "production", // 可选,设置环境
});

// 故意制造一个错误
try {
  console.log(undefinedVariable.toString());
} catch (error) {
  Sentry.captureException(error); // 手动捕获错误
}

Sentry 会自动捕获未处理的错误,并将错误信息发送到 Sentry 平台。你也可以使用 Sentry.captureException() 手动捕获错误。

第三部分:最佳实践——让你的系统更“健康”

  • 不要记录敏感信息: 例如用户密码、银行卡号等。
  • 设置合适的日志级别: 上线后,将日志级别设置为 warnerror,避免噪音。
  • 定期清理日志: 避免日志文件过大,占用磁盘空间。
  • 监控关键指标: 例如 CPU 使用率、内存使用率、响应时间等。
  • 设置报警阈值: 当关键指标超过阈值时,及时发出警报。
  • 定期分析日志和错误信息: 找出潜在的问题,并及时修复。
  • 使用 source map: 在生产环境中,使用 source map 可以将压缩后的代码还原为原始代码,方便调试。

总结:

日志记录和错误监控是保证系统可观测性的重要手段。通过合理的日志记录和错误监控,你可以及时发现问题,并迅速修复,让你的系统更加“健康”。

希望今天的讲座对大家有所帮助!祝大家的代码永远没有 Bug!(手动狗头) 谢谢大家!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注