SAM Local

這邊使用 sam local start-api 在地端起 Lambda Function 來測試,需要注意的是,這邊需要是會是在地端啟動 Docker ,所以需要將 Docker 預先安裝好。

 1$ sam local start-api
 2
 3Initializing the lambda functions containers.
 4Local image is out of date and will be updated to the latest runtime. To skip this, pass in the parameter --skip-pull-image
 5Building image.....................................................................................................................................................................................................................................................................................................
 6Using local image: public.ecr.aws/lambda/nodejs:20-rapid-x86_64.
 7
 8Mounting /Users/allen/sam-app/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated, inside runtime container
 9Containers Initialization is done.
10Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET]
11You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions,
12changes will be reflected instantly/automatically. If you used sam build before running local commands, you will need to re-run sam build for
13the changes to be picked up. You only need to restart SAM CLI if you update your AWS SAM template
142024-03-24 23:43:10 WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
15 * Running on http://127.0.0.1:3000
162024-03-24 23:43:10 Press CTRL+C to quit

使用 Curl 測試

我們可以看到,Sam 在 local 起的 Application 並聽 3000 port ,這使用可以使用 curl 來測試。

1$ curl http://127.0.0.1:3000/hello
2{"message":"hello world"}

而在原本執行 sam local start-api 的視窗,可以到以下的資訊

1Invoking app.lambdaHandler (nodejs20.x)
2Reuse the created warm container for Lambda function 'HelloWorldFunction'
3Lambda function 'HelloWorldFunction' is already running
4START RequestId: 3475a269-43c0-468d-a6c9-f31fbf2c34fa Version: $LATEST
5END RequestId: 13944b37-b949-480d-865e-8e9ac1c4b0ea
6REPORT RequestId: 13944b37-b949-480d-865e-8e9ac1c4b0ea	Init Duration: 0.06 ms	Duration: 115.87 ms	Billed Duration: 116 ms	Memory Size: 128 MB	Max Memory Used: 128 MB
7
8No Content-Type given. Defaulting to 'application/json'.
92024-03-24 23:45:16 127.0.0.1 - - [24/Mar/2024 23:45:16] "GET /hello HTTP/1.1" 200 -

這邊可以還可以清楚看到,執行時間,和收費時間。雖然 Local 的硬體規格和 AWS 上的機器不同,但還可以作為簡單參考。

插旗除錯

在發中過程中,插旗除錯是最常見的,我在 app.mjs 插入 Line2 將 Event 印出來,但修改後直接執行 sam local start-api ,在 Terminal 中的訊息,看不到剛剛的 Log。

 1export const lambdaHandler = async (event, context) => {
 2    console.log("EVENT: \n" + JSON.stringify(event, null, 2));
 3    const response = {
 4      statusCode: 200,
 5      body: JSON.stringify({
 6        message: 'hello world',
 7      })
 8    };
 9
10    return response;
11  };

之前在第一篇文章有提過 sam buildsam local 是跑 build 完以後的結果,所以直接改 source code 沒有重新 build 是無法生效的。

以下是重新 Build 完以後的結果。

1Invoking app.lambdaHandler (nodejs20.x)
2Reuse the created warm container for Lambda function 'HelloWorldFunction'
3Lambda function 'HelloWorldFunction' is already running
4{"timestamp":"2024-03-24T16:08:27.280Z","level":"INFO","requestId":"fa7a5020-efc9-4791-aaf3-1c9951aaa65a","message":"EVENT: \n{\n  \"body\": null,\n  \"headers\": {\n    \"Accept\": \"*/*\",\n    \"Host\": \"127.0.0.1:3000\",\n    \"User-Agent\": \"curl/7.79.1\",\n    \"X-Forwarded-Port\": \"3000\",\n    \"X-Forwarded-Proto\": \"http\"\n  },\n  \"httpMethod\": \"GET\",\n  \"isBase64Encoded\": false,\n  \"multiValueHeaders\": {\n    \"Accept\": [\n      \"*/*\"\n    ],\n    \"Host\": [\n      \"127.0.0.1:3000\"\n    ],\n    \"User-Agent\": [\n      \"curl/7.79.1\"\n    ],\n    \"X-Forwarded-Port\": [\n      \"3000\"\n    ],\n    \"X-Forwarded-Proto\": [\n      \"http\"\n    ]\n  },\n  \"multiValueQueryStringParameters\": null,\n  \"path\": \"/hello\",\n  \"pathParameters\": null,\n  \"queryStringParameters\": null,\n  \"requestContext\": {\n    \"accountId\": \"123456789012\",\n    \"apiId\": \"1234567890\",\n    \"domainName\": \"127.0.0.1:3000\",\n    \"extendedRequestId\": null,\n    \"httpMethod\": \"GET\",\n    \"identity\": {\n      \"accountId\": null,\n      \"apiKey\": null,\n      \"caller\": null,\n      \"cognitoAuthenticationProvider\": null,\n      \"cognitoAuthenticationType\": null,\n      \"cognitoIdentityPoolId\": null,\n      \"sourceIp\": \"127.0.0.1\",\n      \"user\": null,\n      \"userAgent\": \"Custom User Agent String\",\n      \"userArn\": null\n    },\n    \"path\": \"/hello\",\n    \"protocol\": \"HTTP/1.1\",\n    \"requestId\": \"ce8b7da4-94ef-42c9-a09a-d06a3d5d5fb2\",\n    \"requestTime\": \"24/Mar/2024:16:08:20 +0000\",\n    \"requestTimeEpoch\": 1711296500,\n    \"resourceId\": \"123456\",\n    \"resourcePath\": \"/hello\",\n    \"stage\": \"Prod\"\n  },\n  \"resource\": \"/hello\",\n  \"stageVariables\": null\n}"}
5END RequestId: fa7a5020-efc9-4791-aaf3-1c9951aaa65a
6REPORT RequestId: fa7a5020-efc9-4791-aaf3-1c9951aaa65a	Init Duration: 0.46 ms	Duration: 170.95 ms	Billed Duration: 171 ms	Memory Size: 128 MB	Max Memory Used: 128 MB
7
8No Content-Type given. Defaulting to 'application/json'.
92024-03-25 00:08:27 127.0.0.1 - - [25/Mar/2024 00:08:27] "GET /hello HTTP/1.1" 200 -

測試單個函式

在 Application 的開發中,程式會隨著時間越來越肥,直接跑起整個 Application 所花費的時間也會越來越多。Sam 提供了可以單純測試某個函式的功能,這樣就不用啟整個 Application

 1$ sam local invoke HelloWorldFunction --event events/event.
 2Invoking app.lambdaHandler (nodejs20.x)
 3Local image is up-to-date
 4Using local image: public.ecr.aws/lambda/nodejs:20-rapid-x86_64.
 5
 6Mounting /Users/allen/sam-app/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated, inside runtime container
 7{"timestamp":"2024-03-24T16:10:18.482Z","level":"INFO","requestId":"61c9bf38-f46f-4906-9c25-0d5f77bac250","message":"EVENT: \n{\n  \"body\": \"{\\\"message\\\": \\\"hello world\\\"}\",\n  \"resource\": \"/{proxy+}\",\n  \"path\": \"/path/to/resource\",\n  \"httpMethod\": \"POST\",\n  \"isBase64Encoded\": false,\n  \"queryStringParameters\": {\n    \"foo\": \"bar\"\n  },\n  \"pathParameters\": {\n    \"proxy\": \"/path/to/resource\"\n  },\n  \"stageVariables\": {\n    \"baz\": \"qux\"\n  },\n  \"headers\": {\n    \"Accept\": \"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\",\n    \"Accept-Encoding\": \"gzip, deflate, sdch\",\n    \"Accept-Language\": \"en-US,en;q=0.8\",\n    \"Cache-Control\": \"max-age=0\",\n    \"CloudFront-Forwarded-Proto\": \"https\",\n    \"CloudFront-Is-Desktop-Viewer\": \"true\",\n    \"CloudFront-Is-Mobile-Viewer\": \"false\",\n    \"CloudFront-Is-SmartTV-Viewer\": \"false\",\n    \"CloudFront-Is-Tablet-Viewer\": \"false\",\n    \"CloudFront-Viewer-Country\": \"US\",\n    \"Host\": \"1234567890.execute-api.us-east-1.amazonaws.com\",\n    \"Upgrade-Insecure-Requests\": \"1\",\n    \"User-Agent\": \"Custom User Agent String\",\n    \"Via\": \"1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)\",\n    \"X-Amz-Cf-Id\": \"cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==\",\n    \"X-Forwarded-For\": \"127.0.0.1, 127.0.0.2\",\n    \"X-Forwarded-Port\": \"443\",\n    \"X-Forwarded-Proto\": \"https\"\n  },\n  \"requestContext\": {\n    \"accountId\": \"123456789012\",\n    \"resourceId\": \"123456\",\n    \"stage\": \"prod\",\n    \"requestId\": \"c6af9ac6-7b61-11e6-9a41-93e8deadbeef\",\n    \"requestTime\": \"09/Apr/2015:12:34:56 +0000\",\n    \"requestTimeEpoch\": 1428582896000,\n    \"identity\": {\n      \"cognitoIdentityPoolId\": null,\n      \"accountId\": null,\n      \"cognitoIdentityId\": null,\n      \"caller\": null,\n      \"accessKey\": null,\n      \"sourceIp\": \"127.0.0.1\",\n      \"cognitoAuthenticationType\": null,\n      \"cognitoAuthenticationProvider\": null,\n      \"userArn\": null,\n      \"userAgent\": \"Custom User Agent String\",\n      \"user\": null\n    },\n    \"path\": \"/prod/path/to/resource\",\n    \"resourcePath\": \"/{proxy+}\",\n    \"httpMethod\": \"POST\",\n    \"apiId\": \"1234567890\",\n    \"protocol\": \"HTTP/1.1\"\n  }\n}"}
 8END RequestId: 61c9bf38-f46f-4906-9c25-0d5f77bac250
 9REPORT RequestId: 61c9bf38-f46f-4906-9c25-0d5f77bac250	Init Duration: 0.07 ms	Duration: 171.48 ms	Billed Duration: 172 ms	Memory Size: 128 MB	Max Memory Used: 128 MB
10{"statusCode": 200, "body": "{\"message\":\"hello world\"}"}