我正在使用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没有捕获异常。
答案 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协议的工作方式,您无法做到这一点。
客户端和服务器通过套接字进行通信。当客户端写入套接字时,它实际上是写入缓冲区;没有迹象表明该消息是否实际发送。
客户端只能知道在某些数据实际发送到服务器后连接已关闭,因此第一次写入缓冲区将成功。
以下是发生的事情:
当您关闭服务器时,它会发送一个TCP FIN
数据包。 FIN
表示连接的一端已完成发送数据,但仍可接收;它并不表示连接已关闭。
客户端成功将第三条日志消息写入套接字缓冲区,因此不会抛出任何异常。
服务器发送一个TCP RST
数据包,表明它不再收听。
由于RST
,操作系统现在知道服务器的TCP连接结束了。当客户端尝试写入套接字缓冲区时,将使用SIGPIPE
发出进程信号,并且写入返回EPIPE
。
脚本在尝试发送第四条消息时最终死亡。不幸的是,eval没有捕获异常。
您没有处理SIGPIPE
,因此您的程序会在发出信号时死亡。在脚本顶部附近添加以下内容以忽略SIGPIPE
:
$SIG{PIPE} = 'IGNORE';
现在您可以根据需要处理send
方法引发的异常。
进一步阅读: