我正在为 Web 应用程序设计一个日志记录和报告系统。
在 Web 应用程序中,可能会发生一些事件(“用户登录”、“用户执行操作 X”等)。
我希望应用程序将这些事件记录在平面日志文件中。
然后我想编写另一个程序,该程序会定期运行并从该文件中提取数据,以在数据库表中维护正在运行的聚合报告(例如“每小时发生多少个类型 X 的事件”,或“获取每日总计事件 X”)。
挑战之一是确保报告程序不会两次处理同一行。
是否有一种 Unix 方法来设计此类系统或处理“跟踪已处理的行”之类的问题?
我已经考虑过在处理日志之前旋转日志并为每行分配唯一的行号,但它们看起来都很老套。
谢谢。
答案1
只要您的应用程序不让日志文件永久打开,在解析日志之前轮换日志听起来确实是个好主意。如果它们这样做,轮换它们将不起作用 - 但由于您正在编写日志记录框架,因此您可以处理这个问题。
如果你想要一个简单的脚本化的东西,你可以使用类似下面的东西(几乎可以用任何脚本语言完成):
#! /bin/bash
process_line() {
# do the work here
echo "== $1 =="
}
logfile=$1
statefile=${logfile}.state
if [ -f ${statefile} ] ; then
processed=$(cat $statefile)
else
processed=0
fi
curline=0
IFS='
'
while read line ; do
if [ $curline -ge $processed ] ; then
echo processing $line
process_line "$line"
fi
curline=$(($curline+1))
done < ${logfile}
echo $curline > $statefile
基本上,它将处理输入的位置保存在单独的文件 ( $statefile
) 中,并从该点开始逐行处理输入(跳过已处理的输入)。
显然,这需要更多的错误处理,并且如果输入很大,则不是最佳的。 (可以通过保存字节偏移量和查找或使用dd bs=1 skip=$already_read count=$(($size-$already_read))
管道输出到另一个进程而不是逐行执行操作来做得更好,但perl
如果需要这种优化,我会选择。)
事实上,如果脚本被中断,它将处理行两次。您可以通过在每行之后更新状态文件而不是在最后更新一次来限制“重播”的数量。
如果您都处理日志并轮换它们,则需要小心处理这些状态文件。它们也需要旋转,并且脚本在旋转后运行一次以处理最后几行输出。
这种方法不容易处理一件事:部分线条。如果应用程序在脚本运行时进行写入,则脚本有可能会看到部分最后一行。它无法区分差异,因此会将其记录为已处理。 (几乎任何方法都需要解决这个问题。)
可以通过在日志文件格式中使用 EOL 标记并在处理该行之前进行检查来避免这种情况。但这不太漂亮。
您可以像这样使用它(用普通的替换)bash
,而不是在脚本本身中进行处理:process_line
echo
$ ./logger /var/log/app12.log | ./analyzer --logtype=app12
./analyzer
将获取数据作为输入。