我有DB错误日志文件,它会不断增长。 现在我想每隔5分钟对该文件设置一些错误监控。 问题是我不想每5分钟扫描整个文件(当监视cron执行时),因为它可能会在未来变得非常大。每5分钟扫描整个(大)文件将消耗更多的资源。 所以我只想扫描在最后5分钟间隔内插入/写入日志的行。 记录在日志中的每个错误都会在下面添加Timestamp:
180418 23:45:00 [错误] mysql收到信号11。
所以我想只在最后5分钟(不是整个文件)中添加的行上搜索模式[错误]并将输出放到另一个文件中。
请在这里帮助我。 如果您需要对我的问题进行更多说明,请随意。
我正在使用RHEL 7,我正试图通过bash shell脚本实现上述监控
答案 0 :(得分:4)
这会在最后一个实例停止的地方找到。如果您每5分钟运行一次,那么它将扫描5分钟的数据。
请注意,此实现可以有意识地扫描在调用运行期间添加的数据两次。这有点草率,但扫描重叠数据两次比根本不读它更安全,如果依靠cron
按计划运行程序,则可能会运行这种风险(同样,如果系统繁忙,sleep
可以在请求的时间内运行。
#!/usr/bin/env bash
file=$1; shift # first input: filename
grep_opts=( "$@" ) # remaining inputs: grep options
dir=$(dirname -- "$file") # extract directory name to use for offset storage
basename=${file##*/} # pick up file name w/o directory
size_file="$dir/.$basename.size" # generate filename to use to store offset
if [[ -s $size_file ]]; then # ...if we already have a file with an offset...
old_size=$(<"$size_file") # ...read it from that file
else
old_size=0 # ...otherwise start at the front.
fi
new_size=$(stat --format=%s -- "$file") || exit # Figure out current size
if (( new_size < old_size )); then
old_size=0 # file was truncated, so we can't trust old_size
elif (( new_size == old_size )); then
exit 0 # no new contents, so no point in trying to search
fi
# read starting at old_size and grep only that content
dd iflag=skip_bytes skip="$old_size" if="$file" | grep "${grep_opts[@]}"; grep_retval=$?
# if the read failed, don't store an updated offset
(( ${PIPESTATUS[0]} != 0 )) && exit 1
# create a new tempfile to store offset in
tempfile=$(mktemp -- "${size_file}.XXXXXX") || exit
# write to that temporary file...
printf '%s\n' "$new_size" > "$tempfile" || { rm -f "$tempfile"; exit 1; }
# ...and if that write succeeded, overwrite the last place where we serialized output.
mv -- "$tempfile" "$new_size" || exit
exit "$grep_retval"
请注意,如果您依靠cron
每隔5分钟调用一次代码,则可能会错过内容;因此,存储字节偏移可以更准确。
#!/usr/bin/env bash
file=$1; shift
start_date=$(date -d 'now - 5 minutes' '+%y%m%d %H:%M:%S')
byte_offset=$(bsearch --byte-offset "$file" "$start_date")
dd iflag=skip_bytes skip="$byte_offset" if="$file" | grep "$@"
答案 1 :(得分:1)
另一种方法可能是这样的:
DB_FILE="FULL_PATH_TO_YOUR_DB_FILE"
current_db_size=$(du -b "$DB_FILE" | cut -f 1)
if [[ ! -a SOME_PATH_OF_YOUR_CHOICE/last_size_db_file ]] ; then
tail --bytes $current_db_size $DB_FILE > SOME_PATH_OF_YOUR_CHOICE/log-file_$(date +%Y-%m-%d_%H-%M-%S)
else
if [[ $(cat last_size_db_file) -gt $current_db_size ]] ; then
previously_readed_bytes=0
else
previously_readed_bytes=$(cat last_size_db_file)
fi
new_bytes=$(($current_db_size - $previously_readed_bytes))
tail --bytes $new_bytes $DB_FILE > SOME_PATH_OF_YOUR_CHOICE/log-file_$(date +%Y-%m-%d_%H-%M-%S)
fi
printf $current_db_size > SOME_PATH_OF_YOUR_CHOICE/last_size_db_file
这会打印先前未打印到DB_FILE
SOME_PATH_OF_YOUR_CHOICE/log-file_$(date +%Y-%m-%d_%H-%M-%S)
的所有字节
请注意,$(date +%Y-%m-%d_%H-%M-%S)
将是当前的&#39;完整&#39;创建日志文件时的日期
您可以将其设为脚本,并使用cron
每五分钟执行一次该脚本;像这样的东西:
*/5 * * * * PATH_TO_YOUR_SCRIPT
答案 2 :(得分:0)
这是我的方法:
首先,到目前为止读取整个日志。 如果到达最后,收集并读取新行的时间跨度(在我的示例中为9秒,以便更快地进行测试,而我的虚拟服务器每隔3秒附加到日志文件)。
在时间跨度之后,回显缓存,清除缓存(数组arr
),循环并休眠一段时间,以便此过程不会占用所有CPU时间。
首先,我的虚拟日志文件编写器:
#!/bin/bash
#
# dummy logfile writer
#
while true
do
s=$(( $(date +%s) % 3600))
echo $s server msg
sleep 3
done >> seconds.log
通过./seconds-out.sh &
启动。
现在更复杂的部分:
#!/bin/bash
#
# consume a logfile as written so far. Then, collect every new line
# and show it in an interval of $interval
#
interval=9 # 9 seconds
#
printf -v secnow '%(%s)T' -1
start=$(( secnow % (3600*24*365) ))
declare -a arr
init=false
while true
do
read line
printf -v secnow '%(%s)T' -1
now=$(( secnow % (3600*24*365) ))
# consume every line created in the past
if (( ! init ))
then
# assume reading a line might not take longer than a second (rounded to whole seconds)
while (( ${#line} > 0 && (now - start) < 2 ))
do
read line
start=$now
echo -n "." # for debugging purpose, remove
printf -v secnow '%(%s)T' -1
now=$(( secnow % (3600*24*365) ))
done
init=1
echo "init=$init" # for debugging purpose, remove
# collect new lines, display them every $interval seconds
else
if ((${#line} > 0 ))
then
echo -n "-" # for debugging purpose, remove
arr+=("read: $line \n")
fi
if (( (now - start) > interval ))
then
echo -e "${arr[@]]}"
arr=()
start=$now
fi
fi
sleep .1
done < seconds.log
使用日志文件生成器在3秒内输出,运行一段时间,然后启动read-seconds.sh脚本,并激活调试输出:
./read-seconds.sh
.......................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................init=1
---read: 1688 server msg
read: 1691 server msg
read: 1694 server msg
---read: 1697 server msg
read: 1700 server msg
read: 1703 server msg
----read: 1706 server msg
read: 1709 server msg
read: 1712 server msg
read: 1715 server msg
^C