【发布时间】:2013-11-26 20:37:22
【问题描述】:
我已经使用了大约 3 年的 shell 脚本,它启动 tail -F -n0 /path/to/LOGFILE,解析和格式化输出并将其转储到管道分隔的文件中。但是,我们已经从每天几千条日志行增加到每天几百万条日志行,并且脚本已经开始占用大量内存和 CPU。
我最近用awk 替换了所有数据解析逻辑,这在测试中似乎在解析数据时要快很多数量级。为了测试新的 awk 代码,我尝试使用 awk 代码和 shell 代码通过逻辑(约 600 万行)抽取一整天的日志,不出所料,awk 代码在约 10 秒内提取了约 92K 相关行,而shell 代码花了超过 15 分钟来做同样的事情。但是,如果我使用完全相同的代码,而不是 cat /path/to/file|awk '...' 而不是 tail -F -n0 /path/to/file|awk '...,那么将文本写入文件的时间会有很大的延迟,最多约 2-3 分钟,而不是约 0.5 分钟-1.0 秒,当它通过 shell 代码时。
Shell 代码(是的,我知道 Shell 代码有多丑):
outDir="/opt/redacted/logs/allerrors/"
tail -F -n0 /opt/redacted/logs/manager.log|while read -a logLine;do
if [[ "${logLine[2]}" == "E" ]];then
if [[ "${logLine[7]:0:1}" == "@" ]];then
echo "${logLine[0]}|${logLine[1]}|${logLine[6]}|${logLine[7]}|${logLine[@]:8:${#logLine[@]}}" >> ${outDir}allerrors.${logLine[0]//\//.}.log
else
echo "${logLine[0]}|${logLine[1]}|${logLine[6]}|NULL|${logLine[@]:7:${#logLine[@]}}" >> ${outDir}allerrors.${logLine[0]//\//.}.log
fi
fi
完成
awk 代码:
outDir="/opt/redacted/logs/allerrors/"
tail -F -n0 /opt/redacted/logs/manager.log|awk -v dir=$outDir '{OFS="|"}
{
if ($3 == "E")
{
file="allerrors."$1".log"
gsub("/",".",file)
if ($8 ~ /@/)
print $1,$2,$7,$8,substr($0, index($0,$9)) >> dir file
else {if ($8 !~ /@/)
print $1,$2,$7,"NULL",substr($0, index($0,$8)) >> dir file
}
}
}'
需要明确的是,如果我使用 cat 而不是拖尾文件,则两组代码都可以工作并创建相同的输出,但是使用 awk 代码,直到它出现大约 2-3 分钟后,我才能在输出文件中看到结果在日志中,而 shell 版本只需要几秒钟。
【问题讨论】:
-
这可能是因为
awk正在缓冲输入而read不是。你为什么使用tail -F而不是仅仅提供文件作为awk的输入(即既不是cat 也不是tail,而是简单地读取):awk ... /opt/redacted/logs/manager.log -
我认为这可能与行缓冲有关,但我在概念上理解得不够好,无法解决它。 .我在将尾部管道输送到 grep 时遇到了问题,但是 grep 有一个 --line-buffer 标志(或类似的东西)可以修复它。 awk 有类似的东西吗?至于为什么尾巴,它是一个日志文件,当它达到一定大小时会翻转。但我希望在实时(ish)时间完成解析。我不知道如何让请求实时读取文件并在文件滚动时跟进,这可能吗?
标签: bash shell optimization awk logparser