在线上定位1G日志文件中的异常信息时,我这样做合适吗

1G级线上日志文件 的异常定位系统性方案

一、快速定位流程

python 复制代码
import datetime
import random

def generate_springboot_log(file_name, file_size_gb):
    # 模拟Spring Boot日志内容
    log_levels = ["INFO", "DEBUG", "WARNING", "ERROR"]
    classes = ["com.example.controller.HomeController", "com.example.service.UserService", 
               "com.example.repository.UserRepository", "com.example.config.WebConfig"]
    messages = [
        "Accessing /home endpoint",
        "User not found with id: {}",
        "Invalid request parameters",
        "Database connection established",
        "Starting application",
        "Shutting down application"
    ]

    # 计算需要写入的行数
    file_size_bytes = file_size_gb * 1024 ** 3
    # 首先生成一条样本日志行,计算其长度
    sample_log = f"[2023-10-01 12:34:56.789] {random.choice(classes)} - {random.choice(log_levels)} : {random.choice(messages)}"
    average_line_length = len(sample_log) + 1  # 加1是因为每行最后还有一个换行符

    with open(file_name, "w", encoding="utf-8") as f:
        # 计算需要写入的行数
        lines_needed = int(file_size_bytes // average_line_length) + 1
        for _ in range(lines_needed):
            timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]  # 保留毫秒
            log_level = random.choice(log_levels)
            clazz = random.choice(classes)
            message = random.choice(messages)
            # 如果日志消息中包含格式化的参数,添加示例数值
            if "{}" in message:
                message = message.format(random.randint(1, 100))
            log_line = f"[{timestamp}] {clazz} - {log_level} : {message}\n"
            f.write(log_line)

generate_springboot_log("springboot_log_1gb.log", 1)

思路:利用时间过滤数据,再利用管道流过滤输出到单独的文件,最后在小文件里面操作就行。

shell 复制代码
[2025-03-16 21:14:11.476] com.example.repository.UserRepository - INFO : Starting application
[2025-03-16 21:14:11.476] com.example.service.UserService - WARNING : User not found with id: 76
[2025-03-16 21:14:11.476] com.example.service.UserService - INFO : Shutting down application
[2025-03-16 21:14:11.476] com.example.service.UserService - ERROR : Starting application
[2025-03-16 21:14:11.476] com.example.config.WebConfig - INFO : User not found with id: 83
[2025-03-16 21:14:11.476] com.example.service.UserService - WARNING : Accessing /home endpoint
[2025-03-16 21:14:11.476] com.example.repository.UserRepository - ERROR : Accessing /home endpoint
[2025-03-16 21:14:11.476] com.example.config.WebConfig - INFO : User not found with id: 57
[2025-03-16 21:14:11.476] com.example.repository.UserRepository - INFO : User not found with id: 89
[2025-03-16 21:14:11.476] com.example.controller.HomeController - ERROR : Database connection established
[2025-03-16 21:14:11.476] com.example.config.WebConfig - ERROR : Accessing /home endpoint
[2025-03-16 21:14:11.476] com.example.controller.HomeController - INFO : Shutting down application
[2025-03-16 21:14:11.476] com.example.repository.UserRepository - INFO : Database connection established
[2025-03-16 21:14:11.476] com.example.config.WebConfig - DEBUG : User not found with id: 16
[2025-03-16 21:14:11.476] com.example.controller.HomeController - WARNING : Database connection established
[2025-03-16 21:14:11.476] com.example.controller.HomeController - ERROR : Accessing /home endpoint
[2025-03-16 21:14:11.476] com.example.controller.HomeController - WARNING : Invalid request parameters
[2025-03-16 21:14:11.476] com.example.repository.UserRepository - WARNING : Invalid request parameters
[2025-03-16 21:14:11.476] com.example.controller.HomeController - WARNING : Accessing /home endpoint
[2025-03-16 21:14:11.476] com.example.service.UserService - DEBUG : Starting application
[2025-03-16 21:14:11.476] com.example.config.WebConfig - ERROR : Starting application
[2025-03-16 21:14:11.476] com.example.config.WebConfig - WARNING : User not found with id: 42
shell 复制代码
awk -v start="[2025-03-16 21:14:11.476]" -v end="[2025-03-16 21:14:11.477]" '($0) >= start && ($0) < end {print}' springboot_log_1gb.log | grep 'ERROR' -> test.log
html 复制代码
($0) >= start && ($0) < end {print}:对每一行内容 $0 进行比较。如果当前行的时间在 start 和 end 之间(包含等于的情况),则打印该行,(测试($0) <= end {print}得到的结果不包括等于)

二、高效处理大文件的技巧

分割和并行处理 : 使用<font style="color:rgb(37, 43, 58);">split</font>将日志文件分割为小文件,分别处理,再合并结果。

shell 复制代码
split -l 100000 springboot_log_1gb.log chunk_

-l 100000:每个小文件包含1000行。

chunk_ :前缀,生成 chunk_aa、chunk_ab 等文件。

shell 复制代码
rm -rf chunk_*
shell 复制代码
split -b 200M --verbose springboot_log_1gb.log split_access_


-b 200M:每个分片200MB
--verbose:显示分割进度
输出文件命名示例:split_access_aa,split_access_ab
shell 复制代码
split -l 5000 -d --additional-suffix=.log springboot_log_1gb.log "split_$(date +%Y%m%d)_"

-additional-suffix:添加文件扩展名
$(date +%Y%m%d):动态生成日期前缀

split_20250316_991230.log

超大文件分块处理:

shell 复制代码
split -l 100000 app.log chunk_  # 每10万行切割文件
find . -name "chunk_*" | xargs -P 4 grep "ERROR" >> errors.txt

优势:避免单文件内存溢出问题
html 复制代码
find . -name "chunk_*":查找当前目录(.)及其子目录中所有文件名以 "chunk_" 开头的文件。
* 是通配符,表示任意字符序列。

xargs -P 4:使用 xargs 将输入的文件名作为参数传递给后续命令。-P 4 表示同时运行 4 个进程,提高处理速度。

grep "ERROR":在每个文件中搜索包含 "ERROR" 的行。grep 是一个强大的文本搜索工具,支持正则表达式。

>> errors.txt:将 grep 找到的所有包含 "ERROR" 的行追加到 errors.txt 文件中。
如果 errors.txt 不存在,会创建一个新文件。
shell 复制代码
find . -name "chunk_*" | xargs rm


动态追加写入:

shell 复制代码
tail -f app.log | grep --line-buffered "ERROR" | xargs -I{} sh -c 'echo {} >> errors.txt'

-line-buffered:强制逐行输出缓冲

xargs:将标准输入转换为命令行参数。
-I{}:指定{}作为每行输入的占位符。
sh -c:执行一个命令。
echo {} >> errors.txt:将每一行ERROR日志追加到errors.txt文件中。


避免内存问题:

shell 复制代码
grep "ERROR" logfile.log | awk '{print $0}'  # 避免 `cat logfile.log | ...`
方法 10GB日志耗时 CPU占用 内存峰值
原生grep 22分钟 98% 1.8GB
xargs单线程 19分钟 25% 800MB
xargs并行(-P 8) 4分30秒 620% 2.1GB

指标来源于网络,仅供参考

三、实时监控与自动化

shell 复制代码
tail -f /path/to/logfile.log | grep "ERROR"  # 实时显示新增的ERROR日志

异常复现的时候可以使用
shell 复制代码
tail -f logfile.log | awk '/ERROR/ {print strftime("%Y-%m-%d %H:%M:%S"), $0}'  # 添加时间戳
相关推荐
yangang1852 小时前
linuxbash原理
linux·运维·服务器
一一Null2 小时前
关于手机取证中逻辑采集与系统备份的差异
服务器·网络·智能手机
Hanson Huang2 小时前
【数据结构】堆排序详细图解
java·数据结构·排序算法·堆排序
yangshuo12812 小时前
如何在服务器上搭建mail服务器邮件服务器
运维·服务器
GOTXX2 小时前
【Qt】Qt Creator开发基础:项目创建、界面解析与核心概念入门
开发语言·数据库·c++·qt·图形渲染·图形化界面·qt新手入门
猿小喵2 小时前
记录一次TDSQL网关夯住故障
运维·数据库·mysql
电商api接口开发3 小时前
如何在C#中使用LINQ对数据库进行查询操作?
数据库·c#·linq
路在脚下@3 小时前
Redis实现分布式定时任务
java·redis
xrkhy3 小时前
idea的快捷键使用以及相关设置
java·ide·intellij-idea
巨龙之路3 小时前
Lua中的元表
java·开发语言·lua