はじめに

ご覧いただき、ありがとうございます。

CloudWatch Logs に保存されるログを New Relic へ転送するためには
ロググループに対して、ログ転送用 Lambda 関数が実行されるサブスクリプションフィルターを設定する形になります。
(ログ転送用 Lambda 関数は New Relic で用意してくれています。)

関数のデプロイ手順や設定方法などは、以下のドキュメントに記載されており
このとおりに行えば、基本的には問題ないと思います。

Install and configure New Relic logging for AWS Lambda with CloudWatch logs, so you can use enhanced log management capabilities.

さて、Lambda 関数のログは CloudWatch Logs に保存されるため
New Relic へ転送するためには、やはりサブスクリプションフィルターの設定が必要になります。

関数が少数であれば、特に問題ないですが
例えば、マイクロサービスとして Lambda 関数を利用すると、かなりの数になってしまうことも多いのではないでしょうか。

大量に存在する場合は、工夫して一括で設定したいところです。

シェルスクリプトと AWS CLI を駆使してやってみます。

1 つのロググループに AWS CLI でサブスクリプションフィルターを設定する

まずは、1 つのロググループに対して、AWS CLI で、サブスクリプションフィルターを設定してみます。

aws logs put-subscription-filter \
  --log-group-name "/aws/lambda/<ログを転送する関数名>" \
  --filter-name "<任意のフィルター名>" \
  --filter-pattern '<任意のフィルターパターン>' \
  --destination-arn '<デプロイしたログ転送用 Lambda 関数の ARN>'

下記のドキュメントに記載があるとおり、Lambda 関数のログは /aws/lambda/<Lambda 関数> に保存されます。

Logging and metrics for AWS Lambda – AWS Prescriptive Guidance

複数のロググループに一括でサブスクリプションフィルターを設定する

あるリージョンに存在する全ての Lambda 関数に設定する

まずは、あるリージョンに存在する全ての Lambda 関数に設定してみます。
以下のようなシェルスクリプトで設定できます。

#!/bin/bash

# 対象の Lambda 関数を取得する

MONITORED_FUNCTIONS=$(
  aws lambda list-functions \
    --query 'Functions[*].[FunctionName]' \
    --output text |
    sed '/^<デプロイしたログ転送用 Lambda 関数名>$/d'
)

# サブスクリプションを設定する

for FUNCTION_NAME in ${MONITORED_FUNCTIONS}
do
  aws logs put-subscription-filter \
    --log-group-name "/aws/lambda/${FUNCTION_NAME}" \
    --filter-name "<任意のフィルター名>" \
    --filter-pattern '<任意のフィルターパターン>' \
    --destination-arn '<デプロイしたログ転送用 Lambda 関数の ARN>'
done

最初に、lambda list-functions コマンドで、Lambda 関数を取得しています。
ログ転送用 Lambda 関数自身にサブスクリプションフィルターを設定しようとするとエラーが出てしまうため
予め、ログ転送用 Lambda 関数は sed コマンドで除外しておきます。

あとは、for 文を使い、先述した logs put-subscription-filter コマンドを繰り返すことで
対象の Lambda 関数のロググループに設定しています。
Lambda 関数のログの保存先が決まっているため、簡単に反復処理させられます。

指定したタグが付与されている Lambda 関数のみに設定する

あるタグが付いているリソースだけを監視したいということは、よくあると思います。
そこで、今度は指定したタグが付与されている Lambda 関数のみに設定します。

#!/bin/bash

ACCOUNT_ID='<AWS アカウント ID>'
REGION='<リージョン>'
KEY='<キー>'
VALUE='<値>'

# 対象の Lambda 関数を取得する

MONITORED_FUNCTIONS=$(
  aws resourcegroupstaggingapi get-resources \
    --tag-filters "Key=${KEY},Values=${VALUE}" \
    --resource-type-filters 'lambda:function' \
    --region ${REGION} |
  jq '.ResourceTagMappingList[].ResourceARN' -r |
  sed -e "s/arn:aws:lambda:${REGION}:${ACCOUNT_ID}:function://g"
)

# サブスクリプションを設定する

for FUNCTION_NAME in ${MONITORED_FUNCTIONS}
do
  aws logs put-subscription-filter \
    --log-group-name "/aws/lambda/${FUNCTION_NAME}" \
    --filter-name "<任意のフィルター名>" \
    --filter-pattern '<任意のフィルターパターン>' \
    --destination-arn '<デプロイしたログ転送用 Lambda 関数の ARN>'
done

まず、resourcegroupstaggingapi get-resources コマンドで、Lambda 関数を取得しています。
タグで絞る場合、このコマンドを使うほうが便利です。
関数名だけが欲しいため、jq コマンドと sed コマンドで整形しています。

あとは、先ほどと同様に、for 文を使い、logs put-subscription-filter コマンドを繰り返すことで
対象の Lambda 関数のロググループに設定をしています。

このようにうまく対象を取得することで、一括で設定できるようになります。

なお、転送したログは logs UI や下記の NRQL 文で確認できます。

SELECT * FROM Log

おまけ(削除方法)

aws logs delete-subscription-filter \
  --log-group-name "/aws/lambda/<Lambda 関数名>" \
  --filter-name "<フィルター名>"

Lambda 関数名とフィルター名がわかっていれば、削除することができます。
そのため、filter-newrelic-<Lambda 関数名> のように、フィルター名のフォーマットを決めておくと
追加する際と同様に、一括で処理できて便利です。

最後までご覧いただき、ありがとうございました。