Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using perl module Log::Syslog::Fast - Unable to catch exception

I am using Log::Syslog::Fast to forward logs to a syslog server. I was testing the script to see how it would react if the syslog server suddenly crashed.

To test I created a file with test messages, started the script & then shutdown the syslog server after 2 messages were received at the syslog server.

The script sent the third message & then dies. The termination is not being caught by eval & 'use warnings 'FATAL' => 'all';' does not help.

Could someone please help me catch the exception & close down the script more gracefully?

What needs to happen here is - After the Command2 is sent, the script should catch the exception & display:

Fail: Command3

Code extract:

$logger = Log::Syslog::Fast->new(LOG_TCP,$server, 514, 13, 6, "test_machine", "Syslog");
$logger->set_pid(0);

foreach $line(<SPOOL>)
{
        ($machine,$time,$message)=(split '\|',$line);
        eval{
                $logger->set_sender($machine);
                $logger->send($message,$time);
        };
        if($@)
        {
                print "\nFail: $message\n";
                exit;
        }
        else
        {
                print "\nSuccess: $message\n";
        }
        sleep 5;
}

Input File:

test_machine1|1461201306|Command1
test_machine1|1461201311|Command2
test_machine1|1461203214|Command3
test_machine1|1461203219|Command4
test_machine2|1461204005|Command5
test_machine2|1461204006|Command6
test_machine2|1461204149|Command7
test_machine3|1461204154|Command8
test_machine3|1461206936|Command9
test_machine3|1461206942|Command10

Output:

Success: Command1

Success: Command2

Success: Command3

Strace Output:

read(4, "test_machine1|1461201306|Command"..., 4096) = 341
read(4, "", 4096)                       = 0
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=3519, ...}) = 0
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=3519, ...}) = 0
sendto(3, "<110>Apr 20 21:15:06 test_machin"..., 59, 0, NULL, 0) = 59
write(1, "Success Command1\n\n\n", 19Success Command1


)  = 19
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({5, 0}, 0x7ffc707478f0)       = 0
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=3519, ...}) = 0
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=3519, ...}) = 0
sendto(3, "<110>Apr 20 21:15:11 test_machin"..., 59, 0, NULL, 0) = 59
write(1, "Success Command2\n\n\n", 19Success Command2


)  = 19
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({5, 0}, 0x7ffc707478f0)       = 0
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=3519, ...}) = 0
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=3519, ...}) = 0
sendto(3, "<110>Apr 20 21:46:54 test_machin"..., 59, 0, NULL, 0) = 59

I want the script to fail here when it tries to send the third message but it does not.

write(1, "Success Command3\n\n\n", 19Success Command3


)  = 19
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({5, 0}, 0x7ffc707478f0)       = 0
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=3519, ...}) = 0
stat("/etc/localtime", {st_mode=S_IFREG|0644, st_size=3519, ...}) = 0
sendto(3, "<110>Apr 20 21:46:59 test_machin"..., 59, 0, NULL, 0) = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=26037, si_uid=3179} ---
+++ killed by SIGPIPE +++

The script finally dies while trying to send the fourth message. Unfortunately the eval is not catching the exception.

like image 783
Gary Vernon Grubb Avatar asked Dec 29 '25 08:12

Gary Vernon Grubb


2 Answers

Try adding a line

$SIG{PIPE} = sub {
die "SIGPIPE";
};

before anything is being sent.

You might also want to try "print" instead of die.

like image 189
Sebastian Avatar answered Jan 01 '26 14:01

Sebastian


You might want to trap SIGPIPE like this:

$SIG{PIPE} = "IGNORE";

From the perlipc:

If you're writing to a pipe, you should also trap SIGPIPE. Otherwise, think of what happens when you start up a pipe to a command that doesn't exist: the open() will in all likelihood succeed (it only reflects the fork()'s success), but then your output will fail--spectacularly. Perl can't know whether the command worked, because your command is actually running in a separate process whose exec() might have failed. Therefore, while readers of bogus commands return just a quick EOF, writers to bogus commands will get hit with a signal, which they'd best be prepared to handle.

Also, have a look at this behavior in a C program when writing to a broken socket.

like image 26
Tudor Constantin Avatar answered Jan 01 '26 12:01

Tudor Constantin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!