AWS Lambda에서 Java 코드 계측하기

AWS Lambda에서 Java 코드 계측하기 (Instrumenting Java code in AWS Lambda)

Lambda는 AWS X-Ray와 통합되어 Lambda 애플리케이션을 추적·디버깅·최적화하는 데 도움을 줘요. X-Ray를 사용하면 요청이 애플리케이션의 리소스(Lambda 함수 및 기타 AWS 서비스 포함)를 거쳐 이동하는 과정을 추적할 수 있어요.

추적 데이터를 X-Ray로 보내려면 다음 두 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에서 엔드투엔드 추적을 구현할 수 있어요. 둘 중 무엇을 선택할지에 대해서는 AWS Distro for Open Telemetry와 X-Ray SDK 중 선택을 참고하세요.

출처: AWS Lambda 개발자 안내서

본문

이 문서의 섹션:

  • Powertools for AWS Lambda (Java)와 AWS SAM으로 추적
  • Powertools for AWS Lambda (Java)와 AWS CDK로 추적
  • ADOT로 Java 함수 계측
  • X-Ray SDK로 Java 함수 계측
  • Lambda 콘솔로 추적 활성화
  • Lambda API로 추적 활성화
  • CloudFormation으로 추적 활성화
  • X-Ray 트레이스 해석
  • 런타임 의존성을 레이어에 저장 (X-Ray SDK)
  • 샘플 애플리케이션의 X-Ray 추적 (X-Ray SDK)

Powertools for AWS Lambda (Java)와 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 메시지를 반환해요.

전제 조건

이 섹션의 단계를 완료하려면 다음이 필요해요.

  • Java 11 이상
  • AWS CLI 버전 2
  • AWS SAM CLI 버전 1.75 이상. 이전 버전의 AWS SAM CLI를 사용 중이면 AWS SAM CLI 업그레이드를 참고하세요.

샘플 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를 누릅니다.
  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: 2025-02-03 14:31:48+01:00
      End time: 2025-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 (2025-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
    

Powertools for AWS Lambda (Java)와 AWS CDK로 추적

아래 단계에 따라 AWS CDK를 사용해 Powertools for AWS Lambda (Java) 모듈이 통합된 샘플 Hello World Java 애플리케이션을 다운로드·빌드·배포해요. 이 애플리케이션은 기본 API 백엔드를 구현하고 Powertools로 로그·지표·트레이스를 내보내요. Amazon API Gateway 엔드포인트와 Lambda 함수로 구성돼요.

전제 조건

  • Java 11 이상
  • AWS CLI 버전 2
  • AWS CDK 버전 2
  • AWS SAM CLI 버전 1.75 이상

샘플 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 생성자와 ApiGatewayv2 생성자를 사용해 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를 실행합니다. 트레이스 출력은 이전 예시와 유사한 형태예요. (이 섹션의 예시 출력은 lambda_handler/app.hello 서브세그먼트를 보여줍니다.)
  14. 이 엔드포인트는 인터넷으로 접근 가능한 공개 API 엔드포인트예요. 테스트 후 삭제합니다.
    cdk destroy
    

ADOT로 Java 함수 계측

ADOT는 OTel SDK로 원격 측정 데이터를 수집하는 데 필요한 모든 것을 패키징한 완전 관리형 Lambda 레이어를 제공해요. 이 레이어를 사용하면 함수 코드를 전혀 수정하지 않고 Lambda 함수를 계측할 수 있어요. 레이어를 구성해 OTel의 커스텀 초기화를 수행할 수도 있어요. 자세한 내용은 ADOT 문서의 Lambda에서 ADOT Collector 커스텀 구성을 참고하세요.

Java 런타임에서는 사용할 레이어를 둘 중에서 선택할 수 있어요.

  • ADOT Java용 AWS 관리 Lambda 레이어 (Auto-instrumentation Agent) – 이 레이어는 시작 시 함수 코드를 자동 변환해 추적 데이터를 수집해요. 이 레이어를 ADOT Java 에이전트와 함께 사용하는 방법은 ADOT 문서의 Java용 AWS Distro for OpenTelemetry Lambda 지원 (Auto-instrumentation Agent)을 참고하세요.
  • ADOT Java용 AWS 관리 Lambda 레이어 – 이 레이어도 Lambda 함수용 내장 계측을 제공하지만, OTel SDK를 초기화하려면 약간의 수동 코드 변경이 필요해요. 자세한 내용은 ADOT 문서의 Java용 AWS Distro for OpenTelemetry Lambda 지원을 참고하세요.

Lambda Telemetry API 이벤트를 OpenTelemetry 스팬으로 변환하는 방법은 Lambda Telemetry API 이벤트 객체를 OpenTelemetry 스팬으로 변환을 참고하세요.

X-Ray SDK로 Java 함수 계측

함수가 애플리케이션의 다른 리소스와 서비스에 보내는 호출에 대한 데이터를 기록하려면 빌드 구성에 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 콘솔의 Functions 페이지를 엽니다.
  2. 함수를 선택합니다.
  3. Configuration을 선택한 다음 Monitoring and operations tools를 선택합니다.
  4. Additional monitoring tools 아래에서 Edit를 선택합니다.
  5. CloudWatch Application Signals and AWS X-Ray 아래에서 Lambda 서비스 트레이스에 대해 Enable을 선택합니다.
  6. Save를 선택합니다.

Lambda API로 추적 활성화

AWS CLI 또는 AWS SDK로 Lambda 함수에서 추적을 구성하려면 다음 API 작업을 사용하세요.

  • UpdateFunctionConfiguration
  • GetFunctionConfiguration
  • CreateFunction

다음 예시 AWS CLI 명령은 my-function이라는 함수의 활성 추적을 활성화해요.

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

추적 모드는 함수 버전을 게시할 때 버전별 구성의 일부예요. 게시된 버전에서는 추적 모드를 변경할 수 없어요.

CloudFormation으로 추적 활성화

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 서비스 그래프는 애플리케이션과 그 모든 구성 요소에 대한 정보를 보여줘요. 다음 예시는 두 함수가 있는 애플리케이션을 보여줘요. 기본 함수가 이벤트를 처리하고 때때로 오류를 반환해요. 상단의 두 번째 함수는 첫 번째 함수 로그 그룹에 나타나는 오류를 처리하고 AWS SDK로 X-Ray, Amazon Simple Storage Service(Amazon S3), Amazon CloudWatch Logs를 호출해요.

X-Ray는 애플리케이션에 대한 모든 요청을 추적하지 않아요. X-Ray는 추적이 효율적이면서도 모든 요청의 대표 샘플을 제공하도록 샘플링 알고리즘을 적용해요. 샘플링 속도는 초당 1건의 요청과 추가 요청의 5%예요. 함수의 X-Ray 샘플링 속도는 구성할 수 없어요.

X-Ray에서 트레이스는 하나 이상의 서비스가 처리하는 요청에 대한 정보를 기록해요. Lambda는 트레이스당 2개의 세그먼트를 기록하며, 이로 인해 서비스 그래프에 2개의 노드가 생성돼요.

왼쪽의 첫 번째 노드는 호출 요청을 받는 Lambda 서비스를 나타내고, 두 번째 노드는 특정 Lambda 함수를 나타내요. 두 세그먼트 모두 my-function으로 이름이 지정되지만 하나는 AWS::Lambda 오리진, 다른 하나는 AWS::Lambda::Function 오리진을 가져요. AWS::Lambda 세그먼트에 오류가 표시되면 Lambda 서비스에 문제가 있는 것이고, AWS::Lambda::Function 세그먼트에 오류가 표시되면 함수에 문제가 있는 거예요.

참고

AWS는 현재 Lambda 서비스에 변경을 적용하고 있어요. 이로 인해 계정의 서로 다른 Lambda 함수가 내보내는 시스템 로그 메시지와 트레이스 세그먼트의 구조·내용에 약간의 차이가 보일 수 있어요. 여기에 표시된 예시 트레이스는 구식 스타일 함수 세그먼트를 보여줘요. 구식·신식 세그먼트의 차이는 다음 문단에 설명돼 있어요. 이 변경은 향후 몇 주 안에 적용되며, 중국 및 GovCloud 리전을 제외한 모든 AWS 리전의 모든 함수가 새 형식의 로그 메시지와 트레이스 세그먼트로 전환돼요.

구식 스타일 함수 세그먼트에는 다음 서브세그먼트가 포함돼요.

  • Initialization – 함수 로드와 초기화 코드 실행에 소요된 시간을 나타냅니다. 이 서브세그먼트는 함수 인스턴스가 처리하는 첫 번째 이벤트에만 나타납니다.
  • Invocation – 핸들러 코드 실행에 소요된 시간을 나타냅니다.
  • Overhead – Lambda 런타임이 다음 이벤트 처리 준비에 소요한 시간을 나타냅니다.

신식 스타일 함수 세그먼트에는 Invocation 서브세그먼트가 없어요. 대신 커스텀 서브세그먼트가 함수 세그먼트에 직접 연결돼요. 구식·신식 함수 세그먼트 구조에 대한 자세한 내용은 X-Ray 트레이스 이해를 참고하세요.

참고

SnapStart 함수에는 Restore 서브세그먼트도 포함돼요. Restore 서브세그먼트는 Lambda가 스냅샷을 복원하고, 런타임을 로드하고, after-restore 런타임 훅을 실행하는 데 걸리는 시간을 보여줘요. 스냅샷 복원 과정에는 MicroVM 외부 활동에 소요되는 시간이 포함될 수 있어요. 이 시간은 Restore 서브세그먼트에 보고돼요. microVM 외부에서 스냅샷을 복원하는 데 소요된 시간은 청구되지 않아요.

HTTP 클라이언트를 계측하고, SQL 쿼리를 기록하고, 어노테이션과 메타데이터로 커스텀 서브세그먼트를 만들 수도 있어요. 자세한 내용은 AWS X-Ray 개발자 안내서의 AWS X-Ray SDK for Java를 참고하세요.

요금

AWS Free Tier의 일부로 매달 일정 한도까지 X-Ray 추적을 무료로 사용할 수 있어요. 그 임계값을 초과하면 X-Ray가 트레이스 저장·검색에 대해 요금을 부과해요. 자세한 내용은 AWS X-Ray 요금을 참고하세요.

런타임 의존성을 레이어에 저장 (X-Ray SDK)

X-Ray SDK로 함수 코드의 AWS SDK 클라이언트를 계측하면 배포 패키지가 상당히 커질 수 있어요. 함수 코드를 업데이트할 때마다 런타임 의존성을 업로드하지 않으려면 X-Ray SDK를 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:
        - java25

이 구성을 사용하면 런타임 의존성을 변경할 때만 라이브러리 레이어를 업데이트해요. 함수 배포 패키지에는 코드만 포함되므로 업로드 시간을 줄일 수 있어요.

의존성용 레이어를 만들려면 배포 전에 레이어 아카이브를 생성하도록 빌드 구성을 변경해야 해요. 실제 예시는 GitHub의 java-basic 샘플 애플리케이션을 참고하세요.

샘플 애플리케이션의 X-Ray 추적 (X-Ray SDK)

이 안내서의 GitHub 저장소에는 X-Ray 추적 사용을 보여주는 샘플 애플리케이션이 포함돼 있어요. 각 샘플 애플리케이션에는 쉬운 배포·정리용 스크립트, AWS SAM 템플릿, 지원 리소스가 포함돼 있어요.

Java 샘플 Lambda 애플리케이션

  • example-java – Lambda로 주문을 처리하는 방법을 보여주는 Java 함수. 커스텀 입력 이벤트 객체의 정의·역직렬화, AWS SDK 사용, 로깅 출력을 설명해요.
  • 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 함수.
  • layer-java – Lambda 레이어로 핵심 함수 코드와 분리해 의존성을 패키징하는 방법을 설명하는 Java 함수.

모든 샘플 애플리케이션은 Lambda 함수에 활성 추적을 활성화해요. 예를 들어 s3-java 애플리케이션은 AWS SDK for Java 2.x 클라이언트의 자동 계측, 테스트용 세그먼트 관리, 커스텀 서브세그먼트, 런타임 의존성 저장용 Lambda 레이어 사용을 보여줘요.

더 알아보기 (Learn more)