Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi All,
I have written a little script which I want to monitor the cpu load of the server it is on and email me if the loads gets too high. I am having some difficulty doing this because I am trying to read the current CPU loads from /proc/loadavg virtual file, but im having trouble actually using the output, as I only want to use the first number which represents the current load ... here is what I have, any help would be greatly appreciated.
#!/usr/bin/perl %config = ( # a) Server name server => 'Server', # b) Notification Email email => 'me@me.com', # c) Alert level (when system load > ?) alert => '5' ); system("cat /proc/loadavg"); while (<STDIN>) { push @loads, $_; } @loads = split(/ /); if ($loads[1] > $config{'notify'}) { # Alert !! :) print "@loads"; print "System [Alert] Current Load = $loads[1]\n\n"; &notify(); } else { print "System [OK] Current Load = $loads[1]\n\n"; } sub notify { $time = system("/bin/date"); open (MAIL, "|/usr/sbin/sendmail -t"); print MAIL "To: $config{'email'}"; print MAIL "From: Load Monitor"; print MAIL "Subject: ALERT High Load"; print MAIL "Server Alert (Server Name $config{'server' +})"; print MAIL "Time: $time"; print MAIL "$config{'server'} has exceeded the load al +ert of $config{'alert'}"; close(MAIL); }


Thanks for any advice.

John

Replies are listed 'Best First'.
Re: Load Monitor
by monkfish (Pilgrim) on Nov 02, 2001 at 21:03 UTC
    Update: Rewrote answer when I realized he wanted second load number which is an avg, not current load.

    Update2: In the first update, I removed the substr in the original post that was the answer because I wasn't paying attention. :(

    John,

    This should get you the values you are looking for in @load:

    @load = split (" ", `cat /proc/loadavg`); # Get loads @load = map {substr $_,0,1} @load; # Convert to first digit $#load =2; # Truncate array include only loads

    By the way this is not very portable as not every unix makes /proc/loadavg available. You might try:

    $w = `w`; # run w and store in $w $w =~ s/^.*load average:\s+//; # Strip of junk before loads @load = split (" ", $w); # split them up @load = map {substr $_,0,1} @load; # Convert to first digit $#load =2; # Truncate array include only loads

    And don't forget those \n in your prints or the MAIL won't make it.

    -monkfish (The Fishy Monk)

Re: Load Monitor
by arhuman (Vicar) on Nov 02, 2001 at 20:59 UTC
    I just don't understand your :
    system("cat /proc/loadavg"); while (<STDIN>) {
    why not just :
    while (</proc/loadavg>) {

    UPDATE :
    Furthermore if it returns only one line (as I suspect) on all platforms, the loop is useless...

    Anyway rob_au pointed us to the right direction today with his post about Proc::ProcessTable

    "Only Bad Coders Code Badly In Perl" (OBC2BIP)
Re: Load Monitor
by perrin (Chancellor) on Nov 02, 2001 at 20:54 UTC
    Have you looked at mon? It does a great job with this kind of thing, and it's written in easy-to-extend Perl.
Re: Load Monitor
by tfrayner (Curate) on Nov 02, 2001 at 20:57 UTC
    Correct me if I'm wrong, but shouldn't you be checking $loads[0] rather than $loads[1]? Nothing else immediately stands out as being wrong :-)

    HTH,

    Tim

    Update: Okay, there are a few other problems. Most of them have been discussed below, so I won't reiterate (or is that preiterate?). Note however that you set $config{alert} but actually check $config{notify}. (Insert usual spiel about use strict here :-P)

    I also rewrote the /proc/loadavg access thusly:

    open (LOADAVG,"/proc/loadavg"); while (<LOADAVG>) { @loads = split(/ /); }
    Do all that and it seems to work, on a one check per run basis.
Re: Load Monitor
by blackmateria (Chaplain) on Nov 02, 2001 at 21:02 UTC
    I see two problems with your code:
    1. system("cat /proc/loadavg") does in fact print to stdout, but that doesn't mean you read its output from stdin. Instead of your cat+while loop+split combo, you want: @loads = split /\s+/, scalar `cat /proc/loadavg` ;
    2. You're using $loads[1] to represent the first element of the array. I think you want $loads[0] instead.
    Hope this helps!