如何找到放错位置的日志文件和未轮换的日志文件?

如何找到放错位置的日志文件和未轮换的日志文件?

我有几个运行各种自制应用程序的 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 {} \; 

相关内容