AWS Lambda での Java コードの作成 - AWS Lambda

AWS Lambda での Java コードの作成

Lambda アプリケーションのトレース、デバッグ、および最適化を行うために、Lambda は AWS X-Ray と統合されています。X-Ray を使用すると、Lambda 関数や他の AWS のサービスが含まれるアプリケーション内で、リソースを横断するリクエストをトレースできます。

トレースされたデータを X-Ray に送信するには、以下の 2 つの SDK ライブラリのいずれかを使用します。

  • AWS Distro for OpenTelemetry (ADOT) – 安全で、本番環境に対応し、AWS でサポートされている OpenTelemetry (OTel) SDK のディストリビューションです。

  • AWS X-Ray SDK for Java - トレースデータを生成して X-Ray に送信するための SDK です。

  • Powertools for AWS Lambda (Java) - サーバーレスのベストプラクティスを実装し、デベロッパーの作業速度を向上させるためのデベロッパーツールキットです。

各 SDK は、テレメトリデータを X-Ray サービスに送信する方法を提供します。続いて、X-Ray を使用してアプリケーションのパフォーマンスメトリクスの表示やフィルタリングを行い、インサイトを取得することで、問題点や最適化の機会を特定できます。

重要

X-Ray および Powertools for AWS Lambda SDK は、AWS が提供する、密接に統合された計測ソリューションの一部です。ADOT Lambda レイヤーは、一般的により多くのデータを収集するトレーシング計測の業界標準の一部ですが、すべてのユースケースに適しているわけではありません。これらのソリューションのいずれかを使用して、X-Ray でエンドツーエンドのトレーシングを実装することができます。選択方法の詳細については、「Choosing between the AWS Distro for Open Telemetry and X-Ray SDKs」( Distro for Open Telemetry または X-Ray SDK の選択) を参照してください。

AWS Lambda (Java) に Powertools の使用、トレースに AWS SAM の使用

以下の手順に従い、AWS SAM を使用する統合された Powertools for AWS Lambda (Java) モジュールを備えた Hello World Java アプリケーションのサンプルをダウンロード、構築、デプロイします。このアプリケーションは基本的な API バックエンドを実装し、Powertools を使用してログ、メトリクス、トレースを生成します。Amazon API Gateway エンドポイントと Lambda 関数で構成されています。API Gateway エンドポイントに GET リクエストを送信すると、Lambda 関数は呼び出し、Embedded Metric Format を使用してログおよびメトリクスを CloudWatch に送信、トレースを AWS X-Ray に送信します。関数は hello world のメッセージを返します。

前提条件

このセクションの手順を完了するには、以下が必要です。

AWS SAM サンプルアプリケーションをデプロイする
  1. Hello World Java テンプレートを使用してアプリケーションを初期化します。

    sam init --app-template hello-world-powertools-java --name sam-app --package-type Zip --runtime java11 --no-tracing
  2. アプリケーションを構築します。

    cd sam-app && sam build
  3. アプリケーションをデプロイします。

    sam deploy --guided
  4. 画面に表示されるプロンプトに従ってください。インタラクティブな形式で提供されるデフォルトオプションを受け入れるには、Enter を押します。

    注記

    [HelloWorldFunction には権限が定義されていない場合がありますが、問題ありませんか?] には、必ず y を入力してください。

  5. デプロイされたアプリケーションの URL を取得します。

    aws cloudformation describe-stacks --stack-name sam-app --query 'Stacks[0].Outputs[?OutputKey==`HelloWorldApi`].OutputValue' --output text
  6. API エンドポイントを呼び出します。

    curl -X GET <URL_FROM_PREVIOUS_STEP>

    成功すると、次のレスポンスが表示されます。

    {"message":"hello world"}
  7. 関数のトレースを取得するには、sam traces を実行します。

    sam traces

    トレース出力は次のようになります。

    New XRay Service Graph Start time: 2023-02-03 14:31:48+01:00 End time: 2023-02-03 14:31:48+01:00 Reference Id: 0 - (Root) AWS::Lambda - sam-app-HelloWorldFunction-y9Iu1FLJJBGD - Edges: [] Summary_statistics: - total requests: 1 - ok count(2XX): 1 - error count(4XX): 0 - fault count(5XX): 0 - total response time: 5.587 Reference Id: 1 - client - sam-app-HelloWorldFunction-y9Iu1FLJJBGD - Edges: [0] Summary_statistics: - total requests: 0 - ok count(2XX): 0 - error count(4XX): 0 - fault count(5XX): 0 - total response time: 0 XRay Event [revision 3] at (2023-02-03T14:31:48.500000) with id (1-63dd0cc4-3c869dec72a586875da39777) and duration (5.603s) - 5.587s - sam-app-HelloWorldFunction-y9Iu1FLJJBGD [HTTP: 200] - 4.053s - sam-app-HelloWorldFunction-y9Iu1FLJJBGD - 1.181s - Initialization - 4.037s - Invocation - 1.981s - ## handleRequest - 1.840s - ## getPageContents - 0.000s - Overhead
  8. これは、インターネット経由でアクセス可能なパブリック API エンドポイントです。テスト後にエンドポイントを削除することを推奨します。

    sam delete

AWS Lambda (Java) に Powertools の使用、トレースに AWS CDK の使用

以下の手順に従い、AWS CDK を使用する統合された Powertools for AWS Lambda (Java) モジュールを備えた Hello World Java アプリケーションのサンプルをダウンロード、構築、デプロイします。このアプリケーションは基本的な API バックエンドを実装し、Powertools を使用してログ、メトリクス、トレースを生成します。Amazon API Gateway エンドポイントと Lambda 関数で構成されています。API Gateway エンドポイントに GET リクエストを送信すると、Lambda 関数は呼び出し、Embedded Metric Format を使用してログおよびメトリクスを CloudWatch に送信、トレースを AWS X-Ray に送信します。関数は「hello world」メッセージを返します。

前提条件

このセクションの手順を完了するには、以下が必要です。

AWS CDK サンプルアプリケーションをデプロイする
  1. 新しいアプリケーション用のプロジェクトディレクトリを作成します。

    mkdir hello-world cd hello-world
  2. アプリケーションを初期化します。

    cdk init app --language java
  3. 次のコマンドを備えた Maven プロジェクトを作成します。

    mkdir app cd app mvn archetype:generate -DgroupId=helloworld -DartifactId=Function -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
  4. hello-world\app\Function ディレクトリで pom.xml を開き、既存のコードを、Powertools の依存関係および Maven プラグインを含む次のコードで置き換えます。

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>helloworld</groupId> <artifactId>Function</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>Function</name> <url>http://maven.apache.org</url> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <log4j.version>2.17.2</log4j.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-core</artifactId> <version>1.2.2</version> </dependency> <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-lambda-java-events</artifactId> <version>3.11.1</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>aspectj-maven-plugin</artifactId> <version>1.14.0</version> <configuration> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> <complianceLevel>${maven.compiler.target}</complianceLevel> <aspectLibraries> <aspectLibrary> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-tracing</artifactId> </aspectLibrary> <aspectLibrary> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-metrics</artifactId> </aspectLibrary> <aspectLibrary> <groupId>software.amazon.lambda</groupId> <artifactId>powertools-logging</artifactId> </aspectLibrary> </aspectLibraries> </configuration> <executions> <execution> <goals> <goal>compile</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.4.1</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="com.github.edwgiz.maven_shade_plugin.log4j2_cache_transformer.PluginsCacheFileTransformer"> </transformer> </transformers> <createDependencyReducedPom>false</createDependencyReducedPom> <finalName>function</finalName> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>com.github.edwgiz</groupId> <artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId> <version>2.15</version> </dependency> </dependencies> </plugin> </plugins> </build> </project>
  5. hello-world\app\src\main\resource ディレクトリを作成し、ログ設定に log4j.xml を作成します。

    mkdir -p src/main/resource cd src/main/resource touch log4j.xml
  6. log4j.xml を開いて次のコード追加します。

    <?xml version="1.0" encoding="UTF-8"?> <Configuration> <Appenders> <Console name="JsonAppender" target="SYSTEM_OUT"> <JsonTemplateLayout eventTemplateUri="classpath:LambdaJsonLayout.json" /> </Console> </Appenders> <Loggers> <Logger name="JsonLogger" level="INFO" additivity="false"> <AppenderRef ref="JsonAppender"/> </Logger> <Root level="info"> <AppenderRef ref="JsonAppender"/> </Root> </Loggers> </Configuration>
  7. hello-world\app\Function\src\main\java\helloworld ディレクトリから App.java を開き、既存コードを次のコードで置き換えます。これは Lambda 関数のコードです。

    package helloworld; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import software.amazon.lambda.powertools.logging.Logging; import software.amazon.lambda.powertools.metrics.Metrics; import software.amazon.lambda.powertools.tracing.CaptureMode; import software.amazon.lambda.powertools.tracing.Tracing; import static software.amazon.lambda.powertools.tracing.CaptureMode.*; /** * Handler for requests to Lambda function. */ public class App implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> { Logger log = LogManager.getLogger(App.class); @Logging(logEvent = true) @Tracing(captureMode = DISABLED) @Metrics(captureColdStart = true) public APIGatewayProxyResponseEvent handleRequest(final APIGatewayProxyRequestEvent input, final Context context) { Map<String, String> headers = new HashMap<>(); headers.put("Content-Type", "application/json"); headers.put("X-Custom-Header", "application/json"); APIGatewayProxyResponseEvent response = new APIGatewayProxyResponseEvent() .withHeaders(headers); try { final String pageContents = this.getPageContents("https://checkip.amazonaws.com"); String output = String.format("{ \"message\": \"hello world\", \"location\": \"%s\" }", pageContents); return response .withStatusCode(200) .withBody(output); } catch (IOException e) { return response .withBody("{}") .withStatusCode(500); } } @Tracing(namespace = "getPageContents") private String getPageContents(String address) throws IOException { log.info("Retrieving {}", address); URL url = new URL(address); try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()))) { return br.lines().collect(Collectors.joining(System.lineSeparator())); } } }
  8. hello-world\src\main\java\com\myorg ディレクトリから HelloWorldStack.java を開き、既存コードを次のコードで置き換えます。このコードは、「Lambda Constructor」および「Apigatewayv2 Constructor」を使用して REST API および Lambda 関数を作成します。

    package com.myorg; import software.amazon.awscdk.*; import software.amazon.awscdk.services.apigatewayv2.alpha.*; import software.amazon.awscdk.services.apigatewayv2.integrations.alpha.HttpLambdaIntegration; import software.amazon.awscdk.services.apigatewayv2.integrations.alpha.HttpLambdaIntegrationProps; import software.amazon.awscdk.services.lambda.Code; import software.amazon.awscdk.services.lambda.Function; import software.amazon.awscdk.services.lambda.FunctionProps; import software.amazon.awscdk.services.lambda.Runtime; import software.amazon.awscdk.services.lambda.Tracing; import software.amazon.awscdk.services.logs.RetentionDays; import software.amazon.awscdk.services.s3.assets.AssetOptions; import software.constructs.Construct; import java.util.Arrays; import java.util.List; import static java.util.Collections.singletonList; import static software.amazon.awscdk.BundlingOutput.ARCHIVED; public class HelloWorldStack extends Stack { public HelloWorldStack(final Construct scope, final String id) { this(scope, id, null); } public HelloWorldStack(final Construct scope, final String id, final StackProps props) { super(scope, id, props); List<String> functionPackagingInstructions = Arrays.asList( "/bin/sh", "-c", "cd Function " + "&& mvn clean install " + "&& cp /asset-input/Function/target/function.jar /asset-output/" ); BundlingOptions.Builder builderOptions = BundlingOptions.builder() .command(functionPackagingInstructions) .image(Runtime.JAVA_11.getBundlingImage()) .volumes(singletonList( // Mount local .m2 repo to avoid download all the dependencies again inside the container DockerVolume.builder() .hostPath(System.getProperty("user.home") + "/.m2/") .containerPath("/root/.m2/") .build() )) .user("root") .outputType(ARCHIVED); Function function = new Function(this, "Function", FunctionProps.builder() .runtime(Runtime.JAVA_11) .code(Code.fromAsset("app", AssetOptions.builder() .bundling(builderOptions .command(functionPackagingInstructions) .build()) .build())) .handler("helloworld.App::handleRequest") .memorySize(1024) .tracing(Tracing.ACTIVE) .timeout(Duration.seconds(10)) .logRetention(RetentionDays.ONE_WEEK) .build()); HttpApi httpApi = new HttpApi(this, "sample-api", HttpApiProps.builder() .apiName("sample-api") .build()); httpApi.addRoutes(AddRoutesOptions.builder() .path("/") .methods(singletonList(HttpMethod.GET)) .integration(new HttpLambdaIntegration("function", function, HttpLambdaIntegrationProps.builder() .payloadFormatVersion(PayloadFormatVersion.VERSION_2_0) .build())) .build()); new CfnOutput(this, "HttpApi", CfnOutputProps.builder() .description("Url for Http Api") .value(httpApi.getApiEndpoint()) .build()); } }
  9. hello-world ディレクトリから pom.xml を開き、既存コードを次のコードで置き換えます。

    <?xml version="1.0" encoding="UTF-8"?> <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <modelVersion>4.0.0</modelVersion> <groupId>com.myorg</groupId> <artifactId>hello-world</artifactId> <version>0.1</version> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cdk.version>2.70.0</cdk.version> <constructs.version>[10.0.0,11.0.0)</constructs.version> <junit.version>5.7.1</junit.version> </properties> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>3.0.0</version> <configuration> <mainClass>com.myorg.HelloWorldApp</mainClass> </configuration> </plugin> </plugins> </build> <dependencies> <!-- AWS Cloud Development Kit --> <dependency> <groupId>software.amazon.awscdk</groupId> <artifactId>aws-cdk-lib</artifactId> <version>${cdk.version}</version> </dependency> <dependency> <groupId>software.constructs</groupId> <artifactId>constructs</artifactId> <version>${constructs.version}</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>software.amazon.awscdk</groupId> <artifactId>apigatewayv2-alpha</artifactId> <version>${cdk.version}-alpha.0</version> </dependency> <dependency> <groupId>software.amazon.awscdk</groupId> <artifactId>apigatewayv2-integrations-alpha</artifactId> <version>${cdk.version}-alpha.0</version> </dependency> </dependencies> </project>
  10. hello-world ディレクトリ内に移動していることを確認し、アプリケーションをデプロイしてください。

    cdk deploy
  11. デプロイされたアプリケーションの URL を取得します。

    aws cloudformation describe-stacks --stack-name HelloWorldStack --query 'Stacks[0].Outputs[?OutputKey==`HttpApi`].OutputValue' --output text
  12. API エンドポイントを呼び出します。

    curl -X GET <URL_FROM_PREVIOUS_STEP>

    成功すると、次のレスポンスが表示されます。

    {"message":"hello world"}
  13. 関数のトレースを取得するには、sam traces を実行します。

    sam traces

    トレース出力は次のようになります。

    New XRay Service Graph Start time: 2023-02-03 14:59:50+00:00 End time: 2023-02-03 14:59:50+00:00 Reference Id: 0 - (Root) AWS::Lambda - sam-app-HelloWorldFunction-YBg8yfYtOc9j - Edges: [1] Summary_statistics: - total requests: 1 - ok count(2XX): 1 - error count(4XX): 0 - fault count(5XX): 0 - total response time: 0.924 Reference Id: 1 - AWS::Lambda::Function - sam-app-HelloWorldFunction-YBg8yfYtOc9j - Edges: [] Summary_statistics: - total requests: 1 - ok count(2XX): 1 - error count(4XX): 0 - fault count(5XX): 0 - total response time: 0.016 Reference Id: 2 - client - sam-app-HelloWorldFunction-YBg8yfYtOc9j - Edges: [0] Summary_statistics: - total requests: 0 - ok count(2XX): 0 - error count(4XX): 0 - fault count(5XX): 0 - total response time: 0 XRay Event [revision 1] at (2023-02-03T14:59:50.204000) with id (1-63dd2166-434a12c22e1307ff2114f299) and duration (0.924s) - 0.924s - sam-app-HelloWorldFunction-YBg8yfYtOc9j [HTTP: 200] - 0.016s - sam-app-HelloWorldFunction-YBg8yfYtOc9j - 0.739s - Initialization - 0.016s - Invocation - 0.013s - ## lambda_handler - 0.000s - ## app.hello - 0.000s - Overhead
  14. これは、インターネット経由でアクセス可能なパブリック API エンドポイントです。テスト後にエンドポイントを削除することを推奨します。

    cdk destroy

Java 関数の計測への ADOT の使用

ADOT は、Otel SDK を使用してテレメトリデータを収集するために必要なすべてをパッケージ化した、フルマネージド型の Lambda レイヤーを提供します。このレイヤーを使用すると、関数コードを変更する必要はなしで、Lambda 関数を計測できます。また、このレイヤーは、OTel でのカスタムな初期化を実行するように構成することもできます。詳細については、ADOT のドキュメントの「Custom configuration for the ADOT Collector on Lambda」(Lambda 上での ADOT Collector のカスタム設定) 参照してください。

Java ランタイムの場合、使用するレイヤーを以下の 2 つの中から選択できます。

  • AWS ADOT Java 向けのマネージド Lambda レイヤー (自動計測エージェント) – このレイヤーは、起動時に関数コードを自動的に変換し、トレーシングデータを収集できるようにします。このレイヤーを ADOT Java エージェントとともに使用する方法の詳細については、「ADOT ドキュメント」の「AWS Distro for OpenTelemetry Lambda Support for Java (Auto-instrumentation Agent)」を参照してください。

  • ADOT Java 用の AWS マネージド型 Lambda レイヤー – このレイヤーによっても、Lambda 関数の組み込み型の計測機能が提供されます。ただし、OTel SDK を初期化するために、手動によるコード変更がいくつか必要となります。このレイヤーを使用する方法の詳細については、「ADOT ドキュメント」の「AWS Distro for OpenTelemetry Lambda Support for Java」を参照してください。

Java 関数の計測のための X-Ray SDK の使用

関数が、アプリケーション内で他のリソースやサービスに対して行う呼び出しのデータを記録するには、X-Ray SDK for Java をビルド設定に追加します。以下に、AWS SDK for Java 2.x クライアントの自動計測をアクティブ化するライブラリを含む Gradle ビルド設定の例を示します。

build.gradle - 依存関係のトレース
dependencies { implementation platform('software.amazon.awssdk:bom:2.16.1') implementation platform('com.amazonaws:aws-xray-recorder-sdk-bom:2.11.0') ... implementation 'com.amazonaws:aws-xray-recorder-sdk-core' implementation 'com.amazonaws:aws-xray-recorder-sdk-aws-sdk' implementation 'com.amazonaws:aws-xray-recorder-sdk-aws-sdk-v2-instrumentor' ... }

正しい依存関係を追加し、必要なコード変更を行った後、Lambda コンソールまたはAPIを介して関数の構成でトレースをアクティブにします。

Lambda コンソールを使用してトレースを有効化する

コンソールを使用して、Lambda 関数のアクティブトレースをオンにするには、次のステップに従います。

アクティブトレースをオンにするには
  1. Lambda コンソールの [関数ページ] を開きます。

  2. 関数を選択します。

  3. [設定] を選択してから、[モニタリングおよび運用ツール] を選択します。

  4. [編集] を選択します。

  5. [X-Ray] で、[アクティブトレース] をオンに切り替えます。

  6. [保存] をクリックします。

Lambda API でのトレースのアクティブ化

AWS CLI または AWS SDK で Lambda 関数のトレースを設定するには、次の API オペレーションを使用します。

以下の例の AWS CLI コマンドは、my-function という名前の関数に対するアクティブトレースを有効にします。

aws lambda update-function-configuration --function-name my-function \ --tracing-config Mode=Active

トレースモードは、関数のバージョンを公開するときのバージョン固有の設定の一部です。公開後のバージョンのトレースモードを変更することはできません。

AWS CloudFormation によるトレースのアクティブ化

AWS CloudFormation テンプレート内で AWS::Lambda::Function リソースに対するアクティブトレースを有効化するには、TracingConfig プロパティを使用します。

function-inline.yml - トレース設定
Resources: function: Type: AWS::Lambda::Function Properties: TracingConfig: Mode: Active ...

AWS Serverless Application Model (AWS SAM) AWS::Serverless::Function リソースに、Tracing プロパティを使用します。

template.yml - トレース設定
Resources: function: Type: AWS::Serverless::Function Properties: Tracing: Active ...

X-Ray トレースの解釈

関数には、トレースデータを X-Ray にアップロードするためのアクセス許可が必要です。Lambda コンソールでトレースを有効にすると、Lambda は必要な権限を関数の [実行ロール] に追加します。それ以外の場合は、AWSXRayDaemonWriteAccess ポリシーを実行ロールに追加します。

アクティブトレースの設定後は、アプリケーションを通じて特定のリクエストの観測が行えるようになります。[X-Ray サービスグラフ] には、アプリケーションとそのすべてのコンポーネントに関する情報が表示されます。次の例は、2 つの関数を持つアプリケーションを示しています。プライマリ関数はイベントを処理し、エラーを返す場合があります。上位 2 番目の関数は、最初のロググループに表示されるエラーを処理し、AWS SDKを使用してX-Ray、Amazon Simple Storage Service (Amazon S3)、および Amazon CloudWatch Logs を呼び出します。

X-Ray の 2 つの個別のアプリケーションおよびそれぞれに関係するサービスマップを示した図

X-Ray は、アプリケーションへのすべてのリクエストをトレースするわけではありません。X-Ray は、サンプリングアルゴリズムを適用することで効率的なトレースを行うと同時に、すべてのリクエストについての代表的なサンプルを示します。サンプルレートは 1 秒あたり 1 回のリクエストで、追加リクエストの 5% です。関数の X-Ray サンプルレートを設定することはできません。

X-Ray では、トレースは 1 つ以上のサービスによって処理されるリクエストに関する情報を記録します。Lambda はトレースごとに 2 つのセグメントを記録します。これにより、サービスグラフに 2 つのノードが作成されます。次の図は、これら 2 つのノードを強調表示しています。

単一の関数を持つ X-Ray サービスマップ。

左に示された 1 つめのノードは、呼び出しリクエストを受信する Lambda サービスを表しています。2 つめのノードは、特定の Lambda 関数を表しています。次の例は、これら 2 つのセグメントを使用したトレースを示しています。いずれも my-function と名付けられていますが、1 つは AWS::Lambda の起点があり、もう 1 つは AWS::Lambda::Function の起点があります。AWS::Lambda セグメントにエラーが表示される場合は、Lambda サービスに問題があります。AWS::Lambda::Function セグメントにエラーが表示される場合、関数に問題があります。

特定の Lambda 呼び出しの各サブセグメント間に生じるレイテンシーを示した X-Ray トレースです。

この例では、AWS::Lambda::Function セグメントを展開して、それの 3 つのサブセグメントが表示されています。

注記

AWS は現在、Lambda サービスに変更を実装しています。これらの変更により、AWS アカウント のさまざまな Lambda 関数によって出力されるシステムログメッセージとトレースセグメントの構造と内容にわずかな違いが生じる場合があります。

ここで示すトレースの例は、古いスタイルの関数セグメントを示しています。古いスタイルのセグメントと新しいスタイルのセグメントの違いについては、次の段落で説明します。

これらの変更は今後数週間に実装され、中国および GovCloud リージョンを除くすべての AWS リージョンのすべての関数は、新しい形式のログメッセージとトレースセグメントを使用するように移行されます。

古いスタイルの関数セグメントには、次のサブセグメントが含まれます。

  • 初期化 - 関数のロードと初期化コードの実行に要した時間を表します。このサブセグメントは、関数の各インスタンスが処理する最初のイベントに対してのみ表示されます。

  • [呼び出し] - ハンドラーコードの実行に要した時間を表します。

  • [オーバーヘッド] - Lambda ランタイムが次のイベントを処理するための準備に要する時間を表します。

新しいスタイルの関数セグメントには Invocation サブセグメントが含まれていません。代わりに、顧客サブセグメントが関数セグメントに直接アタッチされます。古いスタイルの関数セグメントと新しいスタイルの関数セグメントの構造の詳細については、「」を参照してくださいX-Ray トレースを理解する

注記

Lambda SnapStart 関数には Restore サブセグメントも含まれます。Restore サブセグメントは、Lambda がスナップショットを復元し、ランタイム (JVM) をロードして、afterRestoreランタイムフックを実行するのにかかる時間を表しています。スナップショットを復元するプロセスには、MicroVM 外部でのアクティビティに費やす時間が含まれる場合があります。この時間は、Restore サブセグメントで報告されます。MicroVM 外部でスナップショットの復元に費やした時間については課金されません。

HTTP クライアントをインストルメント化し、SQL クエリを記録して、注釈とメタデータからカスタムサブセグメントを作成することもできます。詳細については、「AWS X-Ray デベロッパーガイド」の 「AWS X-Ray SDK for Java」を参照してください。

料金

X-Ray トレースは、毎月、AWS 無料利用枠で設定された一定限度まで無料で利用できます。X-Ray の利用がこの上限を超えた場合は、トレースによる保存と取得に対する料金が発生します。詳細については、「AWS X-Ray 料金表」を参照してください。

ランタイムの依存関係をレイヤー (X-Ray SDK) に保存する

X-Ray SDK を使用して AWS SDK クライアントを関数コードに埋め込むと、デプロイパッケージが巨大になる可能性があります。機能コードを更新するたびに実行時の依存関係がアップロードされないようにするには、X-Ray SDK を [Lambda layer] (Lambda レイヤー) にパッケージ化します。

以下の例では、AWS SDK for Java および X-Ray SDK for Java を保存する AWS::Serverless::LayerVersion リソースを示しています。

template.yml - 依存関係レイヤー
Resources: function: Type: AWS::Serverless::Function Properties: CodeUri: build/distributions/blank-java.zip Tracing: Active Layers: - !Ref libs ... libs: Type: AWS::Serverless::LayerVersion Properties: LayerName: blank-java-lib Description: Dependencies for the blank-java sample app. ContentUri: build/blank-java-lib.zip CompatibleRuntimes: - java21

この設定では、ランタイム依存関係を変更した場合にのみ、ライブラリレイヤーの更新が必要です。関数のデプロイパッケージにはユーザーのコードのみが含まれるため、アップロード時間を短縮できます。

依存関係のレイヤーを作成するには、デプロイ前にレイヤーアーカイブを生成するようにビルド設定を変更する必要があります。実際の例については、GitHub のサンプルアプリケーション、java-basic を参照してください。

サンプルアプリケーションでの X-Ray トレース (X-Ray SDK)

このガイドで参照する GitHub リポジトリには、X-Ray トレースの使用方法を示すサンプルアプリケーションが含まれています。各サンプルアプリケーションには、簡易のデプロイとクリーンアップ用のスクリプト、AWS SAM テンプレート、サポートリソースが含まれています。

Java のサンプル Lambda アプリケーション
  • [java17-examples] — Java レコードを使用して入力イベントデータオブジェクトを表現する方法を示す Java 関数。

  • java-basic - 単位テストと変数ログ記録設定を使用する、最小限の Java 関数のコレクション。

  • java-events - Amazon API Gateway、Amazon SQS、Amazon Kinesis などのさまざまなサービスからのイベントを処理する方法のスケルトンコードを含む Java 関数のコレクション。これらの関数は、最新バージョンの aws-lambda-java-events ライブラリ (3.0.0 以降) を使用します。これらの例では、依存関係としての AWS SDK が不要です。

  • s3-java - Amazon S3 からの通知イベントを処理し、Java Class Library (JCL) を使用して、アップロードされたイメージファイルからサムネイルを作成する Java 関数。

  • custom-serialization – fastJson 、Gson、Moshi、jackson-jr などの一般的なライブラリを使用してカスタムシリアル化を実装する方法例。

  • API Gateway を使用して Lambda 関数を呼び出す - 従業員情報を含む Amazon DynamoDB テーブルをスキャンする Java 関数。次に、Amazon Simple Notification Service を使用して、仕事の記念日を祝うテキストメッセージを従業員に送信します。この例では、API ゲートウェイを使用して関数を呼び出します。

すべてのサンプルアプリケーションでは、Lambda 関数に対するアクティブトレースが有効になっています。例えば、s3-java アプリケーションは、AWS SDK for Java 2.x クライアントの自動インストルメンテーション、テストのセグメント管理、カスタムのサブセグメント、Lambda レイヤーによるラインタイムの依存関係の保存を示しています。