[アップデート] .NET Lambda のオブザーバビリティを簡単に向上出来る「AWS Lambda Powertools for .NET」が GA になりました
いわさです。
本日 AWS Lambda Powertools for .NET が GA になりました。
半年ほど前からプレビューでは使えていたのですが、今回 GA となりプロダクション環境へも採用しやすくなりました。
今回は Lambda Powertools for .NET を使わない場合と使う場合を比較してみたので紹介します。
AWS Lambda Powertools とは
AWS Lambda Powertools 自体は他のランタイムと同じもので主にオブザーバビリティ(可観測性)を向上させるための実装を簡単に導入出来るライブラリです。
DevelopersIO で詳しく紹介された記事もあります。
当時は Python と Java がサポートされていて、半年ほど前に TypeScript (NodeJS) もサポートに追加されています。
今回この .NET 版がついに GA となったという形です。
機能
主な機能は他のランタイムと同じで Tracing / Logger / Metrics となっています。
簡単にまとめると、以下を独自で準備しなくても Powertools の仕組みを使うだけで簡単に利用することが出来るようになります
- X-Ray のサブセグメントごとに計測
- CloudWatch Logs Insights で扱いやすい状態でログ出力
- カスタムメトリクスを出力
Powertools なし
ではまずは Powertools なしでログやトレースを有効化してみます。
いくつかヘルパーライブラリなども存在はしますが、公式ドキュメントの以下を使ってみます。
カスタムメトリクスに関してはかなり準備が面倒なのでやりませんでした。
デプロイと実行
Amazon.Lambda.Templates の lambda.EmptyFunction あたりに上記を組み込んでいきます。
この記事では以下を使って Lambda 関数のスケルトン作成からデプロイと Invoke までやっていきます。
まずは初期化します。
% dotnet new lambda.EmptyFunction The template "Lambda Empty Function" was created successfully.
実装は適当ですが、こんな感じにしました。
using Amazon.Lambda.Core; [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] namespace hoge0301nonpowertool; public class Function { public string FunctionHandler(string input, ILambdaContext context) { var upperCaseString = UpperCaseString(input); LambdaLogger.Log($"Uppercase of '{input}' is {upperCaseString}"); return input.ToUpper(); } private static string UpperCaseString(string input) { try { //hogehoge //カスタムメトリクス出力したかった return input.ToUpper(); } catch (Exception ex) { LambdaLogger.Log(ex.ToString()); throw; } } }
dotnet builkd
-> dotnet lambda deploy-function
でそのままデプロイします。このあたりは Amazon.Lambda.Tools によって非常にスムーズで良い体験です。
デプロイ後に Lambda コンソール上からアクティブトレースを有効化しています。X-Ray を使いためです。
では実行してみましょう。
% dotnet lambda invoke-function hoge0301nonpowertool --payload "fuga" Amazon Lambda Tools for .NET Core applications (5.6.3) Project Home: https://github.com/aws/aws-extensions-for-dotnet-cli, https://github.com/aws/aws-lambda-dotnet Payload: "FUGA" Log Tail: START RequestId: aafd79ea-1b19-4c7f-a8a6-bc8ab7e89893 Version: $LATEST 2023-02-28T22:04:16.325Z aafd79ea-1b19-4c7f-a8a6-bc8ab7e89893 Uppercase of 'fuga' is FUGA END RequestId: aafd79ea-1b19-4c7f-a8a6-bc8ab7e89893 REPORT RequestId: aafd79ea-1b19-4c7f-a8a6-bc8ab7e89893 Duration: 6.12 ms Billed Duration: 7 ms Memory Size: 256 MB Max Memory Used: 62 MB XRAY TraceId: 1-63fe7a60-19a4e3b13b2ed24932d82530 SegmentId: 6770c68e7e96e750 Sampled: true
問題なく実行されました。
観測してみる
では各サービスからどのように確認が出来るのか見てみましょう。
CloudWatch Logs
CloudWatch Logs はLambdaLogger.Log()
で出力した内容がそのまま確認出来ます。
期待どおりといえば期待どおりです。
X-Ray
続いて X-Ray コンソールからトレースマップを確認してみましょう。
こちらも期待どおりで、Invocation サブセグメントで関数ハンドラの実行時間を確認出来ます。
Overhead サブセグメントはランタイムが応答を送信してから次の呼び出しのシグナルを送信するまでの間に発生するフェーズを指しています。
Powertools あり
デプロイと実行
では、続いて Powertools を使って先程に近いことを実現してみましょう。
Lambda Powertools for .NET のリポジトリは以下です。
README にも記載がありますが、Logging/Metrics/Tracing それぞれで個別の NuGet パッケージをインストールする必要があります。
逆にいえば一部だけ使うことも可能ということですね。
- NuGet Gallery | AWS.Lambda.Powertools.Logging 1.0.0
- NuGet Gallery | AWS.Lambda.Powertools.Metrics 1.0.0
- NuGet Gallery | AWS.Lambda.Powertools.Tracing 1.0.0
そして、以下が Lambda Powertools for .NET を使う形で実装されたクラスです。
ハイライト部分に注目してください。
using Amazon.Lambda.Core; using AWS.Lambda.Powertools.Logging; using AWS.Lambda.Powertools.Metrics; using AWS.Lambda.Powertools.Tracing; [assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] namespace hoge0301powertool; public class Function { [Logging(LogEvent = true)] [Tracing] public string FunctionHandler(string input, ILambdaContext context) { var upperCaseString = UpperCaseString(input); Logger.LogInformation($"Uppercase of '{input}' is {upperCaseString}"); return upperCaseString; } [Metrics(CaptureColdStart = true)] [Tracing(SegmentName = "UpperCaseString Method")] private static string UpperCaseString(string input) { try { Metrics.AddMetric("UpperCaseString_Invocations", 1, MetricUnit.Count); return input.ToUpper(); } catch (Exception ex) { Logger.LogError(ex); throw; } } }
Logging
Logging(LogEvent = true)
では着信イベントをログに記録するように Logger に指示するオプションです。機密情報がログに記録されないようにデフォルトではオフになっています。今回は開発環境なのでこちらをオンにしました。
Logger.LogInformation
あるいはLogger.LogError
などを使うことで、JSON の構造化された形式でログを出力することが出来ます。この方法でログ出力される時にはいくつかの追加の情報(ログレベルなど)も自動で追加されるので Insights などからフィルタリングする際にも利用することが出来ます。
今回は使いませんでしたが、AppendKeys
などを使うことで追加のカスタム属性をログに含めることがも出来るので、マルチテナントアプリケーションでテナントコンテキストを埋め込むなど、色々なシーンで使うことが出来そうです。
なお、ログ出力にあたっていくつかの環境変数を指定する必要があります。
変数名 | 役割 |
---|---|
POWERTOOLS_SERVICE_NAME | ログのトレースに使用されるサービス名 |
POWERTOOLS_LOG_LEVEL | ログレベル |
Metrics
メトリクスは主にMetrics.AddMetric
で出力することが出来ます。
仕組みですが、実体としては CloudWatch Logs へのログ出力で EMF(Embedded Metrics Format) に準拠した出力となるのでそのままカスタムメトリクスとして扱うことが出来るようになっています。
またデフォルトはオフになっているのですが、[Metrics(CaptureColdStart = true)]
を指定することでコールドスタートの回数をメトリクスとして取得することが出来ます。
これはだいぶ便利かもしれない。
なお、カスタムメトリクス発行にあたっていくつかの環境変数を指定する必要があります。
変数名 | 役割 |
---|---|
POWERTOOLS_SERVICE_NAME | メトリクスのディメンションに使用されるサービス名 |
POWERTOOLS_METRICS_NAMESPACE | メトリクスの名前空間 |
Tracing
[Tracing]
を使うことで X-Ray トレースが有効化されます。
また[Tracing(SegmentName = "UpperCaseString Method")]
のように任意のメソッドで別のカスタムサブセグメント名としてキャプチャされるように指定することも出来ます。
なお、X-Ray トレースの利用にあたって以下の環境変数を指定する必要があります。
変数名 | 役割 |
---|---|
POWERTOOLS_SERVICE_NAME | X-Ray 名前空間に使用されるサービス名 |
別の環境変数としてPOWERTOOLS_TRACE_DISABLED
もあり、トレースの無効化を環境変数から設定することも出来ます。
実行して観察してみる
いくつかの Lambda 環境変数を指定して、X-Ray アクティブトレースを有効化し、関数を実行してみましょう。
% dotnet lambda invoke-function hoge0301powertool --payload "hoge" Amazon Lambda Tools for .NET Core applications (5.6.3) Project Home: https://github.com/aws/aws-extensions-for-dotnet-cli, https://github.com/aws/aws-lambda-dotnet Payload: "HOGE" Log Tail: START RequestId: 516f9001-0634-4459-92b8-8f8757a1a271 Version: $LATEST 2023-02-28T21:14:49.990Z 516f9001-0634-4459-92b8-8f8757a1a271 info {"ColdStart":true,"XrayTraceId":"1-63fe6ec9-13c7c57773c5262647ebd25f","FunctionName":"hoge0301powertool","FunctionVersion":"$LATEST","FunctionMemorySize":256,"FunctionArn":"arn:aws:lambda:ap-northeast-1:123456789012:function:hoge0301powertool","FunctionRequestId":"516f9001-0634-4459-92b8-8f8757a1a271","Timestamp":"2023-02-28T21:14:49.9293294Z","Level":"Information","Service":"PowertoolsFunction","Name":"AWS.Lambda.Powertools.Logging.Logger","Message":"hoge"} 2023-02-28T21:14:50.229Z 516f9001-0634-4459-92b8-8f8757a1a271 info {"_aws":{"Timestamp":1677618890068,"CloudWatchMetrics":[{"Namespace":"powertools_function","Metrics":[{"Name":"ColdStart","Unit":"Count"}],"Dimensions":[["FunctionName"],["Service"]]}]},"FunctionName":"hoge0301powertool","Service":"PowertoolsFunction","ColdStart":1} 2023-02-28T21:14:50.229Z 516f9001-0634-4459-92b8-8f8757a1a271 info {"_aws":{"Timestamp":1677618890229,"CloudWatchMetrics":[{"Namespace":"powertools_function","Metrics":[{"Name":"UpperCaseString_Invocations","Unit":"Count"}],"Dimensions":[["Service"]]}]},"Service":"PowertoolsFunction","UpperCaseString_Invocations":1} 2023-02-28T21:14:50.229Z 516f9001-0634-4459-92b8-8f8757a1a271 info {"ColdStart":true,"XrayTraceId":"1-63fe6ec9-13c7c57773c5262647ebd25f","FunctionName":"hoge0301powertool","FunctionVersion":"$LATEST","FunctionMemorySize":256,"FunctionArn":"arn:aws:lambda:ap-northeast-1:123456789012:function:hoge0301powertool","FunctionRequestId":"516f9001-0634-4459-92b8-8f8757a1a271","Timestamp":"2023-02-28T21:14:50.2296548Z","Level":"Information","Service":"PowertoolsFunction","Name":"AWS.Lambda.Powertools.Logging.Logger","Message":"Uppercase of 'hoge' is HOGE"} END RequestId: 516f9001-0634-4459-92b8-8f8757a1a271 REPORT RequestId: 516f9001-0634-4459-92b8-8f8757a1a271 Duration: 1188.11 ms Billed Duration: 1189 ms Memory Size: 256 MB Max Memory Used: 77 MB XRAY TraceId: 1-63fe6ec9-13c7c57773c5262647ebd25f SegmentId: 329b8f764796bef2 Sampled: true
うまく実行出来ています。
では各サービスを確認してみましょう。
CloudWatch Logs
CloudWatch Logs には以下のような形式で出力されていました。
コードから埋め込んだ内容はMessage
として設定されていますね。
JSON 形式になっているので、CloudWatch Logs Insights から利用する際は Parse 不要でそのまま属性を指定出来ます。
フラットでプレーンなテキストよりも分析しやすくてかなり良いですね。
CloudWatch メトリクス
まず CloudWatch Logs で次のようにカスタムメトリクスが埋め込まれた EMF のログが出力されていることを確認しました。
CloudWatch のメトリクス機能で確認してみましょう。
次のようにコードから指定したカスタムメトリクスを確認することが出来ています。
また、コールドスタートメトリクスについても同様にカスタムメトリクスとして取得出来るようになっていました。
X-Ray
X-Ray の出力自体は Powertools を使う前も簡単に有効化出来ていましたが、今回はカスタムサブセグメントを指定しているので、ハンドラーの更に内訳を分析することが出来ます。
これはすごく良いですね。
いつも泥臭くデバッグログみたいなの埋め込むこと多かったのですが、こんな簡単に導入出来るとは...。
Amazon.Lambda.Templates でもサポート
今回の Lambda Powertools for .NET ですが、既に Amazon.Lambda.Templates の最新版でもデフォルトで実装されたテンプレートが使えるようになっています。
気になる方はバージョン 6.10 以上をインストールして使ってみてください。
% dotnet new install Amazon.Lambda.Templates::6.10.0 The following template packages will be installed: Amazon.Lambda.Templates::6.10.0 Amazon.Lambda.Templates (version 6.9.0) is already installed, it will be replaced with version 6.10.0. Amazon.Lambda.Templates::6.9.0 was successfully uninstalled. Success: Amazon.Lambda.Templates::6.10.0 installed the following templates: Template Name Short Name Language Tags ------------------------------------------------------------------------------------ -------------------------------------------- -------- -------------------------------- Empty Top-level Function lambda.EmptyTopLevelFunction [C#] AWS/Lambda/Serverless : Lambda Function project configured for deployment using .NET 7's Native AOT feature. lambda.NativeAOT [C#],F# AWS/Lambda/Function Lambda Function with Powertools lambda.Powertools [C#] AWS/Lambda/Function/Powertools :
さいごに
本日は AWS Lambda Powertools for .NET が GA になったので使ってみました。
AWS Lambda Powertools が強力すぎて驚きました。
これは必須で使っていきたいですね。
導入することによってコールドスタートの時間が長くなるとかデメリットがあったりするのかという点は確認しておきたいですね。
また、Java の SnapStart や .NET の Native AOT との相性がまだよくわかってないのでそのあたりも少し気にしたいところです。