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

I'm wrapping up the final bit of work on a plugin that reads in a CSV formatted file, aggregates matching sets of results from a certain column together, and then runs calculations on the coinciding values for each set. I have this currently working fine but am at a bit of a loss as to how to save and present the data. In particular the challenge is that this is designed for implementation as a Nagios monitoring plugin. In the other plugins I have written (mind you I am a rookie), I have directly referenced the values. I am a bit flustered establishing how to save the output of a foreach loop to a data structure and then re-referencing it in a scalable dynamic way? (the set(s) that the calculations are run against will be differ in both count as well as name.

The script looks like this presently (again I am a rookie, so any advice on streamlining would be appreciated). My primary question regards saving the output of the foreach loop at the end and then re-referencing it. I have also included the syntax that I am used to for returning Nagios friendly results (InfoData and PerfData). The big picture is to access the values of the foreach in this section.

My script:

# Calculations to be run on the array values 181 sub average { 182 @_ == 1 or die ('Sub usage: $average = average(\@array);'); 183 my ($array_ref) = @_; 184 my $sum; 185 my $count = scalar @$array_ref; 186 foreach (@$array_ref) { $sum += $_; } 187 return $sum / $count; 188 } 189 190 sub median { 191 @_ == 1 or die ('Sub usage: $median = median(\@array);'); 192 my ($array_ref) = @_; 193 my $count = scalar @$array_ref; 194 my @array = sort { $a <=> $b } @$array_ref; 195 if ($count % 2) { 196 return $array[int($count/2)]; 197 } else { 198 return ($array[$count/2] + $array[$count/2 - 1]) / 2; 199 } 200 } 201 202 sub min { 203 @_ == 1 or die ('Sub usage: $min = min(\@array);'); 204 my ($array_ref) = @_; 205 my @array = sort { $a <=> $b } @$array_ref; 206 return $array[0]; 207 } 208 209 sub max { 210 @_ == 1 or die ('Sub usage: $max = max(\@array);'); 211 my ($array_ref) = @_; 212 my @array = sort { $a <=> $b } @$array_ref; 213 return $array[-1]; 214 } 215 216 sub samplecount { 217 @_ == 1 or die ('Sub usage: $sample_count = samplecount(\@array);' +); 218 my ($array_ref) = @_; 219 my $count = scalar @$array_ref; 220 return $count; 221 } 222 223 224 #Modification for Jenkins syncronized metrics 225 226 my %seen = (); 227 228 open (FH, "< $resultsFile"); 229 my @lines = <FH>; 230 foreach my $line (@lines) { 231 my @values = split ',', $line; 232 233 my $col1_val = $values[1]; 234 my $key = $values[2]; 235 my $errors = $values[7]; 236 237 $key =~ s/LOGGED IN //; 238 $key =~ s/ GET //; 239 $key =~ s/ POST //; 240 241 $seen{$key}->{error_count}++ if $errors eq 'false'; 242 push @{$seen{$key}->{col1_vals}}, $col1_val; 243 244 } 245 246 foreach my $key (keys %seen) { 247 my $sample_count = samplecount($seen{$key}->{col1_vals}); 248 my $error_count = ((($seen{$key}->{error_count})/($sample_coun +t))*100); 249 my $median = median($seen{$key}->{col1_vals}); 250 my $average = average($seen{$key}->{col1_vals}); 251 my $minimum = min($seen{$key}->{col1_vals}); 252 my $maximum = max($seen{$key}->{col1_vals}); 253 print <<HERE; 254 Key: $key 255 Samples: $sample_count 256 ErrorPercent: $error_count 257 Median: $median 258 Average: $average 259 Minimum: $minimum 260 Maximum: $maximum 261 HERE 262 } 263 264 print Dumper \%seen; 265 266 close (FH);

Nagios Output Sample:

691 $InfoData = sprintf ("$targetname Avg.Time= $o_target"); 692 $PerfData = sprintf ("value1=%f value2=%f value3=%f",($value1),( +$value2),($value3)); 693 }

Thanks always for your assistance, insights, and wisdom monks!

-bp-

Replies are listed 'Best First'.
Re: Returning foreach results in a Nagios-Friendly Fashion
by Riales (Hermit) on Apr 02, 2012 at 23:39 UTC

    If I'm understanding you correctly, you can just assemble $InfoData and $PerfData as you've detailed and push it onto some array for reference later instead of printing the information out.

    $InfoData = sprintf("$targetname Avg.Time= $o_target"); $PerfData = sprintf( "value1=%f value2=%f value3=%f", $value1, $value2, $value3 ); push @results, { InfoData => $InfoData, PerfData => $PerfData, );

    Now that you have all your results in @results, you can access it in a variety of ways:

    foreach (@results) { my $info_data = $_->{InfoData}; my $perf_data = $_->{PerfData}; # Do whatever you need to... } # Or maybe: my $arb_info = $results[$arbitrary_idx]->{InfoData}; my $arb_perf = $results[$arbitrary_idx]->{PerfData};

    Is that what you were asking?

      Riales you're the best :) Thanks as always! That is exactly it.

      -bp-

        I just wanted to update this thread with the way in which this problem was solved (it was a 'duh' moment). Perhaps this will help someone else down the line.

        244 # Build data structure and run calaculations for Jenkins syncroniz +ed metrics 245 246 my %seen = (); 247 248 open (FH, "< $resultsFile"); 249 my @lines = <FH>; 250 foreach my $line (@lines) { 251 my @values = split ',', $line; 252 253 my $col1_val = $values[1]; 254 my $key = $values[2]; 255 my $errors = $values[7]; 256 257 $key =~ s/LOGGED IN //; 258 $key =~ s/ GET //; 259 $key =~ s/ POST //; 260 $key =~ s/ //; 261 $key =~ s/\(PHP\)//; 262 $key =~ s/\(JAVA\)//; 263 264 $seen{$key}->{error_count}++ if $errors eq 'false'; 265 push @{$seen{$key}->{col1_vals}}, $col1_val; 266 267 } 268 269 my @results; 270 my @samples; 271 my @averages; 272 my @minimums; 273 my @maximums; 274 my @medians; 275 my @linenineties; 276 my @errors; 277 my @PerfA; 278 foreach my $key (keys %seen) { 279 my $sample_count = samplecount($seen{$key}->{col1_vals}); 280 my $error_count = ((($seen{$key}->{error_count})/($sample_coun +t))*100); 281 my $median = ((median($seen{$key}->{col1_vals}))/1000); 282 my $average = ((average($seen{$key}->{col1_vals}))/1000); 283 my $minimum = ((min($seen{$key}->{col1_vals}))/1000); 284 my $maximum = ((max($seen{$key}->{col1_vals}))/1000); 285 my $lineninety = ((lineninety($seen{$key}->{col1_vals}))/1000) +; 286 my $PerfA = sprintf("$key\_samples=$sample_count $key\_errs=$e +rror_count $key\_avg=$average $key\_min=$minimum $key\_max=$maximum $ +key\_med=$median $key\_line90=$lineninety"); 287 push (@samples, $sample_count); 288 push (@averages, $average); 289 push (@minimums, $minimum); 290 push (@maximums, $maximum); 291 push (@linenineties, $lineninety); 292 push (@errors, $error_count); 293 push (@medians, $median); 294 push (@PerfA, $PerfA); 295 } 296 297 close (FH); 298 299 my $total_avg = average(\@averages); 300 my $total_samples = eval join '+', @samples; 301 my $total_minimum = min(\@minimums); 302 my $total_maximum = max(\@maximums); 303 my $total_median = median(\@medians); 304 my $total_errors = average(\@errors); 305 my $total_lineninety = lineninety(\@linenineties); 306 my $PerfA_join = join ' ', @PerfA; 307 308 #comment this line out if you want to retain the results file for 309 #debugging/detailed results. 310 unlink ($resultsFile); 311 312 if ($sum > 0) { 313 $suminsecs = $sum / 1000; 314 } 315 316 my $PerfData = $PerfA_join; 317 318 if ($failure) { 319 $state = "CRITICAL"; 320 print "$state:$failure\n"; 321 } else { 322 my $details = ''; 323 324 if (defined($critTime) && ($critTime < $suminsecs)) { 325 $state = "CRITICAL"; 326 $details = " (critical $critTime)"; 327 }elsif (defined($warnTime) && ($warnTime < $suminsecs)) { 328 $state = "WARNING"; 329 $details = " (warning $warnTime)"; 330 } else { 331 $state = "OK"; 332 } 333 334 print "$state : $suminsecs seconds to complete $details Samples: +$total_samples Avg:$total_avg Min:$total_minimum Max:$total_maximum M +edian:$total_median ErrorPerc:$total_errors Line90 :$total_linenin +ety | TotalTimetoExecute=$suminsecs Samples=$total_samples AverageTim +e(sec)=$total_avg MinimumTime(sec)=$total_minimum MaximumTime(sec)=$t +otal_maximum MedianTime(sec)=$to tal_median TotalErrors(perc)=$tot +al_errors Line90(all)=$total_lineninety ".$PerfData."\n"; 335 336 }

        This was a good exercise in learning data structures in perl. For reference in the future I found the perlreftut, and doc pages on splice, push, and join extremely helpful.

        Thanks again to all who commented on this thread!

        Best,

        -bp-

Re: Returning foreach results in a Nagios-Friendly Fashion
by JavaFan (Canon) on Apr 02, 2012 at 22:49 UTC
    Since you've shown how to use a hash, and you know how to iterate over it (which you're doing in the loop you seem to be asking about), I'm a bit confused. What is it you are asking? The technique of the obvious answer you've mastered (as you're showing it), so it must be something that you want.

    Perhaps I don't know what re-referencing it in a scalable dynamic way is?