如何通过Python脚本向syslog-ng服务器发送日志?
解决Python向syslog-ng发送TCP日志的问题
我来帮你分析并解决这两个实际遇到的问题:
1. SysLogHandler触发AttributeError的原因与解决办法
你碰到的AttributeError: 'SysLogHandler' object has no attribute 'socket',本质是连接失败后的连锁反应:当SysLogHandler初始化时连接被拒绝,它内部并没有成功创建socket对象,但Python的logging模块在程序退出时会自动调用所有handler的close()方法,这个方法尝试去关闭一个根本不存在的socket,于是抛出了错误。
修复方案一:提前捕获连接异常
在初始化handler时就捕获连接错误,避免无效的handler被添加到logger中:
import logging import logging.handlers import socket my_logger = logging.getLogger('MyLogger') my_logger.setLevel(logging.DEBUG) try: # 尝试创建并连接SysLogHandler handler = logging.handlers.SysLogHandler( address=('10.10.11.11', 611), socktype=socket.SOCK_STREAM ) my_logger.addHandler(handler) my_logger.debug('this is debug') my_logger.critical('this is critical') except ConnectionRefusedError: print("无法连接到syslog-ng服务器,连接被拒绝") except Exception as e: print(f"初始化SysLogHandler时发生错误: {e}")
修复方案二:自定义安全的SysLogHandler
继承原handler并重写close()方法,先检查socket是否存在再执行关闭操作:
import logging import logging.handlers import socket class SafeSysLogHandler(logging.handlers.SysLogHandler): def close(self): # 先判断socket属性是否存在,避免报错 if hasattr(self, 'socket'): super().close() my_logger = logging.getLogger('MyLogger') my_logger.setLevel(logging.DEBUG) try: handler = SafeSysLogHandler( address=('10.10.11.11', 611), socktype=socket.SOCK_STREAM ) my_logger.addHandler(handler) my_logger.debug('this is debug') my_logger.critical('this is critical') except ConnectionRefusedError: print("连接被拒绝,请检查syslog-ng服务器状态")
2. SocketHandler无输出的问题解析
SocketHandler确实支持TCP传输,但它只是原样发送你传入的日志字符串,而syslog-ng服务器需要符合syslog标准格式的消息(比如RFC 3164或RFC 5424),否则会直接忽略这些不符合格式的内容,导致你看不到输出。
如果一定要用SocketHandler,需要手动构造符合syslog格式的日志内容,比如按照RFC 3164规范:
import logging import logging.handlers import socket from datetime import datetime class FormattedSocketHandler(logging.handlers.SocketHandler): def makePickle(self, record): # 构造syslog标准格式:<优先级> 时间 主机 进程[PID]: 消息 # 优先级 = 设施代码*8 + 日志级别,这里用user设施(1) + 对应日志级别 priority = (1 * 8) + record.levelno timestamp = datetime.now().strftime('%b %d %H:%M:%S') hostname = socket.gethostname() log_msg = f"<{priority}>{timestamp} {hostname} {record.name}[{record.process}]: {record.getMessage()}" return log_msg.encode('utf-8') + b'\n' # syslog通常以换行结尾 my_logger = logging.getLogger('MyLogger') my_logger.setLevel(logging.DEBUG) try: handler = FormattedSocketHandler('10.10.11.11', 611) my_logger.addHandler(handler) my_logger.debug('this is debug') my_logger.critical('this is critical') except ConnectionRefusedError: print("无法连接到syslog-ng服务器")
不过更推荐的方式还是修复SysLogHandler的使用问题,因为它已经内置了syslog格式的封装逻辑,只要服务器可达,就能自动发送符合要求的日志。
额外检查项
- 确认syslog-ng服务器已经配置监听TCP 611端口,可查看配置文件(通常是
/etc/syslog-ng/syslog-ng.conf),确保有类似配置:source s_tcp { tcp(ip(0.0.0.0) port(611)); }; - 服务器可达时,
SysLogHandler的TCP模式可以正常工作,无需手动构造日志格式。
内容的提问来源于stack exchange,提问作者Maria628




