使用perl模块Log :: Syslog :: Fast - 无法捕获异常

时间:2016-04-22 11:25:28

标签: perl exception tcp exception-handling eval

我正在使用Log :: Syslog :: Fast将日志转发到syslog服务器。我正在测试脚本,看看如果syslog服务器突然崩溃它会如何反应。

为了测试我创建了一个包含测试消息的文件,启动了脚本&然后在syslog服务器收到2条消息后关闭syslog服务器。

该脚本发送了第三条消息&然后死了。终止未被eval& ' use warnings 'FATAL' => 'all';'没有用。

有人可以帮助我抓住异常&更优雅地关闭脚本?

这里需要做的是 - 发送Command2后,脚本应该捕获异常&显示:

Fail: Command3

代码提取:

$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;
}

输入文件:

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

输出:

Success: Command1

Success: Command2

Success: Command3

Strace输出:

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

我希望脚本在尝试发送第三条消息时失败,但不是。

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 +++

在尝试发送第四条消息时,脚本终于死了。不幸的是,eval没有捕获异常。

3 个答案:

答案 0 :(得分:1)

你可能想要像这样陷入SIGPIPE:

$SIG{PIPE} = "IGNORE";

来自perlipc

如果你正在写一个管道,你也应该陷入SIGPIPE。否则,想一想当你启动一个不存在的命令时会发生什么:open()很可能会成功(它只反映fork()的成功),但是你的输出会失败 - 非常棒。 Perl无法知道该命令是否有效,因为您的命令实际上是在一个exec()可能失败的单独进程中运行的。因此,虽然虚假命令的读者只返回一个快速的EOF,但伪造命令的编写者会被信号击中,他们最好准备好处理。

另外,在写入损坏的套接字时,请查看C程序中的this behavior

答案 1 :(得分:1)

尝试添加一行

ALOAD 1  # load sut on stack
ICONST_1 # load true on stack
ALOAD 1  # load sut on stack
INVOKEINTERFACE SUT.withDefaultParam$default$2 ()I # call method which returns the value of the default parameter and leave result on stack
INVOKEINTERFACE SUT.withDefaultParam (ZI)I         # call the actual implementation

在发送任何内容之前。

您可能还想尝试&#34; print&#34;而不是死。

答案 2 :(得分:1)

  

我希望脚本在尝试发送第三条消息时失败,但不是。

TL; DR 由于TCP协议的工作方式,您无法做到这一点。

客户端和服务器通过套接字进行通信。当客户端写入套接字时,它实际上是写入缓冲区;没有迹象表明该消息是否实际发送。

客户端只能知道在某些数据实际发送到服务器后连接已关闭,因此第一次写入缓冲区将成功。

以下是发生的事情:

  1. 当您关闭服务器时,它会发送一个TCP FIN数据包。 FIN表示连接的一端已完成发送数据,但仍可接收;它并不表示连接已关闭。

  2. 客户端成功将第三条日志消息写入套接字缓冲区,因此不会抛出任何异常。

  3. 服务器发送一个TCP RST数据包,表明它不再收听。

  4. 由于RST,操作系统现在知道服务器的TCP连接结束了。当客户端尝试写入套接字缓冲区时,将使用SIGPIPE发出进程信号,并且写入返回EPIPE

  5.   

    脚本在尝试发送第四条消息时最终死亡。不幸的是,eval没有捕获异常。

    您没有处理SIGPIPE,因此您的程序会在发出信号时死亡。在脚本顶部附近添加以下内容以忽略SIGPIPE

    $SIG{PIPE} = 'IGNORE';
    

    现在您可以根据需要处理send方法引发的异常。

    进一步阅读: