The art of extracting metrics

Awk

filtering between log timestamps

second precision

awk \
  -v from="$(date +'%s' -d '2021-10-29 02:34' --utc)" \
  -v to="$(date +'%s' -d '2021-10-29 02:36' --utc)" \
  '
  split($0, parts, /[\- :]/) {
    linefmt=""
    for(i=1;i<=6;i++) linefmt = sprintf("%s %s",linefmt, parts[i]);
    ts=mktime(sprintf("%s UTC", linefmt))
    if (ts > from && ts < to) print
  }
  '<<<'2021-10-29 04:50:00 172.31.43.164'
awk \
  -v from="$(date +'%s' -d '2021-11-27 04:15:00 UTC' --utc)" \
  -v to="$(date +'%s' -d 'now UTC' --utc)" \
  '
  patsplit($1, p, /[0-9]+/) {
    ts=mktime(sprintf("%d %d %d %d %d %d", p[1], p[2], p[3], p[4], p[5], p[6]), 1)
    if (ts > from && ts < to) print
  }
  ' <(tac /var/log/frr/frr.log)|head

perl alternative

from="$(date -Is -d '2021-11-27T04:15:00 UTC' --utc)" \
to="$(date -Is -d 'now UTC' --utc)" \
perl \
  -MDate::Parse \
  -lane \
  '
  $ts=str2time($F[0]);
  $f=str2time($ENV{from});
  $t=str2time($ENV{to});
  print if ($ts >= $f && $ts <= $t )
  ' <(zcat -f /var/log/syslog*) | tee "/syslog-$(date -Im)"

from ms precision unix timestamp

awk \
  -v format="%F %T" \
  -v from="$(date +'%s' -d '2021-10-29 02:34' --utc)" \
  -v to="$(date +'%s' -d '2021-10-29 02:36' --utc)" \
  '{
  ts=substr($0, 0, index($0, ".")-1)
  if (ts > from && ts < to) print strftime(format, ts), substr($0,index($0,$1))
  }
  ' <<<'2021-10-29 04:50:00 172.31.43.164'

Sed

Filtering interesting lines for nxos. Note, NXOS has an ancient version of sed, it only supports the -n flag.

monitoring procfs

Continuously monitor a procfs file, output is tabular.

cat /proc/pressure/cpu
some avg10=1.34 avg60=1.63 avg300=2.33 total=2483371233
full avg10=0.18 avg60=0.03 avg300=0.06 total=827064553
awk -F'[ =]' -vlinefmt="%-8s%-8s%-10s%-10s%-8s\n" -vcpu_pressure="/proc/pressure/cpu" 'BEGIN {
    printf linefmt, "avg10", "avg60", "avg300", "increase", "total"
    do {
        if (system("trap true INT; sleep 1")) {
            exit
        }
        if ($1=="full") {
            printf linefmt, $3, $5, $7, int($9-prev_tot), $9
            prev_tot = $9
        }
    } while ((getline < cpu_pressure) || (close(cpu_pressure) == 0))
}'
avg10   avg60   avg300    increase  total
0.36    0.17    0.07      829472174 829472174
0.36    0.17    0.07      0         829472174
0.24    0.16    0.07      24957     829497131

collect metrics for all PSI.

awk -F'[ =\t]' -vlinefmt="%-14s" -vfields='"cpuavg10", "cpuavg60", "cpuavg300", "cputotal", "cpuincrease", "ioavg10", "ioavg60", "ioavg300", "iototal", "ioincrease", "memavg10", "memavg60", "memavg300", "memtotal","memincrease"' -vcpu_pressure="bash -c 'paste /proc/pressure/{cpu,io,memory}'" \
'BEGIN { patsplit(fields, fmts, /[^", ]+/)
    do {
        if (system("trap true INT; sleep 1")) {
            exit
        }
        if ($1=="some") {
            for(x in fmts) {
                printf linefmt, fmts[x]
            }
            printf "\n"
            for(i=3;i<=NF;i+=2) {
                if(i%9 == 0 && i%3 == 0) {
                    printf linefmt""linefmt, $(i), int($(i)-increase[i]) 
                    increase[i]=$i
                    i+=1
                } else {
                    printf linefmt, $i
                }
                
            }
            printf "\n\n"
        }
    } while ((cpu_pressure|getline) || (close(cpu_pressure) == 0))
}'

histograms

The .1 decimal for size helps tie breakers when min == max.

while :; do
  ss -n -t -o $(printf " state %s" {fin-wait-1,fin-wait-2,time-wait,close-wait,last-ack,closing}) |\
    awk -v bmin=0 -vsize=10000.1 'BEGIN{tm["sec"] = 1000;tm["min"] = "60000"} match($6,/,([0-9.]+)(sec|ms|min)/,m){t=(m[2]=="ms"?int(m[1]):int(tm[m[2]]*m[1]) );b=int(t/size);a[b]++;bmax=b>bmax?b:bmax; bmin=b<bmin?b:bmin} END{for(i=bmin;i<=bmax;++i) printf "%d %d %d\n", (i*size)/tm["sec"],((i+1)*size)/tm["sec"],a[i]}'
  sleep 1
done

Using time functions.

LC_ALL=C awk -v bmin=0 -vsize=10.1 -v date="2022 03 06" 'split($4,p,/[: ]/){t=mktime(date" "p[2]" "p[3]" "p[4]); b=int(t/size);a[b]++;bmax=b>bmax?b:bmax; bmin=b<bmin?b:bmin} END{for(i=bmin;i<=bmax;++i) if(a[i]) printf "%s %s %d\n", strftime("%T",(i*size)),strftime("%T",((i+1)*size)),a[i]}' gopkg.in-access.log-2022-03-06T13\:00.log

Useful squid one

for i in $(awk '!s[$4]++{print $4}' squid-access.log); do  LC_ALL=C awk -v bmin=0 -vsize=300.1 -vhist="$i" '$0 !~ hist{next} {t=int($1);b=int(t/size);a[b]++;bmax=b>bmax?b:bmax; bmin=b<bmin?b:bmin} END{printf "%s 300s histogram\n", hist;for(i=bmin;i<=bmax;++i) if(a[i]) printf "%s %s %d\n", strftime("%T",(i*size)),strftime("%T",((i+1)*size)),a[i] > "histograms.sout"}' squid-access.log; done
LC_ALL=C awk -v bmin=0 -vsize=10.1 -v date="2022 03 06" \
'split($4, p, /[: ]/) {
  t = mktime(date " " p[2] " " p[3] " " p[4])
  b = int(t / size)
  a[b]++
  bmax = b > bmax ? b : bmax
  bmin = b < bmin ? b : bmin
}

END {
  for (i = bmin; i <= bmax; ++i) {
    if (a[i]) {
      printf "%s %s %d\n", strftime("%T", (i * size)), strftime("%T", ((i + 1) * size)), a[i]
    }
  }
}'

iptables filtering

sudo iptables -t nat  -nL |awk -vrgxp="$(rgxg cidr 10.131.31.0/24)" '/^(Chain|target)/{f=1} f==1||$0 ~ rgxp{s[NR]=$0;f++}; length($0)==0{if(f>2){for(i in s) print s[i]};split("",s);f=0}'
#awk #perl #logs #metrics #observability #sre