Lambda@EdgeでもDeliveryログクラスを使用できるのか確認してみた
Lambda@EdgeでもDeliveryログクラスを使いたい
こんにちは、のんピ(@non____97)です。
皆さんはLambda@EdgeでもDeliveryログクラスを使いたいなと思ったことはありますか? 私はあります。
以下アップデートでLambda関数のログを簡単にS3やDataFirehoseに出力できるようになりました。
ログ確認をリアルタイムに行わない場合やメトリクスフィルターを使ったログ監視が不要である場合において、コストメリットを優先して選択したいこともあるのでは無いでしょうか。
個人的にはLambda@Edgeでも使用できるか気になりました。
特にビューワーリクエストやビューワーレスポンスなど、キャッシュ前に動作する処理については実行回数は必然的に多くなります。
Lambda@Edgeで大量のログ出力をしているシチュエーションは少ないとは思いますが、チリツモというやつです。
AWS公式ドキュメントを見てもLambda@EdgeのDeliveryログクラスについての言及はされていませんでした。
Lambda features
The following Lambda features are not supported by Lambda@Edge:
- Lambda runtime management configurations other than Auto (default)
- Configuration of your Lambda function to access resources inside your VPC
- Lambda function dead letter queues
- Lambda environment variables (except for reserved environment variables, which are automatically supported)
- Lambda functions with Managing AWS Lambda dependencies with layers
- Using AWS X-Ray
- Lambda provisioned concurrency
Note
Lambda@Edge functions share the same Regional concurrency capabilities as all Lambda functions. For more information, see Quotas on Lambda@Edge.- Create a Lambda function using a container image
- Lambda functions that use the arm64 architecture
- Lambda functions with more than 512 MB of ephemeral storage
- Using a customer managed key to encrypt your .zip deployment packages
実際に試して確認しましょう。
いきなりまとめ
- Lambda@EdgeではDeliveryログクラスは実質使用できない
- Lambda@Edgeにデプロイ済みのLambda関数に設定しようとすると
This operation is only supported on the Standard log class.
とエラーになる - 出力先のS3バケットとDelviryログクラスのロググループが同じリージョンでなければならない
- Lambda@Edgeにデプロイ済みのLambda関数に設定しようとすると
- Deliveryログクラスに出力されたログはマネジメントコンソールから確認することはできない
- Deliveryログクラスに出力されたログはサブスクリプションフィルターを用いてS3バケットに出力される
- マネジメントコンソールからその設定を確認することはできない
- AWS CLIからは確認可能
- Deliveryログクラスを介してS3バケットに出力されたログオブジェクトはzstdで圧縮されている
- Deliveryログクラスを介してS3バケットに出力されたログオブジェクトのキーは
AWSLogs/<AWSアカウントID>/<リージョン名>/_<CloudWatch Logsロググループ名>/YYYY/MM/DD/HH/_<CloudWatch Logsロググループ名>_YYYY-MM-DD-HH_<ランダムな文字列>.log.zst
- Deliveryログクラスを介してS3バケットに出力されたログオブジェクトの中身はJSON Linesで出力されている
やってみた
既存のLambda@Edgeのログ設定を変更してみる
ちょうどオリジンリクエストをトリガーに動作するLambda@Edgeがあったので、こちらの既存のLambda@Edgeのログ設定を変更してみます。
現在のログ設定は以下のとおりです。
Deliveryログクラスを使用する際にはS3バケットおよび、IAMロールの事前作成が必要なので作成しておきます。
S3バケットはデフォルトの設定で作成しました。
IAMロールは以下AWS公式ドキュメントを参考に作成します。
# 信頼ポリシーのファイル用意
> vi TrustPolicyForCWL.json
> cat TrustPolicyForCWL.json
{
"Statement": {
"Effect": "Allow",
"Principal": { "Service": "logs.amazonaws.com" },
"Condition": {
"StringLike": {
"aws:SourceArn": "arn:aws:logs:*:<AWSアカウントID>:*"
}
},
"Action": "sts:AssumeRole"
}
}
# IAMロールの作成
> aws iam create-role \
--role-name CWLtoS3Role \
--assume-role-policy-document file://TrustPolicyForCWL.json
{
"Role": {
"Path": "/",
"RoleName": "CWLtoS3Role",
"RoleId": "AROA6KUFAVPU4KOGWNFQO",
"Arn": "arn:aws:iam::<AWSアカウントID>:role/CWLtoS3Role",
"CreateDate": "2025-05-12T00:45:12+00:00",
"AssumeRolePolicyDocument": {
"Statement": {
"Effect": "Allow",
"Principal": {
"Service": "logs.amazonaws.com"
},
"Condition": {
"StringLike": {
"aws:SourceArn": "arn:aws:logs:*:<AWSアカウントID>:*"
}
},
"Action": "sts:AssumeRole"
}
}
}
}
# IAMポリシーのファイル用意
> vi PermissionsForCWL.json
> cat PermissionsForCWL.json
{
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:PutObject"],
"Resource": ["arn:aws:s3:::non-97-lambda-log-test/*"]
}
]
}
# IAMロールにインラインポリシーをアタッチ
> aws iam put-role-policy \
--role-name CWLtoS3Role \
--policy-name Permissions-Policy-For-S3 \
--policy-document file://PermissionsForCWL.json
これでログ出力先をS3に設定しようとしてみます。
Role AROA6KUFAVPU6347NJTRH trusts too many services, expected only 1
となりましたね。
過剰なサービスを信頼となっているので、もしかすると信頼ポリシーで"aws:SourceArn": "arn:aws:logs:*:<AWSアカウントID>:*"
としたのが良くなかったのでしょうか。
該当箇所を"aws:SourceArn": "arn:aws:logs:us-east-1:<AWSアカウントID>:*"
に変更して再度設定しようとしてみます。
今度はThe specified log group already exists
と表示されました。
実際にマネジメントコンソールをCloudWatch Logsを確認すると、こちらのロググループが作成されていました。
既存のロググループを指定して、設定しようとしてみます。
はい、変わらずRole AROA6KUFAVPU6347NJTRH trusts too many services, expected only 1
です。
Lambda will not send logs to any destination if you save. This log group doesn't have a configured destination. To enable logging, either select a log group with a configured destination or create a new delivery log group before saving.
というメッセージも気になりますね。
Lambda@EdgeではないLambda関数にDeliveryログクラスのログ設定を実施
問題切り分けのために、Lambda@EdgeではないLambda関数にDeliveryログクラスのログ設定を実施します。
適当にNode.js 22のLambda関数を用意しました。コードもデフォルトのものにconsole.info()
を一行追加しただけです。
export const handler = async (event) => {
// TODO implement
console.info("これはテストですよ")
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
こちらのLambda関数に先ほど作成されたCloudWatch Logsロググループを指定します。
特にエラーはなく、設定できました。
このロググループに設定されているサブスクリプションフィルターを確認すると、S3バケットを出力先とするサブスクリプションフィルターが設定されていました。
> aws logs describe-subscription-filters --log-group-name /aws/lambda/WebsiteStack-ContentsDeliveryConstructRewriteToWeb-qBTJvrRRqQ0Q/s3-delivery
{
"subscriptionFilters": [
{
"filterName": "lambda-logs-delivery",
"logGroupName": "/aws/lambda/WebsiteStack-ContentsDeliveryConstructRewriteToWeb-qBTJvrRRqQ0Q/s3-delivery",
"filterPattern": "",
"destinationArn": "arn:aws:s3:::non-97-lambda-log-test",
"roleArn": "arn:aws:iam::<AWSアカウントID>:role/CWLtoS3Role",
"distribution": "ByLogStream",
"applyOnTransformedLogs": false,
"creationTime": 1747010764054
}
]
}
AWSマネジメントコンソールからはサブスクリプションフィルターの確認および、出力先をS3バケットとしたサブスクリプションフィルターの作成はできないようです。
ログの確認
Lambda関数を実行して、ログを確認しましょう。
実行すると、ログストリームが作成されていました。
こちらのログストリーム内に出力されたログを確認しようとしましたが、This operation is only supported on the Standard log class.
と確認できませんでした。
Logsからはログは確認できない仕様なのですね。
出力先のS3バケットを確認すると、確かにログが出力されていました。
オブジェクトのキーはAWSLogs/<AWSアカウントID>/<リージョン名>/_<CloudWatch Logsロググループ名>/YYYY/MM/DD/HH/_<CloudWatch Logsロググループ名>_YYYY-MM-DD-HH_<ランダムな文字列>.log.zst
となっていました。
ロググループ名と日時がプレフィックスに含まれているため、分析はしやすそうです。
私が気になったのでは拡張子がzst
になっていることです。
通常、サブスクリプションフィルターを介して送信されるログはgzip形式で圧縮されます。
Amazon Kinesis Data Streams、、Amazon Data Firehose AWS Lambda、または Amazon OpenSearch Service でサブスクリプションフィルターを使用できます。サブスクリプションフィルターを介してサービスに送信されるログは、base64 でエンコードされ、gzip 形式で圧縮されます。
今回もサブスクリプションフィルターを使っているのですが、zstdで圧縮されているのはちょっと気になりますね。
ログオブジェクトがPUTされた際にLambda関数で加工する場合は意識するといいでしょう。
ログオブジェクトをダウンロードして、実際のログの中身を確認しましょう。
> unzstd _aws_lambda_WebsiteStack-ContentsDeliveryConstructRewriteToWeb-qBTJvrRRqQ0Q_s3-delivery_2025-05-12-00_78cc46cb352b.log.zst
_aws_lambda_WebsiteStack-ContentsDeliveryConstructRewriteToWeb-qBTJvrRRqQ0Q_s3-delivery_2025-05-12-00_78cc46cb352b.log.zst: 594 bytes
> unzstd _aws_lambda_WebsiteStack-ContentsDeliveryConstructRewriteToWeb-qBTJvrRRqQ0Q_s3-delivery_2025-05-12-00_98b39e16f38e.log.zst
_aws_lambda_WebsiteStack-ContentsDeliveryConstructRewriteToWeb-qBTJvrRRqQ0Q_s3-delivery_2025-05-12-00_98b39e16f38e.log.zst: 1773 bytes
> cat _aws_lambda_WebsiteStack-ContentsDeliveryConstructRewriteToWeb-qBTJvrRRqQ0Q_s3-delivery_2025-05-12-00_78cc46cb352b.log
{"accountId":"<AWSアカウントID>","logGroup":"/aws/lambda/WebsiteStack-ContentsDeliveryConstructRewriteToWeb-qBTJvrRRqQ0Q/s3-delivery","logStream":"2025/05/12/test[$LATEST]ce10100b55ec46c1b480c63109e545d9","id":"38959656261330376061811538787577386245389683549139304448","timestamp":1747011407668,"message":"{\"time\":\"2025-05-12T00:56:47.668Z\",\"type\":\"platform.report\",\"record\":{\"requestId\":\"43299cb9-daee-42f1-ab6f-271d864cecb7\",\"metrics\":{\"durationMs\":23.665,\"billedDurationMs\":24,\"memorySizeMB\":128,\"maxMemoryUsedMB\":74,\"initDurationMs\":139.135},\"status\":\"success\"}}"}
> cat _aws_lambda_WebsiteStack-ContentsDeliveryConstructRewriteToWeb-qBTJvrRRqQ0Q_s3-delivery_2025-05-12-00_98b39e16f38e.log
{"accountId":"<AWSアカウントID>","logGroup":"/aws/lambda/WebsiteStack-ContentsDeliveryConstructRewriteToWeb-qBTJvrRRqQ0Q/s3-delivery","logStream":"2025/05/12/test[$LATEST]ce10100b55ec46c1b480c63109e545d9","id":"38959656257227038945281904118749910946317742121937993728","timestamp":1747011407484,"message":"{\"time\":\"2025-05-12T00:56:47.484Z\",\"type\":\"platform.initStart\",\"record\":{\"initializationType\":\"on-demand\",\"phase\":\"init\",\"runtimeVersion\":\"nodejs:22.v39\",\"runtimeVersionArn\":\"arn:aws:lambda:us-east-1::runtime:576ec84cd71c8caab28ea1e9274c074e78b669789b1bf4e6dec43ba6924336a3\",\"functionName\":\"test\",\"functionVersion\":\"$LATEST\",\"instanceId\":\"2025/05/12/test[$LATEST]ce10100b55ec46c1b480c63109e545d9\",\"instanceMaxMemory\":134217728}}"}
{"accountId":"<AWSアカウントID>","logGroup":"/aws/lambda/WebsiteStack-ContentsDeliveryConstructRewriteToWeb-qBTJvrRRqQ0Q/s3-delivery","logStream":"2025/05/12/test[$LATEST]ce10100b55ec46c1b480c63109e545d9","id":"38959656260393744763473252604847982941033809455787212801","timestamp":1747011407626,"message":"{\"time\":\"2025-05-12T00:56:47.626Z\",\"type\":\"platform.start\",\"record\":{\"requestId\":\"43299cb9-daee-42f1-ab6f-271d864cecb7\",\"functionArn\":\"arn:aws:lambda:us-east-1:<AWSアカウントID>:function:test\",\"version\":\"$LATEST\"}}"}
{"accountId":"<AWSアカウントID>","logGroup":"/aws/lambda/WebsiteStack-ContentsDeliveryConstructRewriteToWeb-qBTJvrRRqQ0Q/s3-delivery","logStream":"2025/05/12/test[$LATEST]ce10100b55ec46c1b480c63109e545d9","id":"38959656260438346253870313851131054377579106178799173634","timestamp":1747011407628,"message":"{\"timestamp\":\"2025-05-12T00:56:47.628Z\",\"level\":\"INFO\",\"requestId\":\"43299cb9-daee-42f1-ab6f-271d864cecb7\",\"message\":\"これはテストですよ\"}"}
JSON Lines形式でログ出力されていますね。
ちょっと見づらいので1レコードだけパースします。
{
"accountId": "<AWSアカウントID>",
"logGroup": "/aws/lambda/WebsiteStack-ContentsDeliveryConstructRewriteToWeb-qBTJvrRRqQ0Q/s3-delivery",
"logStream": "2025/05/12/test[$LATEST]ce10100b55ec46c1b480c63109e545d9",
"id": "38959656260438346253870313851131054377579106178799173634",
"timestamp": 1747011407628,
"message": "{\"timestamp\":\"2025-05-12T00:56:47.628Z\",\"level\":\"INFO\",\"requestId\":\"43299cb9-daee-42f1-ab6f-271d864cecb7\",\"message\":\"これはテストですよ\"}"
}
ログメッセージと時刻だけではなく、1レコードごとにアカウントIDやロググループ名、ログストリーム、IDが記録されていますね。
messageはJSON文字列になっているので、加工する際にも意識が必要そうです。
Deliveryログクラスに出力する設定をしているLambda関数をLambda@Edgeにデプロイ
Deliveryログクラスに出力する設定をしているLambda関数をLambda@Edgeにデプロイするとどのような挙動をするのか確認します。
Lambda@Edgeにデプロイします。
デプロイが完了した後、ログ設定を何も変更を加えず保存しようとします。
はい、Role AROA6KUFAVPU6347NJTRH trusts too many services, expected only 1
とエラーになりましたね。
ということで、Lambda@EdgeはDeliveryログクラスをサポートしていないようです。
試しに、Lambda@Edgeが動作するように、CloudFrontで配信しているWebサイトにアクセスします。
> curl -I https://www.non-97.net/test.html
HTTP/2 502
content-type: text/html
content-length: 1074
server: CloudFront
date: Mon, 12 May 2025 02:39:45 GMT
x-cache: LambdaValidationError from cloudfront
via: 1.1 b975fe7c5ea4d03e3d0250a217f79d38.cloudfront.net (CloudFront)
x-amz-cf-pop: KIX56-C1
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: rLJ-cqJ0AyBHii4Id2gVRzHqgzE8RpkimGjCRrQvfXSE6rgkQeaiHw==
x-xss-protection: 1; mode=block
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
Lambda@Edgeに適したコードではないので、502エラーが返ってきました。
自宅からアクセスしたため東京リージョンのロググループを確認します。
Deliveryログクラスにはなっていませんね。
ログも普通に確認できました。
us-east-1のCloudShellからもアクセスしてみましょう。
us-east-1はDeliveryログクラスがあるので、S3にログ出力されていますね。
実際のログを確認すると、確かにus-east-1のCloudShellからアクセスした際の時刻でログが記録されていました。
> unzstd _aws_lambda_WebsiteStack-ContentsDeliveryConstructRewriteToWeb-qBTJvrRRqQ0Q_s3-delivery_2025-05-12-02_0ca625c5b301.log.zst
_aws_lambda_WebsiteStack-ContentsDeliveryConstructRewriteToWeb-qBTJvrRRqQ0Q_s3-delivery_2025-05-12-02_0ca625c5b301.log.zst: 2397 bytes
> cat _aws_lambda_WebsiteStack-ContentsDeliveryConstructRewriteToWeb-qBTJvrRRqQ0Q_s3-delivery_2025-05-12-02_0ca625c5b301.log
{"accountId":"<AWSアカウントID>","logGroup":"/aws/lambda/WebsiteStack-ContentsDeliveryConstructRewriteToWeb-qBTJvrRRqQ0Q/s3-delivery","logStream":"2025/05/12/us-east-1.test[1]5323e87e05314365a72f149fb4036c19","id":"38959811245934319550009725075274472742431372755549814784","timestamp":1747018357418,"message":"{\"time\":\"2025-05-12T02:52:37.418Z\",\"type\":\"platform.initStart\",\"record\":{\"initializationType\":\"on-demand\",\"phase\":\"init\",\"runtimeVersion\":\"nodejs:22.v39\",\"runtimeVersionArn\":\"arn:aws:lambda:us-east-1::runtime:576ec84cd71c8caab28ea1e9274c074e78b669789b1bf4e6dec43ba6924336a3\",\"functionName\":\"us-east-1.test\",\"functionVersion\":\"1\",\"instanceId\":\"2025/05/12/us-east-1.test[1]5323e87e05314365a72f149fb4036c19\",\"instanceMaxMemory\":134217728}}"}
{"accountId":"<AWSアカウントID>","logGroup":"/aws/lambda/WebsiteStack-ContentsDeliveryConstructRewriteToWeb-qBTJvrRRqQ0Q/s3-delivery","logStream":"2025/05/12/us-east-1.test[1]5323e87e05314365a72f149fb4036c19","id":"38959811249101025368201073561372544737147440089399033857","timestamp":1747018357560,"message":"{\"time\":\"2025-05-12T02:52:37.560Z\",\"type\":\"platform.start\",\"record\":{\"requestId\":\"9b096f33-a77c-4b21-b5ae-a676049447a3\",\"functionArn\":\"arn:aws:lambda:us-east-1:<AWSアカウントID>:function:us-east-1.test:1\",\"version\":\"1\"}}"}
{"accountId":"<AWSアカウントID>","logGroup":"/aws/lambda/WebsiteStack-ContentsDeliveryConstructRewriteToWeb-qBTJvrRRqQ0Q/s3-delivery","logStream":"2025/05/12/us-east-1.test[1]5323e87e05314365a72f149fb4036c19","id":"38959811249145626858598134807655616173692736812410994690","timestamp":1747018357562,"message":"{\"timestamp\":\"2025-05-12T02:52:37.562Z\",\"level\":\"INFO\",\"requestId\":\"9b096f33-a77c-4b21-b5ae-a676049447a3\",\"message\":\"これはテストですよ\"}"}
{"accountId":"<AWSアカウントID>","logGroup":"/aws/lambda/WebsiteStack-ContentsDeliveryConstructRewriteToWeb-qBTJvrRRqQ0Q/s3-delivery","logStream":"2025/05/12/us-east-1.test[1]5323e87e05314365a72f149fb4036c19","id":"38959811249948453685745237240750902031508077826626289667","timestamp":1747018357598,"message":"{\"time\":\"2025-05-12T02:52:37.598Z\",\"type\":\"platform.report\",\"record\":{\"requestId\":\"9b096f33-a77c-4b21-b5ae-a676049447a3\",\"metrics\":{\"durationMs\":37.315,\"billedDurationMs\":38,\"memorySizeMB\":128,\"maxMemoryUsedMB\":74,\"initDurationMs\":139.192},\"status\":\"success\"}}"}
Lambda@EdgeではDeliveryログクラスは実質使用できない
Lambda@EdgeでもDeliveryログクラスを使用できるのか確認してみました。
結論、「Lambda@Edgeでも動作はするけれども、各リージョンでは動作しない以上本番では使えない」です。
よくよくドキュメントを見ると、S3バケットとロググループが同じリージョンでなければならないという制約事項がありました。
Set up a CloudWatch Logs subscriptions filter to send Lambda function logs to Amazon S3
To send logs from CloudWatch Logs to Amazon S3, you need to create a subscription filter. This filter defines which log events are delivered to your Amazon S3 bucket. Your Amazon S3 bucket must be in the same Region as your log group.
この記事が誰かの助けになれば幸いです。
以上、クラウド事業本部 コンサルティング部の のんピ(@non____97)でした!