如何阻止Python AWS Lambda记录未处理异常且保留FunctionError属性
如何阻止Python 3.8 AWS Lambda记录未处理的异常?
我们有一个仅支持直接调用的Python 3.8 AWS Lambda函数,并非通过API Gateway或其他AWS服务触发。我们已配置自定义日志系统,可将日志格式化为第三方服务能解析的规范格式。
简化示例代码
custom_logging.py
import json import logging from contextlib import contextmanager from typing import Generator, List metadata_stack: List[dict] = [{}] @contextmanager def add_metadata(metadata: dict) -> Generator[None, None, None]: metadata_stack.append({ **metadata_stack[-1], **metadata}) yield metadata_stack.pop() class CustomFormatter(logging.Formatter): def formatMessage(self, record: logging.LogRecord) -> str: record.message = json.dumps( { 'status': record.levelname, 'message': record.message, 'metadata': metadata_stack[-1], }, separators=(',',':'), default=str, ) return super().formatMessage(record) def _init_root_logger(): logger = logging.root logger.setLevel(logging.DEBUG) if logger.hasHandlers(): handler = logger.handlers[0] class_name = handler.__class__.__name__ if (class_name == 'LambdaLoggerHandler'): handler.setFormatter(CustomFormatter()) logger.info('Identified AWS Lambda logger') else: logger.info('Unidentified logger handler: ' + class_name) else: logger.info('Identified no logger. Assuming logging to console.') _init_root_logger()
lambda-handler.py
import logging import custom_logging class LambdaHandler: def __init__(self, logger): self.logger = logger def handle(self, event, context): with custom_logging.add_metadata({ 'foo': 'bar' }): try: self.logger.info('Lambda initialised.') self.main() except: self.logger.exception('An error occurred.') raise def main(self): raise ValueError('I have no values.') def handle(event, context): logger = logging.getLogger('LambdaHandler') return LambdaHandler(logger).handle(event, context)
执行测试
当Lambda发生异常时,我们希望它执行失败。测试时通过CLI执行以下命令:
aws lambda invoke --function-name mylambda response.json && cat response.json | jq .
调用响应(第一条命令的输出):
{ "StatusCode": 200, "FunctionError": "Unhandled", "ExecutedVersion": "$LATEST" }
返回的负载(第二条命令的输出):
{ "errorMessage": "I have no values.", "errorType": "ValueError", "requestId": "526834fc-4bf8-4b39-b695-6c0525aecead", "stackTrace": [ " File \"/var/task/lambda_handler.py\", line 22, in handle\n return LambdaHandler(logger).handle(event, context)\n", " File \"/var/task/lambda_handler.py\", line 12, in handle\n self.main()\n", " File \"/var/task/lambda_handler.py\", line 18, in main\n raise ValueError('I have no values.')\n" ] }
整体表现符合预期,但Python崩溃时,AWS会使用默认日志记录器(推测是stderr)记录该异常,而非我们的自定义日志记录器,这会干扰与第三方日志聚合服务的集成。
日志内容
执行后生成的日志如下:
INIT_START Runtime Version: python:3.9.v23 Runtime Version ARN: arn:aws:lambda:ap-southeast-2::runtime:bf3c2cf12ad277455c3732549ac6b43321b08f1eb445e825bd941721e0415621
{ "status": "INFO", "message": "Identified AWS Lambda logger", "metadata": {} }
START RequestId: 526834fc-4bf8-4b39-b695-6c0525aecead Version: $LATEST
{ "status": "INFO", "message": "Lambda initialised.", "metadata": { "foo": "bar" } }
以下日志需优化以将异常纳入JSON格式,此问题将另行处理,暂未干扰集成:
{ "status": "ERROR", "message": "An error occurred.", "metadata": { "foo": "bar" } } Traceback (most recent call last): File "/var/task/lambda_handler.py", line 12, in handle self.main() File "/var/task/lambda_handler.py", line 18, in main raise ValueError('I have no values.') ValueError: I have no values.
我们需要移除以下由AWS的bootstrap.py生成的日志:
[ERROR] ValueError: I have no values. Traceback (most recent call last): File "/var/task/lambda_handler.py", line 22, in handle return LambdaHandler(logger).handle(event, context) File "/var/task/lambda_handler.py", line 12, in handle self.main() File "/var/task/lambda_handler.py", line 18, in main raise ValueError('I have no values.')
END RequestId: 526834fc-4bf8-4b39-b695-6c0525aecead
已尝试的解决方案
我们尝试用try/except包裹整个Lambda函数并显式记录日志,但这会导致客户端响应中丢失"FunctionError": "Unhandled"字段。返回包含"statusCode": 500的负载也无济于事,只会将其作为普通返回负载。
响应内容:
{ "StatusCode": 200, "ExecutedVersion": "$LATEST" }
负载内容:
{ "statusCode": 500 }
这会破坏AWS SDK和Step Functions状态机的原生行为。
如何在保留"FunctionError"属性的同时,避免触发自动错误日志?
内容的提问来源于stack exchange,提问作者Hand-E-Food
相关产品推荐
相关产品推荐

