我有一个 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>")}'