NAME

Log::Progress::Parser - Parse progress data from a file

SYNOPSIS

open my $fh, "<", $logfile or die;
my $parser= Log::Progress::Parser->new(input => $fh);
$parser->parse;

A practical application:

# Display a 40-character progress bar at 1-second intervals
$|= 1;
while (1) {
  $parser->parse;
  printf "\r%3d%%  [%-40s] ",
    $parser->state->{progress}*100,
    "#" x int($parser->state->{progress}*40);
  last if $parser->state->{progress} >= 1;
  sleep 1;
}
print "\n";

DESCRIPTION

This module parses progress messages from a file handle or string. Repeated calls to the "parse" method will continue parsing the file where it left off, making it relatively efficient to repeatedly call "parse" on a live log file.

ATTRIBUTES

input

This is a seekable file handle or scalar of log text from which the progress data will be parsed. Make sure to set the utf-8 layer on the file handle if you want to read progress messages that are more than just ascii.

input_pos

Each call to parse makes a note of the start of the final un-finished line, so that the next call can pick up where it left off, assuming the file is growing and the file handle is seekable.

state

This is a hashref of data describing the progress found in the input.

{
  progress => $number_between_0_and_1,
  message  => $current_progress_messsage,  # empty string if no message
  current  => $numerator,   # only present if progress was a fraction
  total    => $denominator, #
  step     => { $step_id => \%step_state, ... },
  data     => \%data,       # most recent JSON data payload, decoded
}

Substeps may additionally have the keys:

idx          => $order_of_declaration,   # useful for sorting
title        => $name_of_this_step,
contribution => $percent_of_parent_task, # can be undef

sticky_message

Defaults to false. If set to true, then progress lines lacking a message will not clear the message of a previous progress line.

on_data

Optional coderef to handle JSON data discovered on input. The return value of this coderef will be stored in the "data" field of the current step.

For example, you might want to combine all the data instead of overwriting it:

my $parser= Log::Progress::Parser->new(
  on_data => sub {
    my ($parser, $step_id, $data)= @_;
    my $prev_data= $parser->step_state($step_id)->{data} || {};
    return Hash::Merge::merge( $prev_data, $data );
  }
);

METHODS

parse

Read (any additional) "input", and return the "state" field, or die trying.

my $state= $parser->parse;

Sets "input_pos" just beyond the end of the final complete line of text, so that the next call to "parse" can follow a growing log file.

step_state

my $state= $parser->step_state($step_id, $create_if_missing);
my $state= $parser->step_state($step_id, $create_if_missing, \@path_out);

Convenience method to traverse "state" to get the data for a step. If the second paramter is false, this returns undef if the step is not yet defined. Else it creates a new state node, with idx initialized.

If you pass the third parameter @path_out it will receive a list of the parent nodes of the returned state node.

AUTHOR

Michael Conrad <mike@nrdvana.net>

VERSION

version 0.13

COPYRIGHT AND LICENSE

This software is copyright (c) 2023 by Michael Conrad.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.