
日志里藏着所有线上问题的答案但面对几万行日志靠肉眼搜索不现实。正则表达式是把日志变成结构化数据的利器。本文从 Nginx 日志讲到自定义格式覆盖日志解析的核心技巧。Nginx access log 解析Nginx 默认的 access log 格式长这样192.168.1.1 - - [19/Aug/2026:13:55:36 0800] GET /api/users HTTP/1.1 200 1234 https://example.com Mozilla/5.0每个字段的含义$remote_addr - $remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent。基础解析constlogRegex/^(\S) - (\S) \[([^\]])\] (\S) (\S) (\S) (\d) (\d) ([^]*) ([^]*)/;constlog192.168.1.1 - - [19/Aug/2026:13:55:36 0800] GET /api/users HTTP/1.1 200 1234 https://example.com Mozilla/5.0;constmatchlog.match(logRegex);if(match){console.log(IP:,match[1]);// 192.168.1.1console.log(时间:,match[3]);// 19/Aug/2026:13:55:36 0800console.log(方法:,match[4]);// GETconsole.log(路径:,match[5]);// /api/usersconsole.log(协议:,match[6]);// HTTP/1.1console.log(状态码:,match[7]);// 200console.log(字节数:,match[8]);// 1234console.log(来源:,match[9]);// https://example.comconsole.log(UA:,match[10]);// Mozilla/5.0}命名捕获组用命名捕获组让代码可读性更好constlogRegex/^(?ip\S) - (?user\S) \[(?time[^\]])\] (?method\S) (?path\S) (?protocol[^]) (?status\d) (?bytes\d) (?referer[^]*) (?ua[^]*)/;constlog192.168.1.1 - - [19/Aug/2026:13:55:36 0800] GET /api/users HTTP/1.1 200 1234 https://example.com Mozilla/5.0;constmatchlog.match(logRegex);if(match){console.log(match.groups.ip);// 192.168.1.1console.log(match.groups.status);// 200console.log(match.groups.path);// /api/users}批量解析统计 QPSconstfsrequire(fs);constlogRegex/^(?ip\S) - \S \[(?time[^\]])\] (?method\S) (?path\S)[^]* (?status\d)/g;constlogs192.168.1.1 - - [19/Aug/2026:13:55:36 0800] GET /api/users HTTP/1.1 200 1234 https://example.com Mozilla/5.0;conststats{total:0,statusCount:{},pathCount:{},slowRequests:[],};letmatch;while((matchlogRegex.exec(logs))!null){stats.total;conststatusmatch.groups.status;stats.statusCount[status](stats.statusCount[status]||0)1;constpathmatch.groups.path;stats.pathCount[path](stats.pathCount[path]||0)1;}console.log(总请求数:${stats.total});console.log(状态码分布:,stats.statusCount);console.log(Top 10 路径:,Object.entries(stats.pathCount).sort((a,b)b[1]-a[1]).slice(0,10));Nginx error log 解析error log 格式2026/08/19 13:55:36 [error] 1234#0: *567 open() /var/www/favicon.ico failed (2: No such file or directory)consterrorRegex/^(?time[\d/: ]) \[(?level\w)\] (?pid\d)#(?tid\d): (?message.*)$/;consterrorLog2026/08/19 13:55:36 [error] 1234#0: *567 open() /var/www/favicon.ico failed (2: No such file or directory);constmatcherrorLog.match(errorRegex);if(match){console.log(时间:,match.groups.time);// 2026/08/19 13:55:36console.log(级别:,match.groups.level);// errorconsole.log(消息:,match.groups.message);// *567 open()...}自定义 JSON 日志解析很多项目使用 JSON 格式输出日志这种情况推荐用JSON.parse()而不是正则。但如果日志行混杂了 JSON 和非 JSON 内容正则可以用来提取 JSON 部分constmixedLog2026-08-19 13:55:36 INFO {userId:123,action:login,ip:192.168.1.1};// 提取 JSON 部分constjsonRegex/\{.*\}/;constjsonStrmixedLog.match(jsonRegex)?.[0];if(jsonStr){constdataJSON.parse(jsonStr);console.log(data.userId);// 123console.log(data.action);// login}正则 vs split 性能对比对于简单分隔符的日志split比正则快constcsvLog192.168.1.1,GET,/api/users,200,1234;// split 方式 — 简单快速 constt0_splitperformance.now();constpartscsvLog.split(,);constt1_splitperformance.now();console.log(split结果,parts);console.log(split()耗时${(t1_split-t0_split).toFixed(4)}ms);// 正则方式 — 适合复杂格式 constt0_matchperformance.now();constmatchcsvLog.match(/^([^,]),([^,]),([^,]),(\d),(\d)$/);constt1_matchperformance.now();console.log(match结果,match);console.log(match正则耗时${(t1_match-t0_match).toFixed(4)}ms);在大文本处理时10 万行以上优先用split。正则的优势在于处理不规则分隔和提取嵌套结构。const{performance}require(perf_hooks);// 模拟 10 万行日志constlogsArray(100000).fill(0).map(()192.168.1.1 - - [19/Aug/2026:13:55:36 0800] GET /api/users HTTP/1.1 200 1234);// 正则解析constregex/^(\S) - (\S) \[([^\]])\] (\S) (\S) (\S) (\d) (\d)/;performance.mark(regex-start);logs.forEach(logregex.exec(log));performance.mark(regex-end);// 实测约 150ms// split 解析performance.mark(split-start);logs.forEach(loglog.split( ));performance.mark(split-end);// 实测约 80ms处理多行堆栈日志Java 异常堆栈是多行的单行正则无法处理。用[\s\S]或sflagdotAll匹配跨行内容constmultilineLog2026-08-19 13:55:36 ERROR NullPointerException at com.example.UserService.getUser(UserService.java:45) at com.example.UserController.handleRequest(UserController.java:78) at java.base/java.lang.Thread.run(Thread.java:834);// 提取异常类型和堆栈conststackRegex/^(\S \S) (\w): (\w)\n([\s\S]*)$/;constmatchmultilineLog.match(stackRegex);if(match){console.log(时间:,match[1]);// 2026-08-19 13:55:36console.log(异常类型:,match[2]);// NullPointerExceptionconsole.log(堆栈:,match[4].trim());// at com.example...}在线实践写日志解析正则时需要用真实日志片段反复测试。我通常把一段 Nginx 日志粘贴到 盘子工具站正则测试工具 里输入正则后看高亮结果和分组信息——分组信息卡片会列出每个捕获组对应的字段内容IP、时间、方法、状态码等命名捕获组的groups对象直接可见调试日志解析正则非常方便。测试器支持g和sflag多行堆栈日志也能处理。日志解析的核心原则先确定日志格式规范再写正则。不要拿一段日志就开始猜正则——先问运维或后端同事拿格式定义写出来的正则才准确。