From what I understand, when using Lambda proxy integration one must return statusCode and body in order to return any meaningful error information to the HTTP client.
My python code is below:
import json
import traceback
def lambda_handler(event, context):
try:
result = business_logic()
return {
'statusCode': 200,
'body': json.dumps(result)
}
except Exception as e:
error = {
"type": e.__class__.__name__,
"message": str(e),
"traceback": traceback.format_exc()
}
return {
'statusCode': 502,
'body': json.dumps(error)
}
My problem is, I'd like to make use of Lambda and Cloudwatch's handy monitoring interface (e.g. being able to trigger off lambda errors) but since lambda in this code is handling all errors and returning a value, I think lambda will have 0% error rate.
Is there an alternative approach which still uses proxy integration and passes error info?
I think if you want to check your metrics, it's better to check API Gateway's cloudwatch than Lambda's.
Unless you really really want Lambda's cloudwatch to monitor errorrs, you might have through non-proxy integration
It's a bit messier, but since the lambda is responsible for the error handling in the code(response codes/etc.) it might have to also be responsible for creating the metrics than.
You can simply generate the metric via the SDK, for example:
def handler(event: Dict[str, Any], context: LambdaContext) -> Dict:
try:
raise Exception( "")
except Exception:
client = boto3.client('cloudwatch')
response = client.put_metric_data(Namespace=‘my_namespace’, MetricData=[
{
'MetricName': f”my_metric",
'Value': 1,
'Timestamp': time.time()
}
])
return ResponseUtil.build_exception_response(logger)
Related
I am currently trying to read my dynamodb table "saved_measurements" with the partition key "Name". I'm doing this through API Gateway Lambda proxy integration, and have tested my
event['pathParameters']['name'] to be working just fine so that shouldn't be the issue.
However, when I query my dynamodb table with my 'Name', the error appears.
{"message": "Internal server error"}
I have referenced many resources for querying dynamodb but nothing seems to work. I have also tried printing a example string within the response body like "yes" and it works with no issues. Only when I attempt to send my data in my response body do I meet that issue again.
What can I try to resolve this?
import json
import boto3
from boto3.dynamodb.conditions import Key, Attr
def lambda_handler(event, context):
client = boto3.resource('dynamodb')
table = client.Table('saved_measurements')
data = table.query(KeyConditionExpression=Key('Name').eq(event['pathParameters']['name']))
stuff = data.Items
response = {
"statusCode": 200,
"headers": {
"Content-Type": 'application/json',
"Access-Control-Allow-Origin" : "*",
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET"
},
"body": json.dumps(stuff),
"isBase64Encoded": False
}
return response
You code needs some optimising, however i'm not certain it will resolve the issue. Let me share how I would write it:
import json
import boto3
from boto3.dynamodb.conditions import Key, Attr
client = boto3.resource('dynamodb')
table = client.Table('saved_measurements')
def lambda_handler(event, context):
status_code=0
stuff = ""
try:
data = table.query(KeyConditionExpression=Key('Name').eq(event['pathParameters']['name']))
stuff = json.dumps(data['Items'])
status_code = 200
except Exception as e:
print(e)
stuff = e
status_code = 400
response = {
"statusCode": status_code,
"headers": {
"Content-Type": 'application/json',
"Access-Control-Allow-Origin" : "*",
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Methods": "OPTIONS,POST,GET"
},
"body": stuff,
"isBase64Encoded": False
}
return response
Some points on the code:
Use try/except block anytime you are making API requests to ensure you are not returning exceptions to your downstream tasks unexpectedly
I tend not to use dot notation in Python, hence I changed date.Items to data['Items']. It tends to work more often than dot notation.
Create you clients outside of your request handler in Lambda, this allows Lambda to reuse clients across multiple invocations which will improve your latency.
Don't return 200 for every request regardless on how it resulted, return the correct status code so your downstream processes don't get confused.
Give those changes a go, if you don't succeed, please share your APIGW config.
I use raise Exception (" message ") and it returns this on browser
{"errorMessage": "{"httpStatus": 200, "message": "Exception: {\"httpStatus\": 200, \"message\": \"Exception: [InternalServerError] Project not found\"}"}", "errorType": "Exception", "stackTrace": [the stack trace]}
The stack trace causes security issue
If you are using API gateway in front of lambda. You can set reponse mapping template like below, this will override the response error message and response code as well.
#if($inputRoot.toString().contains('InternalServerError'))
{
"message": "Internal Server Error"
}
#set($context.responseOverride.status = 500)
Alternately you can also catch all the exceptions in the lambda and return whatever you like. However this will not override the status code and you would still get 200 even in case of error.
def handler(event, context):
try:
dosomething(event)
except Exception:
retunrn { "message": "Internal Server error" }
def dosomething(event):
.... You business logic.
I've written a small lambda function and connected it to the API Gateway. I added an error regex to match .*Error.* and return status 400 in the API Gateway. The problem I face is that the regex seems to match only if the lambda failed, as this thread suggests.
My lambda function:
import logging
import boto3
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
if int(event['event_id']) == 1:
return {
'statusCode': 200,
"status": "success"
}
elif int(event['event_id']) == 2:
return {
'statusCode': 400,
"status": "Error"
}
else:
raise Exception("Error")
It looks like case 1 works well, it returns status 200 by default. With event_id=3 it returns status 400 from the API Gateway (with the stack trace in the data, which I would like to avoid), but with event_id=2 it returns status 200 with the Error string in the data.
How can I mark the lambda as failed without throwing an exception?
A failed Lambda means there was some code section in Lambda Function that was failed to be executed. In the current code only the third path has such code section (artificially generated Exception).
It is not possible to execute all your code sections in Lambda and still fail.
If you want to handle API Gateway return status code via program, you'll have to use Lambda Integration. With Lambda integration you may return a response like:
{
"statusCode": "400",
body: json.dumps({ "error": "you messed up!" }),
headers: {
"Content-Type": "application/json",
}
}
and you'll get 400 in API response.
I am writing an AWS Lambda Python 3.6 function to use as a Lambda proxy on my API in API Gateway. When writing the Lambda, I am calling a helper function, where if there is an error, raises an exception. API Gateway doesn't like this, as it expects "body," "statusCode," and "headers" in the response from the Lambda, and when an exception is raised in Python, those keys are not provided.
I am wondering if it's possible to raise my custom exception with Lambda proxy in mind, so that I can break out of whatever callee I am in and return from the program fluidly, without having to check for errors from the callee in the caller. Basically, I want to raise an exception, provide my status code, headers, and body, and completely return from the Lambda function with API Gateway recognizing the error.
If you're using Lambda Proxy integration, you are responsible for returning a proper response whether its a success or an exception.
You can do so by catching the exception.
def handler(event, context):
try:
return {
'statusCode': 200,
'body': json.dumps({
'hello': 'world'
})
}
except BadRequestError:
return {
'statusCode': 400,
'body': json.dumps({
'error': 'Bad Request Error'
})
}
except:
return {
'statusCode': 500,
'body': json.dumps({
'error': 'Internal Server Error'
})
}
in node.js you can use:
callback(null, RESPONSE_NO_SUCCESS);
where RESPONSE_NO_SUCCESS is like this:
import json
return {
statusCode: 200,
body: json.dumps({YOUR_ERROR_HERE})
};
This should work as you want you just need to look up how the callback works in python
I'm attempting to create some mock tests for some in-house AWS scripts. Upon creating the boto3 resource and attempting to validate a non-existent stack, I get the following error when I run my python script:
"The requested action failed to validate. Please review the Boto exceptions below for more details."
This would be great except....there is no "Boto exceptions below" like the error mentions, just the command prompt. What does this mean?
Here is some example code:
def function():
resource = boto3.resource('cloudformation')
verify_stack(resource, 'MockStack')
def verify_stack(session, stackName):
try:
stack = session.meta.client.describe_stacks(StackName=stack_name)
except:
return {}
else:
return stack
Just for reference, the stack "MockStack" doesn't exist, and I'm testing to make sure of this. A part of me thinks the error I'm getting is telling me that the stack doesn't exist, which would be correct. Could this be the issue?
Found the solution to my issue. It was a ClientError that I caught by wrapping the verify_stack function in a try/except statement. Here is the correct code:
try:
session = boto3.Session()
resource = session.resource('cloudformation')
sqs_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"QueueGroup": {
"Type": "AWS::SQS::Queue",
"Properties": {
"QueueName": "my-queue",
"VisibilityTimeout": 60,
}
},
},
}
sqs_template_json = json.dumps(sqs_template)
resource.create_stack(
StackName="mock_stack",
TemplateBody=sqs_template_json,
)
response = resource.verify_stack(resource, 'mock_stack')
print response
except ClientError as error:
print error.message