我已将 rsyslog 4.6.4 配置为将邮件日志写入 PostgreSQL 数据库。一切正常,直到日志消息包含反斜杠,如下例所示:
6月12日 11:37:46 dc5 postfix/smtp[26475]: Vk0nYDKdH3sI: to=<[电子邮件保护]>,relay=----.---[---.---.---.---]:25,delay=1.5,delays=0.77/0.07/0.3/0.35,dsn=4.3.0,status=deferred(主机 ----.---[---.---.---.---] 说:451 4.3.0 写入文件 d:\pmta\spool\B\00000414 时出错,status = ERROR_DISK_FULL 在“DATA”中(回复 DATA 命令结束))
以上是写入 /var/log/mail.log 的日志条目。它是正确的。问题是,当发送到以下 SQL 配方时,文件名中的反斜杠字符被解释为转义符:
$template dcdb, "SELECT rsyslog_insert(('%timereported:::date-rfc3339%'::TIMESTAMPTZ)::TIMESTAMP,'%msg:::escape-cc%'::TEXT,'%syslogtag%'::VARCHAR)",STDSQL
:syslogtag, startswith, "postfix" :ompgsql:/var/run/postgresql,dc,root,;dcdb
结果,rsyslog_insert()
存储过程获得以下值msg
:
Vk0nYDKdH3sI: 至=<[电子邮件保护]>,中继 = ----.---[---.---.---.---]:25,延迟 = 1.5,延迟 = 0.77/0.07/0.3/0.35,dsn = 4.3.0,状态 = 延迟(主机 ----.---[199.85.216.241] 说:451 4.3.0 写入文件 d:pmtaspoolB 时出错
文件名中的 、 和被\p
PostgreSQL\s
解释为文字、和后跟一个 NULL 字符,从而提前终止字符串。此行为可以通过以下方式轻松确认:\B
\0
p
s
B
dc=# SELECT 'd:\pmta\spool\B\00000414';
?column?
--------------
d:pmtaspoolB
(1 row)
dc=#
有办法纠正这个问题吗?有没有我在 rsyslog 文档中找不到的方法可以转换\
为\\
?
答案1
首先,你应该真的在传递任意字符串时使用参数化查询和准备好的语句。
(这可能不是你的错——rsyslog 几乎肯定要为这种可怕的事情负责)。
如果你不能切换到更好的查询结构,Postgresencode
函数可能可以帮助你(请参阅此处的文档)——指定一个编码escape
,Postgres 将方便地将您传递给它的字符串中的所有反斜杠加倍。
请注意,如果你觉得自己特别迂腐,你可以使能够standard_conforming_strings
,这使得 Postgres 将\
字符串中的字符视为文字反斜杠,而不是将其视为转义字符的历史 (unix-y) 行为。
这种变化是否可行取决于很多因素……