You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何通过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

火山引擎 最新活动