如何用awk按月份拆分含多行日志的未轮转服务日志文件?
按月份拆分含多行内容的服务日志(支持自动压缩)
问题场景
我有一份从未轮转的服务日志文件,需要按月份拆分为独立文件。多数日志行以[Unix时间戳]开头,但存在dig输出这类跨多行的日志信息需要完整保留;且多行日志后的下一条带时间戳的日志可能不属于同一月份。
最初尝试用awk按每月首尾时间戳筛选行,但无法正确处理多行日志——无时间戳的多行内容会被错误过滤。试过按行号处理也没解决问题,最终通过调整awk脚本实现了需求:抓取所有匹配时间范围的行及后续非时间戳行,直到遇到首个超过当月最大时间戳的带时间戳行,同时自动将拆分后的文件gzip压缩,文件名统一为unbound-YYYY-MM格式。
解决方案:awk脚本
BEGIN { # 替换为你的日志实际覆盖的年份范围 start_year = 2023 end_year = 2024 # 预计算每个月的起始/结束时间戳(UTC时区),映射到对应文件名 for (year = start_year; year <= end_year; year++) { for (month = 1; month <= 12; month++) { start_ts = mktime(year " " month " 1 0 0 0") end_ts = mktime(year " " (month+1) " 1 0 0 0") - 1 file_name = "unbound-" year "-" sprintf("%02d", month) ts_to_file[start_ts] = file_name sorted_ts[n++] = start_ts } } asort(sorted_ts) current_file = "" } # 匹配带时间戳的日志行 /^\[[0-9]+\]/ { match($0, /^\[([0-9]+)\]/, arr) current_ts = arr[1] + 0 # 找到当前时间戳所属的月份文件 target_file = "" for (i = 0; i < n; i++) { if (current_ts >= sorted_ts[i] && (i == n-1 || current_ts < sorted_ts[i+1])) { target_file = ts_to_file[sorted_ts[i]] break } } # 切换文件时,关闭旧文件并压缩 if (target_file != current_file) { if (current_file != "") { close(current_file) system("gzip " current_file) } current_file = target_file } } # 所有行(包括多行日志)写入当前文件 current_file != "" { print >> current_file } END { # 处理最后一个文件 if (current_file != "") { close(current_file) system("gzip " current_file) } }
核心逻辑说明
- 预计算时间映射:提前算出每个月的起始时间戳,建立时间戳到目标文件名的关联,避免每行重复计算,提升效率。
- 动态切换文件:遇到带时间戳的行时,判断其所属月份,若需要切换文件,先关闭并压缩之前的文件,再打开新文件。
- 完整保留多行内容:无论行是否带时间戳,只要当前处于某个月份的处理周期,就写入对应文件,确保dig这类跨多行的日志不会被拆分。
- 自动压缩:每次关闭文件后自动调用
gzip压缩,减少磁盘占用。
使用步骤
- 打开脚本,修改
start_year和end_year为你的日志实际覆盖的年份。 - 保存脚本为
split_unbound_log.awk。 - 执行命令:
awk -f split_unbound_log.awk your_service.log - 执行完成后,会生成
unbound-YYYY-MM.gz格式的按月拆分日志文件。
内容的提问来源于stack exchange,提问作者LxAdm
相关产品推荐
相关产品推荐

