AWS Lambda Powertoolsを触ってみた


<!--原稿ここから-->
<!-- # 目次

1. [はじめに](#anchor1)
2. [触ってみる](#anchor2)
3. [AWS Lambda Powertoolsの導入](#anchor3)
4. [構造化ロギング](#anchor4)
5. [カスタムメトリックの出力](#anchor5)
6. [最後に](#anchor6) -->

<a id="anchor1"></a>
# はじめに
サーバレスの[イベント](https://serverless.connpass.com/event/190055/)で[紹介](https://speakerdeck.com/_kensh/aws-lambda-powertools)された[AWS Lambda Powertools](https://awslabs.github.io/aws-lambda-powertools-python/)が便利そうだったので触ってみました。
（こちらの[セッション](https://pages.awscloud.com/EMEA-field-OE-AWS-Developer-Acceleration-Series-2020-reg-event.html)でも詳しく紹介されています）

AWS Lambda PowertoolsはLambda関数用のユーティリティ集で、
AWS X-Rayによるトレース、構造化ロギング、カスタムメトリックの出力が容易に実装できます。
また、SSM Parameter StoreやSecrets Managerからのパラメータ取得ができるユーティリティも付属しています。

現在の対応ランタイムは[AWS Lambda Powertools Python](https://awslabs.github.io/aws-lambda-powertools-python/)と[AWS Lambda Powertools Java](https://awslabs.github.io/aws-lambda-powertools-java/)の2種類となっています。
Node.jsでは[DAZN Lambda Powertools](https://getndazn.github.io/dazn-lambda-powertools/)があります。

<a id="anchor2"></a>
# 触ってみる

構造化ロギング、カスタムメトリックの出力を試してみます。
LambdaのランタイムはPython3.8を使います。

<a id="anchor3"></a>
# AWS Lambda Powertoolsの導入
[インストール](https://awslabs.github.io/aws-lambda-powertools-python/#install)はPyPIまたはLambda Layerで行います。
今回はLambda Layerで導入してみます。

[Lambda Layer](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-layers.html)は複数のLambda関数でライブラリを共有する機能です。
AWS Lambda PowertoolsをLambda Layerとして作成し、
各Lambda関数にLayerを追加することでライブラリをimportできるようになります。

## Lambda Layerの作成
AWS Lambda PowertoolsのLambda Layerは
[AWS Serverless Application Repository](https://aws.amazon.com/jp/serverless/serverlessrepo/)に公開されているAWS SAM テンプレートから数クリックで作成できます。

Lambda関数の作成画面を開き「Serverless Application Repository の参照」を選択、
検索ウィンドウに「powertools」を入力して「aws-lambda-powertools-python-layer」をデプロイします。

![lambda_001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/611601/8ddca739-338d-e2d1-142f-c29e512047a2.png)

AWS Lambda PowertoolsのLambda Layerを作成するCloud Formationが実行され、 
「aws-lambda-powertools-python-layer」という名前のLambda Layerが作成さます。
![lambda_001-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/611601/8604e871-9606-c036-04e0-fad663e90a74.png)

## Lambda関数へLayerを追加
Lambda関数内のコードからAWS Lambda PowertoolsのライブラリをimportできるようにLambda Layerを追加します。
![lambda_002.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/611601/b52ca3e3-5c68-6877-b104-bdb4441d76d6.png)

<a id="anchor4"></a>
# 構造化ロギング
## Loggerの実装
print文やPythonのLoggerを使ってログ出力をすることができますが、
AWS Lambda Powertoolsを使うと、Jsonで構造化されたログを簡単に出力できます。

AWS Lambda PowertoolsのLoggerをimportし、
INFOログと、ネストしたログを出力してみます。

```python:lambda_function.py
from aws_lambda_powertools import Logger

logger = Logger(service="logger_sample")

def lambda_handler(event, context):
    logger.info("HELLO WORLD")
    logger.info({
        "operation": "collect",
        "status_code": 200
    })
```
他にもこのような便利な機能がありました。

- デコレーターでLambdaのコンテキスト情報(cold_start:bool,memory_size:int)をログに含めたり
https://awslabs.github.io/aws-lambda-powertools-python/core/logger/#capturing-lambda-context-info

- ログに固定で出力する項目を追加したり
https://awslabs.github.io/aws-lambda-powertools-python/core/logger/#appending-additional-keys

- 例外発生時にスタックトレースを出力したり
https://awslabs.github.io/aws-lambda-powertools-python/core/logger/#logging-exceptions

## CloudWatch Logsに出力されたログの確認
先ほど出力したログをCloudWatch Logsで確認してみます。
ログがJSONで構造化されていることが確認できます。
![lambda_003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/611601/272d0485-9ca0-a917-1d7b-f185744544c7.png)

## CloudWatch Logs Insightsによるログ検索
Jsonで構造化されたログを出力すると、CloudWatch Logs Insightsは自動的にフィールドとして認識してくれます。  
特定のフィールドに対してクエリで絞り込みや集計が行えるメリットがあります。
ネストされたJSONフィールドはドットで表記します。
![lambda_004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/611601/6fe8cd03-4750-a23c-baaa-ec0fc091756e.png)


<a id="anchor5"></a>
# カスタムメトリックの出力
boto3の[CloudWatch](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudwatch.html)では、[MetricDataをJSON構造で表記](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cloudwatch.html#CloudWatch.Client.put_metric_data)する必要がありました。

AWS Lambda Powertoolsを使うと、これが数ステップで実装できます。

```python:lambda_function.py
from aws_lambda_powertools import Metrics
from aws_lambda_powertools.metrics import MetricUnit

metrics = Metrics(namespace="001_CustomMetrics")

@metrics.log_metrics
def lambda_handler(event, context):
    metrics.add_dimension(name="environment", value="sample")
    metrics.add_metric(name="sample_count", unit=MetricUnit.Count, value=1)
```

![lambda_005.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/611601/fef12da9-719e-eb62-af23-0b5349f550f6.png)

<a id="anchor6"></a>
# 最後に

AWS Lambda Powertoolsを使うと、可読性も高くシンプルに実装できとても便利ですね！
これからはLambdaでのコード開発で積極的に使ってみたいと思います。

次はAWS Lambda Powertoolsを使ったAWS X-Rayトレースの実装も試してみます。

以上です。