我有几个运行各种自制应用程序的 Linux 系统、一些 Web API、一些后台数据处理、多个数据库以及我什至还没有找到的东西。这些系统是由多人在几个月内建立起来的,没有人真正知道每台服务器是如何使用的。一些制定这些项目的人是实习生或承包商,但后来已经离开了。所以我们不知道如何配置任何东西。
我试图找出正在写入的日志文件,尤其是那些写入在奇怪位置的日志文件(即不在 /var/log 下)。我还试图找到所有未轮换的日志文件,特别是当它们快速增长时。我发现过去一周有两台服务器超出了磁盘限制并且进程崩溃。我的目标是重新配置每个应用程序以执行一些合理的日志记录,并最终将其全部发送到 ELK 堆栈,但现在我只需要弄清楚我已经得到了什么。
因此,首先我试图找到日志文件中的所有内容。如果它们随机分散在系统中,那就很难了。有的在/home/someuser下,有的在/root下,有的在/tmp下,有的在/var/lib下。
我查找日志文件的第一个想法是查找最近修改的任何文件。看这个答案:https://askubuntu.com/a/704163/139584
但这给我带来了很多噪音。数据库将内容保存到磁盘,因此它们写入文件,系统更新替换二进制文件,以便修改这些内容,并且用户在家中修改了内容。
我的下一个想法是按名字查找。大多数日志文件以 .log 结尾,但有些则不然。也许有些路径名中的某处有“log”。看这个答案:https://askubuntu.com/a/144703/139584
一旦我有了日志列表,我就可以扫描 logrotate 规则以查找任何匹配的内容。使用 for 和 grep 应该很容易。
有谁对如何在未记录的 Linux 系统中枚举杂散日志文件有更好的了解吗?
答案1
我发现这是一个有趣的问题,原因如下:
- 我可能会遇到类似的系统,并且需要处理文件/文件系统的增长
- 收集“本地”文件系统列表并不简单
- 确定增长需要多次检查,中间有时间延迟
- 一般排除“用户主目录”需要小心
我提出了一个脚本,在较高级别上用于find
查找过去 7 天内修改过的不可执行文件;然后它会休眠一分钟,然后重新扫描这些文件以查看其中是否有任何文件增长超过 42 字节。
当然,所有任意数字都可以在您自己的脚本副本中自由编辑:
- 睡眠时间(等待日志文件增长)
- 需要警惕的增长量
- 文件的最新修改程度
我使用收集本地文件系统列表lsblk
,要求它生成一个不带标题的仅包含挂载点的列表;因为该输出包括不一定安装的块设备(例如整个磁盘、交换区域等),所以我然后过滤包含/
.
排除主目录似乎是一个好主意,但我不想假设每个主目录都在 下/home
,所以我从/etc/login.defs作为“典型”用户的起始范围,然后使用 awk 从 /etc/passwd 中提取此类用户的主目录。然后,这些主目录将从find
.
注意事项
我想find ... -print0
结合使用readarray -t -d ''
来安全地捕获任何和所有文件名,但空分隔的 readarray 需要最新的 bash 版本(4.4-alpha 或更高版本)。相反,我妥协并使用find ... -print
,但需要注意的是,任何包含换行符的相关文件名都会导致错误。
该脚本不会(在任何单次运行期间)找到新创建的日志文件;它收集潜在日志文件的初始列表,然后循环返回同一列表以查看哪些文件增长了。新创建的文件只会被后续运行捕获。
剧本
#!/bin/bash
# files that grow by more than this much are interesting; in bytes per second; also, the answer to Life, the Universe, and Everything
rate=42
# how long we'll wait to account for file growth
sleeptime=60
function gethomedirs() (
uidmin=$(awk '/^UID_MIN/ { print $2 }' < /etc/login.defs)
awk -F: -v umin="$uidmin" '$3 >= umin { print $6 }' < /etc/passwd | sort -u
)
function findlogfiles {
readarray -t homedirs < <(gethomedirs)
if [ ${#homedirs[@]} -eq 0 ]
then
excludes=()
elif [ ${#homedirs[@]} -eq 1 ]
then
excludes=("( -path ${homedirs[0]} ) -prune -o")
else
excludes=()
excludes+=("(")
excludes+=(" -path ${homedirs[0]}")
for((i=1; i < ${#homedirs[@]}; i++))
do
excludes+=(" -o -path ${homedirs[i]}")
done
excludes+=(") -prune -o ")
fi
find $(lsblk --list --noheadings --output MOUNTPOINT | grep /) \
-xdev \
${excludes[@]} \
-type f -mtime -7 ! -executable -print
}
readarray -t files < <(findlogfiles)
declare -A initialsize
for file in "${files[@]}"
do
initialsize["$file"]=$(stat -c %s "$file")
done
#echo Waiting $sleeptime seconds for log files to grow... >&2
sleep $sleeptime
for file in "${files[@]}"
do
# if the file went away, skip it
[ -f "$file" ] || continue
size2=$(stat -c %s "$file")
if (( size2 >= (${initialsize["$file"]} + rate * sleeptime) ))
then
printf "%s\n" "$file"
fi
done
答案2
所以我想出了一个计划,尽管我对此不太满意。
我使用了我已有的想法,但使其不那么通用。我正在特定位置查找日志文件,并且仅查找大于特定大小(1M)的日志文件。因此,如果有人在一个奇怪的地方有日志,我可能找不到它。为了排除由 logrotate 轮换的文件,我首先手动触发日志轮换。这样,在我运行搜索之前,这些文件将被截断。我还假设日志文件名为 *.log,所以如果有人有一个奇怪命名的日志,那么我将找不到它,最终会导致问题。我希望在不久的将来通过教我的团队使用 ELK 堆栈来解决这个问题。
这是我在每台机器上运行的脚本:
#!/bin/sh
sudo logrotate -vf /etc/logrotate.conf
sudo find /var/log -type f -mtime -2 -name "*log" -size +1M -exec sudo ls -l {} \;
sudo find /home -type f -mtime -2 -name "*log" -size +1M -exec sudo ls -l {} \;
sudo find /root -type f -mtime -2 -name "*log" -size +1M -exec sudo ls -l {} \;