Registros de registro multilínea en syslog

Así que he configurado mi aplicación Python para iniciar sesión en syslog con SysLogHandler de Python, y todo funciona bien. Excepto para el manejo multilínea. No es que deba emitir registros de multilínea tan mal (lo hago un poco), pero necesito poder leer las excepciones de Python. Estoy usando Ubuntu con rsyslog 4.2.0. Esto es lo que estoy recibiendo:

Mar 28 20:11:59 telemachos root: ERROR 'EXCEPTION'#012Traceback (most recent call last):#012 File "./test.py", line 22, in #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! 

Código de prueba en caso de que lo necesite:

 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") 

Alternativamente, si desea mantener su syslog intacto en una línea para analizar, puede simplemente reemplazar los caracteres al ver el registro.

 tail -f /var/log/syslog | sed 's/#012/\n\t/g' 

OK, lo descubrí finalmente …

rsyslog por defecto escapa a todos los caracteres extraños (ASCII <32), y esto incluye nuevas líneas (así como pestañas y otros). Simplemente agregue esto a su configuración rsyslog para desactivarlo:

 $EscapeControlCharactersOnReceive off 

Otra opción sería crear una subclase de SysLogHandler y anular emit emit() : luego puede llamar a la superclase emit() para cada línea en el texto que se envía. Algo como:

 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)