Python日志流式传输至S3报递归深度超限错误的解决方法
问题根源
这个递归错误的核心原因是boto3客户端本身会使用Python的logging模块输出日志,而你把S3Handler加到了根日志器(logging.getLogger())上。当emit方法调用put_object时,boto3会产生日志,根日志器又会把这些日志交给S3Handler处理,触发新一轮的emit调用,循环往复直到超出递归深度。
解决方法
方法1:给应用日志单独分配Logger
不要把S3Handler绑定到根日志器,而是给你的应用代码创建专属Logger,只给这个Logger添加S3Handler。这样boto3的日志会走默认的handler,不会触发递归。
# 创建应用专属Logger app_logger = logging.getLogger("my_app") app_logger.setLevel(logging.INFO) # 初始化S3Handler并绑定到应用Logger s3_handler = S3Handler(bucket_name='mybucketname', key='path/to/logfile.txt') app_logger.addHandler(s3_handler) # 应用代码中使用这个Logger打日志 app_logger.info("应用启动") app_logger.error("发生错误")
方法2:在emit方法中防止重入
在emit执行期间,通过标志位禁止重入调用,避免递归循环。
class S3Handler(logging.StreamHandler): def __init__(self, bucket_name, key): super().__init__() self.bucket_name = bucket_name self.key = key self.s3_client = boto3.client('s3', endpoint_url=..., aws_access_key_id=..., aws_secret_access_key=...) self._is_processing = False # 重入控制标志 def emit(self, record): # 如果正在处理日志,直接返回,避免递归 if self._is_processing: return try: self._is_processing = True log_entry = self.format(record).encode('utf-8') self.s3_client.put_object(Bucket=self.bucket_name, Key=self.key, Body=log_entry, ACL="public-read") except Exception as e: # 这里不要用logging,避免再次触发递归,直接用print输出错误 print(f"Error while logging to S3: {str(e)}") finally: self._is_processing = False
方法3:限制boto3的日志级别
如果不需要boto3的调试日志,可以把boto3相关日志器的级别设为WARNING或更高,减少递归触发的概率。
# 禁用boto3系列的低级别日志 logging.getLogger('boto3').setLevel(logging.WARNING) logging.getLogger('botocore').setLevel(logging.WARNING) logging.getLogger('s3transfer').setLevel(logging.WARNING)
额外提醒
你当前的代码每次调用put_object都会覆盖S3上的同一个文件,脚本运行结束后只会保留最后一条日志。如果要完整保存所有日志,可以考虑:
- 给日志文件key添加时间戳(比如
path/to/logs/{datetime.now()}.txt),生成独立的日志文件 - 先将日志写入本地文件,再定期批量上传到S3
- 使用S3的追加写入机制(需要结合
Content-Range参数,实现相对复杂)
内容的提问来源于stack exchange,提问作者user984621
相关产品推荐
相关产品推荐

