38

所以我已经将我的 Python 应用程序配置为使用 Python 的 SysLogHandler 登录到 syslog,并且一切正常。多行处理除外。并不是说我需要如此糟糕地发出多行日志记录(我做了一点),但我需要能够读取 Python 的异常。我正在使用带有 rsyslog 4.2.0 的 Ubuntu。这就是我得到的:

Mar 28 20:11:59 telemachos root: ERROR 'EXCEPTION'#012Traceback (most recent call last):#012  File "./test.py", line 22, in <module>#012    foo()#012  File "./test.py", line 13, in foo#012    bar()#012  File "./test.py", line 16, in bar#012    bla()#012  File "./test.py", line 19, in bla#012    raise Exception("EXCEPTION!")#012Exception: EXCEPTION!

测试代码以备不时之需:

import logging
from logging.handlers import SysLogHandler

logger = logging.getLogger()
logger.setLevel(logging.INFO)
syslog = SysLogHandler(address='/dev/log', facility='local0')
formatter = logging.Formatter('%(name)s: %(levelname)s %(message)r')
syslog.setFormatter(formatter)
logger.addHandler(syslog)

def foo():
    bar()

def bar():
    bla()

def bla():
    raise Exception("EXCEPTION!")

try:
    foo()
except:
    logger.exception("EXCEPTION")
4

3 回答 3

38

或者,如果您想将系统日志完整地保留在一行以进行解析,则可以在查看日志时替换字符。

tail -f /var/log/syslog | sed 's/#012/\n\t/g'
于 2012-04-26T17:50:51.320 回答
36

好吧,终于想通了……

rsyslog 默认转义所有奇怪的字符(ASCII < 32),这包括换行符(以及制表符和其他)。

$EscapeControlCharactersOnReceive:

该指令指示 rsyslogd 在接收消息期间替换控制字符。目的是提供一种方法来阻止不可打印的消息整体进入 syslog 系统。如果打开此选项,所有控制字符都将转换为 3 位八进制数,并以 $ControlCharacterEscapePrefix 字符为前缀(默认为“\”)。例如,如果 BEL 字符 (ctrl-g) 包含在消息中,它将被转换为“\007”。

您可以简单地将其添加到您的 rsyslog 配置中以将其关闭:

$EscapeControlCharactersOnReceive off

或者,使用“新”高级语法:

global(parser.escapeControlCharactersOnReceive="off")
于 2011-04-04T20:46:21.257 回答
3

另一种选择是将 SysLogHandler 子类化并覆盖- 然后您可以为发送的文本中的每一行emit()调用超类。emit()就像是:

from logging import LogRecord
from logging.handlers import SysLogHandler

class MultilineSysLogHandler(SysLogHandler):
    def emit(self, record):
        if '\n' in record.msg:
            record_args = [record.args] if isinstance(record.args, dict) else record.args
            for single_line in record.msg.split('\n'):
                single_line_record = LogRecord(
                    name=record.name,
                    level=record.levelno,
                    pathname=record.pathname,
                    msg=single_line,
                    args=record_args,
                    exc_info=record.exc_info,
                    func=record.funcName
                )
                super(MultilineSysLogHandler, self).emit(single_line_record)
        else:
            super(MultilineSysLogHandler, self).emit(record)
于 2016-06-02T08:54:23.170 回答