Perl写日志的方法:几行代码搞定网络服务排错记录

ref="/tag/72/" style="color:#479099;font-weight:bold;">网络运维或开发时,经常要盯 Perl 脚本跑的状态——比如监控端口、轮询 API、转发日志。光靠 print 往终端甩信息,一关窗口就没了;用 system('date >> log.txt') 拼凑又容易乱码、没格式、多进程写还冲突。其实 Perl 写日志,不用装一堆模块,原生就能干得挺利索。

最简方案:直接打开文件追加写

适合单脚本、低并发场景,比如定时检查 Nginx 进程是否存活:

open my $log, '>>', '/var/log/check_nginx.log' or die "无法打开日志:$!";
print $log "[" . localtime() . "] nginx 进程正常\n";
close $log;

注意两点:一是用 >> 追加模式,避免覆盖;二是每次写完记得 close,否则缓冲区可能不落盘——尤其在脚本异常退出时,日志就丢了。

带时间戳和级别,更实用一点

手动拼时间太麻烦?用内置的 POSIX::strftime 整个清爽的格式:

use POSIX qw(strftime);

sub log_msg {
    my ($level, $msg) = @_;
    open my $fh, '>>', '/var/log/myapp.log';
    printf $fh "[%s] [%s] %s\n", 
        strftime('%Y-%m-%d %H:%M:%S', localtime), 
        uc($level), 
        $msg;
    close $fh;
}

# 调用示例
log_msg('info', '启动服务');
log_msg('warn', '响应超时,重试第1次');
log_msg('error', '数据库连接失败');

这样查日志时一眼能分清轻重缓急,也方便 grep 过滤,比如 grep '\[ERROR\]' /var/log/myapp.log

多进程别抢同一个文件:用 Sys::Syslog 更稳

如果你的 Perl 程序开了多个 fork(比如 FastCGI 或并行探测),自己 fopen 写文件容易撞车。这时候推荐系统级日志接口:

use Sys::Syslog;

openlog('mycheck', 'pid,ndelay', 'user');

syslog('info', '检测完成:%d 个主机在线', $up_count);
syslog('err', 'SSH 登录失败:%s@%s', $user, $host);

closelog();

它会自动把日志送到 /var/log/messages/var/log/user.log(具体看系统配置),由 syslogd 统一排队、轮转、归档,不怕并发写崩。

不想折腾?Log::Log4perl 真香

如果项目稍大,或者你常写 Perl 且想统一日志风格,cpan Log::Log4perl 装一个,配个简单配置文件:

# log4perl.conf
log4perl.rootLogger              = INFO, FileAppender
log4perl.appender.FileAppender = Log::Log4perl::Appender::File
log4perl.appender.FileAppender.filename = /var/log/mytool.log
log4perl.appender.FileAppender.layout = Log::Log4perl::Layout::PatternLayout
log4perl.appender.FileAppender.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss} [%p] %m%n

然后 Perl 脚本里这么用:

use Log::Log4perl qw(:easy);
Log::Log4perl->init('/path/to/log4perl.conf');

INFO '服务已启动';
WARN '证书7天后过期';
ERROR 'DNS 解析超时:', $domain;

支持日志分级、按大小/时间自动切割、输出到文件/Socket/DB,甚至发邮件告警——但对小工具来说,有点杀鸡用牛刀,按需选。

最后提醒一句:日志路径权限别设错。比如往 /var/log/ 下写,确保运行脚本的用户有写权限,不然静默失败,你还以为“没出问题”。试试 touch /var/log/test.log && echo ok 先探路。