Golang新手如何做日志分析_Go语言实战案例

用 bufio.Scanner 流式读取大日志文件可避免内存溢出;默认按行切分,每次仅加载一行,需设置 scanner.Buffer 防止超长行报错。

直接上手做日志分析,不需要先搭整套可观测体系——用 os.Open + bufio.Scanner 读文件、strings.Containsregexp 匹配关键信息、再用 map 统计,三步就能跑通一个真实可用的分析脚本。

怎么读大日志文件不卡死?

新手常把整个日志文件 os.ReadFile 读进内存,一碰几百 MB 的 /var/log/syslog 就 panic:out of memory。Go 原生没“流式读取”封装,但 bufio.Scanner 就是干这个的。

  • 它默认按行切分,每次只加载一行到内存,适合处理任意大小的日志文件
  • 注意设置缓冲区上限,避免超长日志行触发 scanner.Err() == bufio.ErrTooLong,可加 scanner.Buffer(make([]byte, 4096), 1
  • 别用 for line := range scanner.Lines(那是 tail 库的写法),标准库 Scanner 没这个字段

匹配关键字该用 strings 还是 regexp?

看场景:查 "ERROR""timeout" 这种固定字符串,strings.Contains 快 5–10 倍,且无编译开销;要抽 ID=12345 或解析 Nginx 时间戳,必须用 regexp

  • strings.ToLower(line) 再搜,比正则忽略大小写更轻量,适合简单 case
  • 正则别在循环里反复 regexp.Compile,提*局变量或用 sync.Once 初始化
  • 容器 JSON 日志里 "log":"panic: connection refused" 这种嵌套内容,得先 json.Unmarshal 解一层,再对 log 字段做字符串处理

统计结果不准?大概率是时间/编码/换行踩了坑

分析结果和 grep -c 对不上?常见三个隐形雷:

  • 日志含中文或特殊符号时,文件可能是 GBK 编码,而 Go 默认按 UTF-8 解析——scanner.Text() 返回乱码,匹配必然失败(Linux 系统日志一般为 UTF-8,但某些嵌入式设备或旧服务可能不是)
  • time.Parse 解析 [05/Feb/2025:10:23:45 +0000] 这类格式,Layout 必须严格匹配:对应的是 "02/Jan/2006:15:04:05 -0700",错一位就返回零值时间
  • scanner.Scan() 遇到 CR/LF 不一致(Windows vs Linux)可能跳行,建议统一用 strings.Trim

    Right(line, "\r\n")
    清理
package main

import (
	"bufio"
	"fmt"
	"os"
	"regexp"
	"strings"
)

func main() {
	file, _ := os.Open("/var/log/syslog")
	defer file.Close()

	scanner := bufio.NewScanner(file)
	scanner.Buffer(make([]byte, 4096), 1<<20) // 防超长行

	// 预编译正则:提取 "kernel:" 开头的内核消息
	kernelRe := regexp.MustCompile(`^kernel: \[.*?\] (.+)`)

	errorCount := 0
	kernelMsgs := []string{}

	for scanner.Scan() {
		line := strings.TrimRight(scanner.Text(), "\r\n")
		if strings.Contains(strings.ToLower(line), "error") {
			errorCount++
		}
		if matches := kernelRe.FindStringSubmatch([]byte(line)); matches != nil {
			kernelMsgs = append(kernelMsgs, string(matches[1]))
		}
	}

	fmt.Printf("Error count: %d\n", errorCount)
	fmt.Printf("Kernel messages: %d\n", len(kernelMsgs))
}

真正难的从来不是写代码,而是确认你读的那行日志,确实是原始日志里“那一行”——比如 Docker 容器日志是 JSON 行,tail -f 看着是纯文本,实际每行开头有时间戳和容器 ID 字段,直接字符串匹配会漏掉真实错误内容。