문제

I'm trying to write a small process that keeps scanning a log path. All the rotated files are ".csv", while the current one which is still being written has ".csv.cur" as its extension. The process should keep looking in "almost" real time for ".csv.cur" files and perform operations on their content. To make it short, it should work like a "tail -f | replace something".

So this is the main code:

for (;;) {

    opendir(my $cdrs, $path) || die $!;
    my @current = grep { /^$host.+\.cur$/ && -f "$path/$_" } readdir($cdrs); 
    closedir($cdrs);

    if (! $current[0]) {
        &debug(DEBUG_MODE, "[PARENT] No CURRENT file found in: $path/. Retrying in $current_lookup_retry second(s)...\n");
        sleep $current_lookup_retry;
        next;
    }

    if ((! $last_file) || ($last_file ne $current[0])) {
        $last_file = $current[0];
        my $c_pid = &spawn_guardian("$path/$current[0]");
        &debug(DEBUG_MODE, "[PARENT] Guardian spawned (Child PID $c_pid)\n");
        &debug(DEBUG_MODE, "[PARENT] CURRENT set to: $current[0]\n");
    }

    select(undef, undef, undef, $ms);
}

And the sub spawn_guardian:

sub spawn_guardian() {
    $SIG{CHLD} = "IGNORE"; # prevent defunct processes
    my $child_pid = fork();    

    if (!defined $child_pid) {
        die "[CHILD] Cannot fork: $!";
    } elsif ($child_pid == 0) {
        # no need to verify if the parent dies

        &debug(DEBUG_MODE, "[CHILD][$$] Spawning guardian for $_[0]...\n");
        my $file = File::Tail->new(name          => $_[0],
                                   #name_changes => \&lcount($line_count),
                                   interval      => 1, 
                                   maxinterval   => 5,
                                   adjustafter   => 1,
                                   #errmode      => 'die',
                                   resetafter    => 15,
                                   debug         => DEBUG_MODE);

        while (defined(my $line = $file->read)) {
            if ($line) { # Try to evaluate the content of $line 
                print "[CHILD][$$] $line";
            } else {  # Try to gracefully exit from this Child process  
                &grace($$, $_[0]);
            }
        }

        &debug(DEBUG_MODE, "[CHILD][$$] Exiting guardian for $_[0]...\n"); # this should never be executed anyway
        exit 0; # same as previous line
    }
    return $child_pid; # return PID to Parent process
} 

This is the output I get from this code:

[DEBUG][PARENT] No CURRENT file found in: /root/OLOShaper/. Retrying in 2 second(s)...
[DEBUG][PARENT] No CURRENT file found in: /root/OLOShaper/. Retrying in 2 second(s)...
[DEBUG][CHILD][8346] Spawning guardian for /root/OLOShaper/mcitti21-KCZsOUrfmlFNRDXk.csv.cur...
[DEBUG][PARENT] Guardian spawned (Child PID 8346)
[DEBUG][PARENT] CURRENT set to: mcitti21-KCZsOUrfmlFNRDXk.csv.cur
[CHILD][8346] <omitted content of the .csv.cur>
[CHILD][8346] <omitted content of the .csv.cur>
[CHILD][8346] <omitted content of the .csv.cur>
[DEBUG][PARENT] No CURRENT file found in: /root/OLOShaper/. Retrying in 2 second(s)...
Error opening /root/OLOShaper/mcitti21-KCZsOUrfmlFNRDXk.csv.cur: No such file or directory at ./OLOShaper line 91
[DEBUG][PARENT] No CURRENT file found in: /root/OLOShaper/. Retrying in 2 second(s)...
[DEBUG]Ctrl-C detected!
Graceful shutdown in progress for PID 8344...
[DEBUG]Resetting PID file...
[DEBUG]Removing PID file...
Service ./OLOShaper has been halted.

So as you can see I get this error:

Error opening /root/OLOShaper/mcitti21-KCZsOUrfmlFNRDXk.csv.cur: No such file or directory at ./OLOShaper line 91

What I do in oder to generate it, it's to delete the .csv.cur file while the process is scanning it. Of course, I'm doing such idiotic thing in order to test the code, but I really can't come up with a solution to handle this error.

Let me know if you need more information.

도움이 되었습니까?

해결책

As far as I can tell, the error is working as you'd probably desire. The child is no longer able to process its assigned file, so it naturally dies and is cleaned up while parent continues to look for new files.

If you'd like to more gracefully handle these types of error conditions, you could wrap your File::Tail code in an eval, and do some cleanup and more detailed logging when an error occurs. You could use use a module like Try::Tiny for a slightly more semantic way of catching errors.

eval {
    my $file = File::Tail->new(...);

    while (defined(my $line = $file->read)) {
       ...
    }
};
if ($@) {
    warn "Error processing $filename: $@";
    # Any additional cleanup here
}

Either way, it looks like your code is going to work fine as it is, but certainly more full error handling can be nice.

side note

Your spawn_guardian is passed a single parameter file name. Go ahead and put that in a descriptive variable: my ($filename) = @_; That way your code is more self-documenting instead of having random $_[0]'s floating around.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top