我有一个连续附加的日志文件,如下所示。我的目的是每分钟找到REJECT计数。一个微小的cron无法以恰好在9:15:00运行的方式同步,这使得这个问题变得棘手。
20160302-09:15:01.283619074 ResponseType:RMS_REJECT|OrderID:4000007|Symbol:-10|Side:S|Price:175|Quantity:10000|AccountID:BMW500 |ErrorCode:100107|TimeStamp:1490586301283617274
20160302-09:15:01.287619074 ResponseType:RMS_REJECT|OrderID:4000007|Symbol:-10|Side:S|Price:175|Quantity:10000|AccountID:BMW500 |ErrorCode:100107|TimeStamp:1490586301283617274
20160302-09:15:01.289619074 ResponseType:RMS_REJECT|OrderID:4000007|Symbol:-10|Side:S|Price:175|Quantity:10000|AccountID:BMW500 |ErrorCode:100107|TimeStamp:1490586301283617274
20160302-09:15:01.290619074 ResponseType:RMS_REJECT|OrderID:4000007|Symbol:-10|Side:S|Price:175|Quantity:10000|AccountID:BMW500 |ErrorCode:100107|TimeStamp:1490586301283617274
20160302-09:15:01.291619074 ResponseType:RMS_REJECT|OrderID:4000007|Symbol:-10|Side:S|Price:175|Quantity:10000|AccountID:BMW500 |ErrorCode:100107|TimeStamp:1490586301283617274
20160302-09:15:01.295619074 ResponseType:RMS_REJECT|OrderID:4000007|Symbol:-10|Side:S|Price:175|Quantity:10000|AccountID:BMW500 |ErrorCode:100107|TimeStamp:1490586301283617274
20160302-09:15:01.297619074 ResponseType:RMS_REJECT|OrderID:4000007|Symbol:-10|Side:S|Price:175|Quantity:10000|AccountID:BMW500 |ErrorCode:100107|TimeStamp:1490586301283617274
20160302-09:16:02.283619074 ResponseType:RMS_REJECT|OrderID:4000007|Symbol:-10|Side:S|Price:175|Quantity:10000|AccountID:BMW500 |ErrorCode:100107|TimeStamp:1490586301283617274
20160302-09:16:03.283619074 ResponseType:RMS_REJECT|OrderID:4000007|Symbol:-10|Side:S|Price:175|Quantity:10000|AccountID:BMW500 |ErrorCode:100107|TimeStamp:1490586301283617274
20160302-09:17:02.283619074 ResponseType:RMS_REJECT|OrderID:4000007|Symbol:-10|Side:S|Price:175|Quantity:10000|AccountID:BMW500 |ErrorCode:100107|TimeStamp:1490586301283617274
20160302-09:17:07.283619074 ResponseType:RMS_REJECT|OrderID:4000007|Symbol:-10|Side:S|Price:175|Quantity:10000|AccountID:BMW500 |ErrorCode:100107|TimeStamp:1490586301283617274
20160302-09:18:07.283619074 ResponseType:RMS_REJECT|OrderID:4000007|Symbol:-10|Side:S|Price:175|Quantity:10000|AccountID:BMW500 |ErrorCode:100107|TimeStamp:1490586301283617274
20160302-09:19:06.283619074 ResponseType:ACCEPT|OrderID:4000007|Symbol:-10|Side:S|Price:175|Quantity:10000|AccountID:BMW500 |ErrorCode:10|TimeStamp:1490586301283617274
20160302-09:19:07.283619074 ResponseType:ACCEPT|OrderID:4000007|Symbol:-10|Side:S|Price:175|Quantity:10000|AccountID:BMW500 |ErrorCode:10|TimeStamp:1490586301283617274
20160302-09:19:07.283619075 ResponseType:ACCEPT|OrderID:4000007|Symbol:-10|Side:S|Price:175|Quantity:10000|AccountID:BMW500 |ErrorCode:10|TimeStamp:1490586301283617274
20160302-09:20:07.283619075 ResponseType:ACCEPT|OrderID:4000007|Symbol:-10|Side:S|Price:175|Quantity:10000|AccountID:BMW500 |ErrorCode:10|TimeStamp:1490586301283617274
20160302-09:21:07.283619075 ResponseType:ACCEPT|OrderID:4000007|Symbol:-10|Side:S|Price:175|Quantity:10000|AccountID:BMW500 |ErrorCode:10|TimeStamp:1490586301283617274
20160302-09:22:07.283619074 ResponseType:RMS_REJECT|OrderID:4000007|Symbol:-10|Side:S|Price:175|Quantity:10000|AccountID:BMW500 |ErrorCode:100107|TimeStamp:1490586301283617274
预期输出
8 9:15 REJECT 7
10 9:16 REJECT 2
12 9:17 REJECT 2
#continue appending every 2 minutes or so by reading more logged lines eg. 9:19 then 9:22 and so on
我的代码:
#!/usr/bin/bash
today=$(date +%Y%m%d)
touch ${today}.log
counter=$(tail -1 ${today}.log | cut -d" " -f1) #get line number to tail from
re='^[0-9]+$'
if ! [[ $counter =~ $re ]] ; then
counter=1
fi
echo "$counter"
echo "tail -n +$counter $1 | grep "ErrorCode:100107" |cut -d'|' -f1 | awk -F '[:-]' '{curr=$2":"$3} (prev!="") && (curr!=prev){print NR, prev, $NF, cnt; cnt=0} {cnt++; prev=curr}' >> ${today}.log"
tail -n +$counter $1 | grep "ErrorCode:100107" |cut -d'|' -f1 | awk -F '[:-]' '{curr=$2":"$3} (prev!="") && (curr!=prev){print NR, prev, $NF, cnt; cnt=0} {cnt++; prev=curr}' >> ${today}.log
我打算每2或3分钟运行一次这个脚本(延迟记录暂时没问题)。但是不想每次都重新读取整个文件(文件将以GB为单位),所以我尝试使用行号和尾部添加行。但是这在第二次迭代期间失败,线号被重置。有没有办法只在新添加的行上工作。
UPDATE1: 生成连续日志文件
while :; do echo "$(date +%Y%m%d-%H:%M:%S).21223 ResponseType:RMS_REJECT|OrderID:4000007|Symbol:-10|Side:S|Price:175|Quantity:10000|AccountID:BMW500 |ErrorCode:100107|TimeStamp:1490586301283617274";sleep $(shuf -i 5-20 -n 1);done >> test_log &
我使用test_log作为参数./myscript.sh test_log
运行脚本
myscript.sh的内容
#!/usr/bin/bash
inputfile=$1
tail -f $inputfile | grep "ErrorCode:100107" |cut -d'|' -f1 | awk '/RMS_REJECT/{key=$2":"$3;a[key]++;if (LK && LK != key){print LK,a[LK]+1;delete a};LK=key}' FS='[-:]' #does not give output on teminal
# however this works=> cat $inputfile | grep "ErrorCode:100107" |cut -d'|' -f1 | awk '/RMS_REJECT/{key=$2":"$3;a[key]++;if (LK && LK != key){print LK,a[LK]+1;delete a};LK=key}' FS='[-:]'
这在某种程度上不会给我终端上的连续输出
参考: Counting lines in a file during particular timestamps in bash
答案 0 :(得分:2)
此gawk
将在时间戳迭代中更新 结果:
tail -f your_log_file|gawk '/RMS_REJECT/{key=$2":"$3;a[key]++;if (LK && LK != key){print LK,a[LK];delete a[LK]};LK=key}' FS='[-:]'
简单且最小的内存开销。
<强>解释强>
FS='[-:]'
:F
ield S
eparator使用-
设置为:
和regex
。
/RMS_REJECT/
:仅考虑被拒绝的记录,因此我们会查找适当的模式,并且当行中不存在时,不执行任何操作。< / p>
key=$2":"$3
:根据第二和第三个字段撰写key
。
a[key]++
:将密钥存储在关联数组(哈希类型)中,并累计看到密钥的次数。
我们暂时会跳过if部分的解释。
LK=key
:LK
代表 Last Key ,因此我们使用此变量存储已处理的 last 键。
if (LK && LK != key){print LK,a[LK];delete a[LK]}
:当我们的 Last Key 有一个值且其内容与当前值(key
var)不同时,请打印 Last Key 及其哈希值和从数组中删除de key以避免内存开销。
示例强>
[klashxx]$ while :; do echo "$(date +%Y%m%d-%H:%M:%S).21223 ResponseType:RMS_REJECT";sleep $(shuf -i 5-20 -n 1);done >> test_log &
[1] 4040
[klashxx]$ tail -f test_log |gawk '/RMS_REJECT/{key=$2":"$3;a[key]++;if (LK && LK != key){print LK,a[LK];delete a[LK]};LK=key}' FS='[-:]'
09:22 6
09:23 5
09:24 6
09:25 3
^C
(按下Control + C)
[klashxx]$ cat test_log
20170405-09:22:13.21223 ResponseType:RMS_REJECT
20170405-09:22:20.21223 ResponseType:RMS_REJECT
20170405-09:22:29.21223 ResponseType:RMS_REJECT
20170405-09:22:44.21223 ResponseType:RMS_REJECT
20170405-09:22:53.21223 ResponseType:RMS_REJECT
20170405-09:23:07.21223 ResponseType:RMS_REJECT
20170405-09:23:22.21223 ResponseType:RMS_REJECT
20170405-09:23:38.21223 ResponseType:RMS_REJECT
20170405-09:23:45.21223 ResponseType:RMS_REJECT
20170405-09:23:55.21223 ResponseType:RMS_REJECT
20170405-09:24:05.21223 ResponseType:RMS_REJECT
20170405-09:24:16.21223 ResponseType:RMS_REJECT
20170405-09:24:24.21223 ResponseType:RMS_REJECT
20170405-09:24:31.21223 ResponseType:RMS_REJECT
20170405-09:24:43.21223 ResponseType:RMS_REJECT
20170405-09:24:56.21223 ResponseType:RMS_REJECT
20170405-09:25:13.21223 ResponseType:RMS_REJECT
20170405-09:25:23.21223 ResponseType:RMS_REJECT
20170405-09:25:43.21223 ResponseType:RMS_REJECT
20170405-09:26:01.21223 ResponseType:RMS_REJECT
<强> PS 强>
不要grep
和cut
,只需使用gawk
,请检查此answer。
<强> PS2 强>
根据您的代码,最终的事情应该是这样的:
#!/usr/bin/bash
inputfile=$1
tail -f $inputfile | gawk -v errorcode="100107" '/RMS_REJECT/ && $20 == errorcode{key=$2":"$3;a[key]++;if (LK && LK != key){print LK,a[LK];delete a[LK]};LK=key}' FS='[-:|]'
答案 1 :(得分:1)
解决问题的一种方法是使用stat
来保存日志大小,使用dd
来跳过日志中的旧信息,以仅处理收到的REJECT
代码最后一分钟。您可以通过{{1}使用sleep
:00
到下一分钟(例如sleep $((60 - $(date +%S)))
秒)的秒数,在下一分钟准确开始下一个循环。
对于作为第一个参数(或默认为/path/to/logfile
)的任何日志文件,一个简短示例可能类似于以下内容。您需要添加REJECT逻辑:
#!/bin/bash
lfn=${1:-/path/to/logfile}
size=$(stat -c "%s" "$lfn") ## save original log size
while :; do
newsize=$(stat -c "%s" "$lfn") ## get new log size
if ((newsize > size)); then ## if change, use new info
## use dd to skip over existing text to new text
newtext=$(dd if="$lfn" bs="$size" skip=1 2>/dev/null)
## process newtext however you need
printf "\n newtext: %s\n" "$newtext"
elif ((newsize < size)); then ## handle log truncation
newtext=$(dd if="lfn" 2>/dev/null)
if [ -n "$newtext" ]; then
## process newtext however you need
printf "\n newtext: %s\n" "$newtext"
fi
fi
size=$((newsize)); ## update size to newsize
sleep $((60 - $(date +%S))) ## sleep seconds until next min
done
您可以使用类似nrejects=$(grep -c RMS_REJECT <<<"$newtext")
的内容来获取每次迭代的拒绝次数。
答案 2 :(得分:1)
我提出了一个完全不同的方法!
我没有安装cpp
,但cpp
取决于libc
,因此无论如何都应使用journal
。
我在互联网上找到this example来设置编程代码。
然后我使用this guide来学习journalctl commands
。
service
内的systemd
才能阅读。最简单的方法是致电systemctl list-units
并搜索您想要阅读的服务。
然后您可以使用Journalctl -f
(类似tail -f
)打印出创建的每个新条目。您也可以指示输出。不再需要awk
或类似,而是您可以使用JSON或您想要的任何内容。
我希望这也有助于您未来的项目!
答案 3 :(得分:0)
好吧,一种方法可能是tail -f
日志文件并将其输出重定向到fifo并在fifo上有一个awk工作,如下所示:
模拟日志文件:
$ while true ; do echo foo ; sleep 1 ; done > simulated_logfile
制作一个fifo:
$ mkfifo fifo
tail -f
日志文件并提供fifo:
$ tail -f simulated_logfile > fifo
连续使用awk处理输出:
$ awk '{print NR}' fifo
使你的awk脚本收集信息到一个哈希值,直到分钟更改然后输出,重置变量并继续收集,因为一旦awk脚本退出,tail
进程也将停止。
答案 4 :(得分:0)
您可以尝试使用以下bash + awk来解决动态日志读取问题。
$ cat logappend.sh
#!/bin/bash
start=1
end=$(wc -l < file)
while [ 1 ]
do
awk -v start=$start -v end=$end -F '[|:-]' '/ErrorCode:100107/ && (NR>=start && NR<= end) {curr=$2":"$3} (prev!="") && (curr!=prev){print NR, prev, $5, cnt; cnt=0} {cnt++; prev=curr}' file > output.txt
cat output.txt
sleep 20
start=$end
end=$(wc -l < file)
done
注意:您需要根据需要更改睡眠时间。
对于第一次运行,它将运行完整文件,从第二次运行,它将仅通过更改开始和结束变量来检查已添加到现有文件的新行。
处理......第一次运行 -
$ ./logappend.sh
8 09:15 RMS_REJECT 7
10 09:16 RMS_REJECT 2
11 09:17 RMS_REJECT 1
18 09:15 RMS_REJECT 7
20 09:16 RMS_REJECT 2
script is going to sleep for 20 sec
此脚本将等待20秒,然后通过将前一个结束值分配给start变量并为结束变量设置新值来查找文件中的新更改。 为了测试,我只将前10行文件复制到现有文件
cp file tempfile
head tempfile >> file
###将10行附加到文件
同时你的20秒将完成,你可以看到新的输出 -
21 09:17 RMS_REJECT 20
28 09:15 RMS_REJECT 7
30 09:16 RMS_REJECT 2
script is going to sleep for 20 sec
答案 5 :(得分:0)
awk -F '[.|[:blank:]]+' -v FlushLast=0 '
# assume first time for counter + init
BEGIN {
CounterFile = ARGV[ ARGC - 2]
printf( "" ) >> CounterFile
Count = 0
}
# Load last know reference
FILENAME == CounterFile {
Last = $1
LastRef = 1
next
}
# skip earlier stat
$1 <= Last {
next
}
# treat new date change
Last != $1 {
if( ! LastRef ) {
print Last " " Count
print Last " " Count >> CounterFile
}
LastRef = 0
Last = $1
Count = 0
}
# count reject
$3 ~ /REJECT/ {
Count++
}
# if you want the very last date also (if FlusLast==2 mean next cycle it s ommited )
END {
if( FlushLast ) {
# printf( "flush: " )
print Last " " Count
if ( FlushLast > 1 ) print Last " " Count >> CounterFile
}
}
' ${today}.Counter ${today}.log
注意: