在日志文件中更改行时发送邮件,tail -f 管道发送到邮件不起作用

在日志文件中更改行时发送邮件,tail -f 管道发送到邮件不起作用

我有一个 nginx access.log ,流量非常少,所以我想在每次访问时收到一封邮件。我试过了

tail -f access.log | cat

这样可行。所以用尾巴管道似乎没问题。

以下没有任何反应:

tail -f access.log | mail -s "Dateizugriff" <Destinationaddress>

都不与

tail -f access.log | grep --line-buffered '.*' | mail -s "Dateizugriff" <Destinationaddress>

也不

stdbuf -oL -eL  tail -f access.log | mail -s "Dateizugriff" <Destinationaddress>

当然有效

echo "test" | mail -s "Dateizugriff" <Destinationaddress>

作品。

然后我尝试了 xargs

tail -f access.log | xargs -I % mail -s "Dateizugriff" <Destinationaddress>

现在我每行收到一封邮件,但没有任何内容

tail -f access.log | xargs -I % | echo "%" |  mail -s "Dateizugriff" <Destinationaddress>

不起作用。什么也没发生。以下也不起作用:

tail -f access.log | xargs |  mail -s "Dateizugriff" <Destinationaddress>

那么让它发挥作用的技巧是什么?

答案1

问题是 tail -f 永远不会终止——它只是永远等待输入。因此管道保持打开状态,邮件永远不会收到 EOF,它只是等待消息“完成”,而这是永远不可能的。

在管道中添加其他进程并没有帮助,除非至少其中一个进程退出并破坏了管道。

如果您确定希望添加到日志中的每一行都在单独的邮件中,您可以使用:

tail -f access.log | head -n 1 | mail -s "Dateizugriff" <user@domain>

Head 读到一行就退出,tail 收到 SIGPIPE 并退出,mail 收到 EOF 并继续发送邮件。

如果日志条目可以是多行,但您始终可以识别最后一行,则可以使用 sed 和 aq 条件来中断管道。

第一个缺点是,您的邮件进程会永久挂起,等待下一行到达。

第二个缺点是您必须认识到该过程每次都已完成,并重新启动整个过程。

就个人而言,我会编写一个 shell 循环,在包含睡眠(可能 60 秒)的循环内反复尝试查找任何添加的行。当文件大小增加时,它可以在不带 -f 的情况下运行 tail,因此它不会挂起。

在循环之前将 nLine 初始化为当前行数。重定向很重要,因为它会阻止 wc 报告文件名和计数。

nLine="$( wc -l < access.log )"

while : ; do
    NEW=$( tail -n +$(( 1 + nLine )) access.log )
    [[ "${#NEW}" -gt 0 ]] && {
        echo "${NEW}" | mail ....
        nLine=$(( nLine + $( wc -l <<<${NEW} ) ))
    }
    sleep 60
done

答案2

由于您可能希望在每次系统重新启动时自动启动此“监视”进程,因此我认为systemd小路是执行此操作的合理方法。

这意味着,您将编写一个monitor_nginx_access.path内容如下的文件

[Unit]
Description=Alert admin about access

[Path]
PathChanged=/absolute/path/to/access.log

和一个服务同名的 ,monitor_nginx_access.service它基本上包含一个具有神奇功能的 shell 脚本的包装器:

[Unit]
Description=Send mail when access log changes

[Service]
ExecStart=/absolute/path/to/mailscript.sh

这两个文件都将被放置在/etc/systemd/系统或者您的安装需要管理员定义的systemd单位的任何地方(参见手册页)。

然后将mailscript.sh包含类似的内容

#!/bin/bash
MAILLINES=5  # how many of the last lines of access.log to mail
...

tail -n "$MAILLINES" /absolute/path/to/access.log | mail -s <your arguments here>

答案3

看看这个答案:https://stackoverflow.com/a/4657782/12332118 我已经测试过了,它对我有用。它完全可以满足您的需求,无需使用 systemd。

答案4

正如 Max Müller 所链接的,你可以这样做

tail -f access.log | grep --line-buffered GET | 
while read line 
do 
echo $line | mail -s New Access <Destinationaddress>
done

或者如果您喜欢在一行中使用我的解决方案

tail -f access.log | awk -W interactive '/GET/{system("echo "$0" | mail -s \"New Access\" <Destinationaddress>")}'

相关内容