使用 perl 模块 Log::Syslog::Fast - 无法捕获异常
Using perl module Log::Syslog::Fast - Unable to catch exception
我正在使用 Log::Syslog::Fast 将日志转发到系统日志服务器。我正在测试脚本,看看如果系统日志服务器突然崩溃,它会如何反应。
为了测试,我创建了一个包含测试消息的文件,启动脚本,然后在系统日志服务器收到 2 条消息后关闭系统日志服务器。
脚本发送了第三条消息,然后就死了。终止未被 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 没有捕捉到异常。
您可能想像这样捕获 SIGPIPE:
$SIG{PIPE} = "IGNORE";
来自perlipc:
如果要写入管道,还应该捕获 SIGPIPE。否则,想想当你启动一个不存在的命令的管道时会发生什么:open() 很可能会成功(它只反映 fork() 的成功),但你的输出将失败 - -壮观地。 Perl 无法知道该命令是否有效,因为您的命令实际上 运行 在一个单独的进程中,其 exec() 可能已失败。因此,虽然虚假命令的读者 return 只是一个快速的 EOF,但虚假命令的编写者将受到信号的攻击,他们最好准备好处理。
此外,在写入损坏的套接字时,请查看 C 程序中的 this behavior。
尝试添加一行
$SIG{PIPE} = sub {
die "SIGPIPE";
};
在发送任何内容之前。
您可能还想尝试 "print" 而不是死去。
I want the script to fail here when it tries to send the third message but it does not.
TL;DR 由于 TCP 协议的工作方式,您不能这样做。
客户端和服务器通过套接字进行通信。当客户端写入套接字时,它实际上是在写入一个缓冲区;没有迹象表明邮件是否实际送达。
客户端只有在一些数据真正发送到服务器后才能知道连接关闭,所以第一次写入缓冲区会成功。
这是正在发生的事情:
当您关闭服务器时,它会发送一个 TCP FIN
数据包。 A FIN
表示连接的一侧已完成发送数据,但仍可以接收;它并不表示连接已关闭。
客户端成功将您的第三条日志消息写入套接字缓冲区,因此没有抛出异常。
服务器发送一个TCPRST
数据包表示它不再监听。
因为RST
,OS现在知道服务器端的TCP连接已经关闭。当客户端尝试写入套接字缓冲区时,进程会收到 SIGPIPE
信号并写入 returns EPIPE
.
The script finally dies while trying to send the fourth message. Unfortunately the eval is not catching the exception.
你没有处理 SIGPIPE
所以你的程序在收到信号时就死掉了。在脚本顶部附近添加以下内容以忽略 SIGPIPE
:
$SIG{PIPE} = 'IGNORE';
现在您可以随心所欲地处理 send
方法引发的异常。
延伸阅读:
- Writing on a TCP socket closed by the peer
- Writing to a closed, local TCP socket not failing
- write on closed socket doesn't generate sigpipe immediatly
- Programming UNIX Sockets in C - Frequently Asked Questions 第 2.22 节,"When will my application receive SIGPIPE?"
我正在使用 Log::Syslog::Fast 将日志转发到系统日志服务器。我正在测试脚本,看看如果系统日志服务器突然崩溃,它会如何反应。
为了测试,我创建了一个包含测试消息的文件,启动脚本,然后在系统日志服务器收到 2 条消息后关闭系统日志服务器。
脚本发送了第三条消息,然后就死了。终止未被 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 没有捕捉到异常。
您可能想像这样捕获 SIGPIPE:
$SIG{PIPE} = "IGNORE";
来自perlipc:
如果要写入管道,还应该捕获 SIGPIPE。否则,想想当你启动一个不存在的命令的管道时会发生什么:open() 很可能会成功(它只反映 fork() 的成功),但你的输出将失败 - -壮观地。 Perl 无法知道该命令是否有效,因为您的命令实际上 运行 在一个单独的进程中,其 exec() 可能已失败。因此,虽然虚假命令的读者 return 只是一个快速的 EOF,但虚假命令的编写者将受到信号的攻击,他们最好准备好处理。
此外,在写入损坏的套接字时,请查看 C 程序中的 this behavior。
尝试添加一行
$SIG{PIPE} = sub {
die "SIGPIPE";
};
在发送任何内容之前。
您可能还想尝试 "print" 而不是死去。
I want the script to fail here when it tries to send the third message but it does not.
TL;DR 由于 TCP 协议的工作方式,您不能这样做。
客户端和服务器通过套接字进行通信。当客户端写入套接字时,它实际上是在写入一个缓冲区;没有迹象表明邮件是否实际送达。
客户端只有在一些数据真正发送到服务器后才能知道连接关闭,所以第一次写入缓冲区会成功。
这是正在发生的事情:
当您关闭服务器时,它会发送一个 TCP
FIN
数据包。 AFIN
表示连接的一侧已完成发送数据,但仍可以接收;它并不表示连接已关闭。客户端成功将您的第三条日志消息写入套接字缓冲区,因此没有抛出异常。
服务器发送一个TCP
RST
数据包表示它不再监听。因为
RST
,OS现在知道服务器端的TCP连接已经关闭。当客户端尝试写入套接字缓冲区时,进程会收到SIGPIPE
信号并写入 returnsEPIPE
.
The script finally dies while trying to send the fourth message. Unfortunately the eval is not catching the exception.
你没有处理 SIGPIPE
所以你的程序在收到信号时就死掉了。在脚本顶部附近添加以下内容以忽略 SIGPIPE
:
$SIG{PIPE} = 'IGNORE';
现在您可以随心所欲地处理 send
方法引发的异常。
延伸阅读:
- Writing on a TCP socket closed by the peer
- Writing to a closed, local TCP socket not failing
- write on closed socket doesn't generate sigpipe immediatly
- Programming UNIX Sockets in C - Frequently Asked Questions 第 2.22 节,"When will my application receive SIGPIPE?"