[アップデート] AWS Lambda がログを送信する CloudWatch ロググループをカスタマイズ可能になり、複数の Lambda 関数のログを集約できるようになりました

2023.11.18

こんにちは、CX 事業本部 Delivery 部の若槻です。

最近の AWS Lambda アップデートで、ログ出力の仕様を柔軟に設定できる機能が追加されました。

上記アップデートで追加された機能は以下の 3 つです。

  1. Lambda のログを独自のロギングライブラリを使用せずに JSON 構造化形式でキャプチャ可能に
  2. Lambda のログレベル(ERROR, DEBUG, INFO など)をコードを変更することなく制御できる
  3. Lambda がログを送信する CloudWatch ロググループを選択できる

今回はこのうち 3 つ目のアップデートである、Lambda がログを送信する CloudWatch ロググループを選択可能となった 機能について実際に試してみました。

何が嬉しいのか

Lambda 関数では既定では /aws/lambda/< 関数名 > というロググループにログが送信されますが、今回のアップデートにより任意のロググループを指定することも可能になりました。

これにより何が嬉しいのかと言うと、複数の関数のログを 1 つのロググループに集約できるようになることです。単一のアプリケーションや関連する一連の処理が複数の Lambda 関数から構成されている場合、ログの出力先が単一のロググループにまとめられている方が便利な場合もあるためです。

いくつか例を挙げます。

1 つ目は下記で紹介されているような方法で CloudWatch Logs に書き込まれたログを Kinesis Data Firehose で他の場所に配信したい場合に、ロググループが集約されている場合は Lambda 関数の個数分の Firehose をセットアップする必要が無くなり、コスト削減や管理の簡素化につながります。

2 つ目は、マネジメントコンソールから CloudWatch Logs Insights でデバッグを行う際に、複数の Lambda のログをクエリしたい場合でも Lambda の数だけロググループを選択する必要が無くなります。

ロググループを集約しない場合は、クエリする必要があるすべてのロググループを選択する必要があります。

3 つ目は、下記で紹介しているような方法で Lambda のアプリケーションエラーをチャットツールに通知したい場合に、Lambda 関数ごとに CloudWatch Logs メトリクスフィルターや CloudWatch Alarm などのリソースを構成する必要が無くなります。Lambda 関数が多い場合にこの通知の構成でリソース数が増えすぎて Cfn スタックのハードリミットに抵触するなんていう悲劇も防げます。

このように、Lambda がログを送信する CloudWatch ロググループを選択できるようになることで、ログデータの取り回しを簡素化できる場合があり、なかなか嬉しいアップデートになっています。

ただし注意点として、個別のログと出力元の Lambda 関数を紐付けられるようにするための工夫は必要です。以降の「やってみた」では出力される構造化ログに Lambda 関数名および Arn を追加して紐づけ可能とする方法を含め紹介します。

やってみた

検証用リソース作成

ログ出力先のロググループ 1 つと、ログを出力する lambda 関数 2 つを AWS CDK で作成します。

CDK コード

lib/cdk-sample-stack.ts

import {
  aws_lambda_nodejs,
  aws_lambda,
  aws_logs,
  Stack,
  StackProps,
  RemovalPolicy,
} from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class CdkSampleStack extends Stack {
  constructor(scope: Construct, id: string, props: StackProps) {
    super(scope, id, props);

    // ロググループ
    const logGroup = new aws_logs.LogGroup(this, 'CustomLogGroup', {
      logGroupName: '/custom/lambda/sample-app',
      removalPolicy: RemovalPolicy.DESTROY,
    });

    // Lambda 関数 1
    new aws_lambda_nodejs.NodejsFunction(this, 'SampleFunc1', {
      functionName: 'sample-app-func-1',
      runtime: aws_lambda.Runtime.NODEJS_20_X,
      architecture: aws_lambda.Architecture.ARM_64,
      entry: 'src/handler.ts',
      logGroup,
    });

    // Lambda 関数 2
    new aws_lambda_nodejs.NodejsFunction(this, 'SampleFunc2', {
      functionName: 'sample-app-func-2',
      runtime: aws_lambda.Runtime.NODEJS_20_X,
      architecture: aws_lambda.Architecture.ARM_64,
      entry: 'src/handler.ts',
      logGroup,
    });
  }
}

v2.110.0 のリリースより aws_lambda_nodejs.NodejsFunctionaws_lambda.Function)でも今回のロギングの機能が設定可能になっています。そして新規追加された logGroup プロパティでロググループを指定することで、Lambda がログを送信する CloudWatch ロググループを選択できます。

ハンドラーコード

ハンドラーコードは 2 つの Lambda 関数で共通のものを使用します。

src/handler.ts

import { Logger, injectLambdaContext } from '@aws-lambda-powertools/logger';
import middy from '@middy/core';

const logger = new Logger({
  logLevel: 'INFO',
  serviceName: 'sample-app',
});

const lambdaHandler = async (): Promise<void> => {
  logger.info('Hello, world!');
};

export const handler = middy(lambdaHandler).use(injectLambdaContext(logger));

下記で紹介されている Powertools を使用した方法により、構造化ログに Lambda 関数名や Arn などのコンテキスト情報を追加しています。

動作確認

前述のコードでリソースを作成したら、2 つの Lambda 関数を実行してログを確認します。

まず 2 つの関数を実行します。

aws lambda invoke \
  --function-name sample-app-func-1 \
  outputfile.txt
aws lambda invoke \
  --function-name sample-app-func-2 \
  outputfile.txt

実行後にマネジメントコンソールからロググループを確認すると、以下のように 2 つの関数が出力したログが 1 つのロググループに出力されていることが確認できます。

なおログストリームはその名前に関数名を含むので、関数ごとに分けて作成されます。< 関数名 >< エイリアス名 > のようなキーワードでフィルターすれば、目当ての関数のログを含むログストリームのみ確認できるのは地味に嬉しいです。

ログストリームを開くと、それぞれの関数が出力したログが確認できます。

{
    "cold_start": true,
    "function_arn": "arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:sample-app-func-1",
    "function_memory_size": 128,
    "function_name": "sample-app-func-1",
    "function_request_id": "220fa28d-c4d9-46d7-a379-ef922fe1609f",
    "level": "INFO",
    "message": "Hello, world!",
    "service": "sample-app",
    "timestamp": "2023-11-18T16:14:45.069Z",
    "xray_trace_id": "1-6558e2f4-5fc8c0115d8dd2e63e16b9df"
}

{
    "cold_start": true,
    "function_arn": "arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:sample-app-func-2",
    "function_memory_size": 128,
    "function_name": "sample-app-func-2",
    "function_request_id": "2b5e4fde-7b0c-4697-a4aa-a052c5e0e611",
    "level": "INFO",
    "message": "Hello, world!",
    "service": "sample-app",
    "timestamp": "2023-11-18T16:14:46.178Z",
    "xray_trace_id": "1-6558e2f5-52b265b52e4dccaa4754edd3"
}

構造化ログに Lambda 関数名および Arn が追加されているので、それぞれのログがどの Lambda から出力されたものであるかを確認できるようになっていますね。

おわりに

AWS Lambda がログを送信する CloudWatch ロググループをカスタマイズ可能になり、複数の Lambda 関数のログを集約できるようになったのでご紹介しました。

冒頭で紹介したように様々なケースで活用できるアップデートだと思うので、ぜひ試してみてください。

参考

以上