
本文将介绍如何解决Python logging.handlers.SysLogHandler在发送日志到远程Syslog服务器时可能发生的无限期阻塞问题。通过自定义SysLogHandler并重写其createSocket方法,我们可以为底层套接字设置连接和发送超时,从而确保在服务器无响应时日志发送操作能够及时放弃,提升应用的健壮性。
1. 问题背景:SysLogHandler的阻塞行为
在使用python的 logging 模块配合 logging.handlers.sysloghandler 将日志发送到远程syslog服务器时,如果远程服务器出现故障、网络中断或响应缓慢,日志发送操作可能会无限期地阻塞应用程序。这通常在使用面向连接的协议(如tcp,即 socktype=socket.sock_stream)时尤为明显,因为套接字尝试建立连接或发送数据时会等待服务器响应,若无响应则会一直阻塞,直到操作系统层面的tcp超时(可能长达数分钟)或连接被重置。
原始代码示例中,SysLogHandler 的初始化并未提供设置连接或发送超时的参数,导致在远程Syslog服务器无响应时,splunk_logger.emergency(msg) 等日志发送调用会长时间挂起,影响应用的可用性。
2. 解决方案:自定义SysLogHandler并重写createSocket方法
logging.handlers.SysLogHandler 内部通过 createSocket 方法来创建和初始化用于与Syslog服务器通信的套接字。这个方法在处理器首次尝试发送日志时被调用。虽然 SysLogHandler 没有直接暴露设置超时的方法,但我们可以通过继承 SysLogHandler 类并重写其 createSocket 方法,在套接字创建之后立即对其进行配置,例如设置超时。
具体步骤如下:
继承 logging.handlers.SysLogHandler: 创建一个新的类,例如 SysLogHandlerCustomTimeout。重写 __init__ 方法(可选但推荐): 允许在初始化时传入超时时间。重写 createSocket 方法:首先调用父类的 createSocket 方法来完成套接字的默认创建和初始化。然后,通过访问 self.socket 属性获取到已创建的套接字对象。使用 self.socket.settimeout(timeout_value) 方法为该套接字设置连接和发送超时。
3. 实现细节与示例代码
以下是实现自定义 SysLogHandler 并集成到现有日志发送逻辑中的示例代码。此代码兼容Python 2.7及更高版本。
立即学习“Python免费学习笔记(深入)”;
import loggingimport logging.handlersimport socketimport sysimport time# 假设的Syslog服务器地址和端口# 在实际部署中,请替换为您的远程Syslog服务器地址SyslogServer = '127.0.0.1' # 示例:指向本地回环地址SyslogPort = 514 # TCP Syslog默认端口,如果使用UDP通常是514或5140# 全局日志器字典,用于缓存已配置的日志器loggers = {}class SysLogHandlerCustomTimeout(logging.handlers.SysLogHandler): """ 自定义SysLogHandler,用于在套接字连接和发送时设置超时。 """ def __init__(self, address, socktype=socket.SOCK_STREAM, timeout_seconds=10): # 使用Python 2.7/3 兼容的super() 调用父类构造函数 super(SysLogHandlerCustomTimeout, self).__init__(address, socktype=socktype) self.timeout_seconds = timeout_seconds def createSocket(self): """ 重写createSocket方法,在创建套接字后设置超时。 此方法在处理器首次尝试发送日志时被调用。 """ # 调用父类的createSocket方法来创建套接字 # Python 2.7/3 兼容的super() 调用 super(SysLogHandlerCustomTimeout, self).createSocket() # 检查套接字是否成功创建,并设置超时 if self.socket: self.socket.settimeout(self.timeout_seconds) else: # 理论上父类的createSocket不会返回None,但作为防御性编程可保留 raise IOError("Failed to create socket for SysLogHandler.")def writeSyslog (mtype, msg): """ 发送消息/日志到Syslog服务器。 此函数整合了自定义超时处理器和日志器缓存逻辑。 """ try: global loggers logger_name = 'SplunkLogger' # 定义日志器名称 # 检查日志器是否已存在于缓存中 if loggers.get(logger_name): splunk_logger = loggers.get(logger_name) else: # 如果日志器不存在,则创建并配置它 # 使用自定义的SysLogHandlerCustomTimeout handler = SysLogHandlerCustomTimeout( address = (SyslogServer, SyslogPort), socktype = socket.SOCK_STREAM, # 示例:使用TCP协议 timeout_seconds = 5 # 设置5秒的连接和发送超时 ) # 设置日志格式 formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') handler.setFormatter(formatter) # 获取或创建名为'SplunkLogger'的日志器 splunk_logger = logging.getLogger(logger_name) # 避免重复添加handler,确保只添加一次 if not splunk_logger.handlers: splunk_logger.addHandler(handler) # 将配置好的日志器存入缓存 loggers[logger_name] = splunk_logger # 根据消息类型发送日志 if "emerg" in mtype: splunk_logger.emergency(msg) elif "alert" in mtype: splunk_logger.alert(msg) elif "crit" in mtype: splunk_logger.critical(msg) elif "err" in mtype: splunk_logger.error(msg) elif "warn" in mtype: splunk_logger.warning(msg) elif "notice" in mtype: splunk_logger.notice(msg) elif "info" in mtype: splunk_logger.info(msg) else: splunk_logger.debug(msg) except socket.timeout: # 捕获套接字超时异常 sys.stdout.write(f"ttSyslog sending timed out to {SyslogServer}:{SyslogPort}n") except Exception as e: # 捕获其他可能的网络或I/O异常 sys.stdout.write(f"ttSyslog failed sending to {SyslogServer}:{SyslogPort} with error: {e}n")# 示例用法if __name__ == "__main__": print(f"尝试向 {SyslogServer}:{SyslogPort} 发送日志...") # 假设远程Syslog服务器未运行,或网络不通 # 在这种情况下,日志发送操作将在5秒后因超时而失败 writeSyslog("info", "这是一条测试信息,期望在超时后失败。") time.sleep(1) # 稍作等待,模拟发送多条日志 writeSyslog("error", "另一条错误信息。") print("日志发送尝试完成。请检查控制台输出以确认超时行为。") # 如果有实际的Syslog服务器运行在指定地址和端口,可以测试成功发送 # SyslogServer = 'your_actual_syslog_server_ip' # SyslogPort = 514 # writeSyslog("info", "这是一条成功发送的测试信息。")
4. 注意事项
超时值选择: timeout_seconds 的值应根据实际的网络环境和Syslog服务器的预期响应时间来合理设置。过短的超时可能导致在网络暂时拥堵时正常请求失败,而过长的超时则会降低超时机制的有效性。TCP与UDP:本教程主要针对 socktype=socket.SOCK_STREAM (TCP) 协议,因为TCP是面向连接的,其连接建立和数据发送过程可能阻塞。如果使用 socktype=socket.SOCK_DGRAM (UDP) 协议,UDP是无连接的,sendto 操作通常是立即返回的,不会阻塞等待远程服务器响应。因此,为UDP套接字设置发送超时通常没有实际意义,但连接超时(如果存在)可能仍有用。Python版本兼容性: 示例代码中的 super(Class, self).__init__(…) 和 super(Class, self).createSocket() 语法在Python 2.7和Python 3中均兼容,确保了广泛适用性。异常处理: 捕获 socket.timeout 异常是关键,它允许应用程序在超时发生时执行特定的错误处理逻辑,例如记录错误、通知管理员或尝试重试。除了 socket.timeout,也应考虑捕获其他可能的 socket.error 或 IOError。日志器管理: 确保日志器和处理器被正确初始化和管理,避免重复添加处理器。在示例代码中,通过 loggers 字典缓存日志器,并检查 splunk_logger.handlers 来避免多次添加同一个处理器。
5. 总结
通过继承 `
以上就是Python日志发送:为SysLogHandler添加连接超时机制的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1376067.html
微信扫一扫
支付宝扫一扫