多次处理日志文件时从最后处理的行开始

多次处理日志文件时从最后处理的行开始

我正在为 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_lineecho

$ ./logger /var/log/app12.log | ./analyzer --logtype=app12

./analyzer将获取数据作为输入。

相关内容