Lambda@EdgeでもDeliveryログクラスを使用できるのか確認してみた

Lambda@EdgeでもDeliveryログクラスを使用できるのか確認してみた

Lambda@EdgeではDeliveryログクラスは実質使用できない
Clock Icon2025.05.12

Lambda@EdgeでもDeliveryログクラスを使いたい

こんにちは、のんピ(@non____97)です。

皆さんはLambda@EdgeでもDeliveryログクラスを使いたいなと思ったことはありますか? 私はあります。

以下アップデートでLambda関数のログを簡単にS3やDataFirehoseに出力できるようになりました。

https://dev.classmethod.jp/articles/cloudwatch-tiered-pricing-additional-destinations-aws-lambda-logs/

ログ確認をリアルタイムに行わない場合やメトリクスフィルターを使ったログ監視が不要である場合において、コストメリットを優先して選択したいこともあるのでは無いでしょうか。

個人的には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

Restrictions on Lambda@Edge - Amazon CloudFront

実際に試して確認しましょう。

いきなりまとめ

  • Lambda@EdgeではDeliveryログクラスは実質使用できない
    • Lambda@Edgeにデプロイ済みのLambda関数に設定しようとするとThis operation is only supported on the Standard log class.とエラーになる
    • 出力先のS3バケットとDelviryログクラスのロググループが同じリージョンでなければならない
  • 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のログ設定を変更してみます。

2.Lambda@Edgeであること.png

現在のログ設定は以下のとおりです。

1.現在のログ設定.png

Deliveryログクラスを使用する際にはS3バケットおよび、IAMロールの事前作成が必要なので作成しておきます。

S3バケットはデフォルトの設定で作成しました。

IAMロールは以下AWS公式ドキュメントを参考に作成します。

https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-edge-permissions.html

# 信頼ポリシーのファイル用意
>  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に設定しようとしてみます。

5.Role AROA6KUFAVPU6347NJTRH trusts too many services, expected only 1_2.png

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>:*"に変更して再度設定しようとしてみます。

6.The specified log group already exists.png

今度はThe specified log group already existsと表示されました。

実際にマネジメントコンソールをCloudWatch Logsを確認すると、こちらのロググループが作成されていました。

3.コンソールからみたDeliveryのロググループ.png

既存のロググループを指定して、設定しようとしてみます。

7.us-east-1に絞っても結果変わらず.png

はい、変わらず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()を一行追加しただけです。

index.mjs
export const handler = async (event) => {
  // TODO implement
  console.info("これはテストですよ")

  const response = {
    statusCode: 200,
    body: JSON.stringify('Hello from Lambda!'),
  };
  return response;
};

こちらのLambda関数に先ほど作成されたCloudWatch Logsロググループを指定します。

8.Lambda@EdgeではLambda関数.png

特にエラーはなく、設定できました。

10.設定できた.png

このロググループに設定されているサブスクリプションフィルターを確認すると、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バケットとしたサブスクリプションフィルターの作成はできないようです。

9.S3を宛先にしたサブスクリプションフィルターはない.png

ログの確認

Lambda関数を実行して、ログを確認しましょう。

実行すると、ログストリームが作成されていました。

11.Lambdaを実行したらログ出力されていた.png

こちらのログストリーム内に出力されたログを確認しようとしましたが、This operation is only supported on the Standard log class.と確認できませんでした。

12.This operation is only supported on the Standard log class..png

Logsからはログは確認できない仕様なのですね。

出力先のS3バケットを確認すると、確かにログが出力されていました。

13.ログがS3に出力されている.png

14.オブジェクト詳細.png

オブジェクトのキーは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 形式で圧縮されます。

ロググループレベルのサブスクリプションフィルター - Amazon CloudWatch Logs

今回もサブスクリプションフィルターを使っているのですが、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にデプロイします。

15.Lambda@Edgeにデプロイ.png

デプロイが完了した後、ログ設定を何も変更を加えず保存しようとします。

16.Lambda@Edgeデプロイ後にログ設定を保存しようとするとエラーになる.png

はい、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エラーが返ってきました。

自宅からアクセスしたため東京リージョンのロググループを確認します。

19.東京リージョンのロググループ.png

Deliveryログクラスにはなっていませんね。

ログも普通に確認できました。

20.ログ確認.png

us-east-1のCloudShellからもアクセスしてみましょう。

21.us-east-1からアクセス.png

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.

Sending Lambda function logs to Amazon S3 - AWS Lambda

この記事が誰かの助けになれば幸いです。

以上、クラウド事業本部 コンサルティング部の のんピ(@non____97)でした!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.