NerdVana

Nerd? We prefer the term 'Intellectual Badass'

freq with me

Written by Eric Schwimmer

I often need to count the frequency of events occurring in log files and what not, and I was never able to find a super clean way to do it in real time, so this perl script was born:

#!/usr/bin/perl

use IO::Select;
use Fcntl;

my (%counts, $start, @lines, $flags, $buffer);

my $select = IO::Select->new();
$select->add(\*STDIN);

my $flags;
fcntl(STDIN, F_GETFL, $flags);
$flags |= O_NONBLOCK;
fcntl(STDIN, F_SETFL, $flags);
my $start = time;

while(1)
{
    print "\033[2J";
    print "\033[0;0H";
    sleep(1);
    @lines = ();
    if ($select->can_read(0))
    {
        @lines = <STDIN>;
        $lines[0] = $buffer . $lines[0];
        $buffer = ($lines[$#lines] =~ /\n$/) ? "" : pop @lines;

        for my $line (@lines)
        {
            chomp $line;
            $counts{$line}->[0]++;
        }

        my $elapsed = time - $start;
        for my $line (sort keys %counts)
        {
            my $rates = $counts{$line};
            my $sec_rate = $rates->[0];
            $rates->[3] += $sec_rate;
            my $total = $rates->[3];

            push @{$rates->[1]}, $sec_rate;
            shift @{$rates->[1]} if scalar @{$rates->[1]} > 60;
            push @{$rates->[2]}, $sec_rate;
            shift @{$rates->[2]} if scalar @{$rates->[2]} > 300;
            my $min1_rate = avg($rates->[1]);
            my $min5_rate = avg($rates->[2]);

            printf "%s -- total: %s, 1 sec: %s, 1 min: %.2f/s, 5 min: %.2f/s\n",
                $line, $total, $sec_rate, $min1_rate, $min5_rate;

            $rates->[0] = 0;
        }
    }
}

sub avg 
{
    my ($list) = @_;
    my $total = 0;
    $total += $_ for (@$list);
    return $total / scalar @$list;
}

Assuming you have jq installed and your logs are in JSON format, you can do something nifty like this:

tail -F /data/nginx/access_log | jq .status | freq

And it will spit out a real time display that looks like this (updated in real time):

"200" -- total: 45226, 1 sec: 676, 1 min: 674.75/s, 5 min: 675.01/s
"204" -- total: 133, 1 sec: 2, 1 min: 2.00/s, 5 min: 1.99/s        
"304" -- total: 70, 1 sec: 1, 1 min: 1.03/s, 5 min: 1.04/s         
"403" -- total: 3, 1 sec: 0, 1 min: 0.08/s, 5 min: 0.08/s          
"404" -- total: 253, 1 sec: 3, 1 min: 4.02/s, 5 min: 3.89/s        
"500" -- total: 52, 1 sec: 0, 1 min: 0.80/s, 5 min: 0.78/s         
"502" -- total: 1, 1 sec: 0, 1 min: 0.05/s, 5 min: 0.05/s