Greengrass V2生成额外stdout日志包裹Python日志:原因及移除方法
问题描述
将Greengrass组件部署到EC2实例后,Python应用的日志被Greengrass添加的额外内容包裹,示例如下:
2022-12-13T23:59:56.926Z [INFO] (Copier) com.bolt-data.iot.RulesEngineCore: stdout. [2022-12-13 23:59:56,925][DEBUG ][iot-ipc] checking redis pub-sub health (io_thread[140047617824320]:pub_sub_redis:_connection_health_nanny_task:61). {scriptName=services.com.bolt-data.iot.RulesEngineCore.lifecycle.Run, serviceName=com.bolt-data.iot.RulesEngineCore, currentState=RUNNING}
其中加粗部分是Greengrass自动添加的包裹内容,斜体部分是Python日志格式化器生成的原始日志,Python日志配置代码如下:
formatter = logging.Formatter( fmt="[%(asctime)s][%(levelname)-7s][%(name)s] %(message)s (%(threadName)s[%(thread)d]:%(module)s:%(funcName)s:%(lineno)d)" ) # TODO: when we're running in the lambda function, don't stream to stdout _handler = logging.StreamHandler(stream=stdout) _handler.setLevel(get_level_from_environment()) _handler.setFormatter(formatter)
原因分析
Greengrass的Copier组件会负责捕获用户组件的标准输出(stdout)和标准错误(stderr),然后给这些输出加上Greengrass的日志元数据(比如时间戳、组件名称、运行状态等),再转发到Greengrass的日志系统,这就是导致Python日志被额外内容包裹的直接原因。
解决方法
方法1:让Python日志写入独立文件而非stdout
修改Python日志配置,将日志输出到指定文件,绕过stdout避免被Copier捕获:
import logging from pathlib import Path # 定义日志文件路径,可根据组件部署路径调整 log_file_path = Path("/greengrass/v2/logs/your_component_custom.log") formatter = logging.Formatter( fmt="[%(asctime)s][%(levelname)-7s][%(name)s] %(message)s (%(threadName)s[%(thread)d]:%(module)s:%(funcName)s:%(lineno)d)" ) # 替换StreamHandler为FileHandler _handler = logging.FileHandler(log_file_path) _handler.setLevel(get_level_from_environment()) _handler.setFormatter(formatter) # 配置根日志器 root_logger = logging.getLogger() root_logger.addHandler(_handler) root_logger.setLevel(get_level_from_environment())
之后可直接查看该日志文件,或通过Greengrass控制台配置读取自定义日志文件。
方法2:修改Greengrass组件配置,关闭Copier的日志包装
在组件的部署配置(recipe.yaml或部署时的组件配置)中,添加以下配置,禁用Copier对stdout/stderr的捕获包装:
ComponentConfiguration: DefaultConfiguration: aws.greengrass.Copier: stdout: enabled: false stderr: enabled: false
配置生效后,Greengrass不再给Python日志添加额外包裹内容,原始日志会直接输出。
方法3:使用Greengrass日志API输出日志(长期兼容推荐)
改用AWS IoT Greengrass提供的日志SDK输出日志,日志会直接进入Greengrass日志系统,无需经过Copier二次包装:
import time import awsiotgreengrasscoreipc import awsiotgreengrasscoreipc.client as client from awsiotgreengrasscoreipc.model import ( LogLevel, PublishLogEventsRequest ) # 初始化IPC客户端 ipc_client = awsiotgreengrasscoreipc.connect() def greengrass_log(message, level=LogLevel.DEBUG): request = PublishLogEventsRequest( log_events=[ { "message": message, "level": level, "timestamp": int(time.time() * 1000) } ], component_name="com.bolt-data.iot.RulesEngineCore" ) operation = ipc_client.new_publish_log_events() operation.activate(request) future = operation.get_response() future.result() # 使用示例 greengrass_log("checking redis pub-sub health (io_thread[140047617824320]:pub_sub_redis:_connection_health_nanny_task:61)", LogLevel.DEBUG)
内容的提问来源于stack exchange,提问作者Pankesh Patel

