

# AWS X-Ray sample application
<a name="xray-scorekeep"></a>

**Note**  
X-Ray SDK/Daemon Maintenance Notice – On February 25th, 2026, the AWS X-Ray SDKs/Daemon will enter maintenance mode, where AWS will limit X-Ray SDK and Daemon releases to address security issues only. For more information on the support timeline, see [X-Ray SDK and Daemon Support timeline](xray-sdk-daemon-timeline.md). We recommend to migrate to OpenTelemetry. For more information on migrating to OpenTelemetry, see [Migrating from X-Ray instrumentation to OpenTelemetry instrumentation ](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-migration.html).

The AWS X-Ray [eb-java-scorekeep](https://github.com/awslabs/eb-java-scorekeep/tree/xray) sample app, available on GitHub, shows the use of the AWS X-Ray SDK to instrument incoming HTTP calls, DynamoDB SDK clients, and HTTP clients. The sample app uses CloudFormation to create DynamoDB tables, compile Java code on instance, and run the X-Ray daemon without any additional configuration.

See the [Scorekeep tutorial](scorekeep-tutorial.md) to start installing and using an instrumented sample application, using the AWS Management Console or the AWS CLI.

![\[Scorekeep uses the AWS X-Ray SDK to instrument incoming HTTP calls, DynamoDB SDK clients, and HTTP clients\]](http://docs.aws.amazon.com/xray/latest/devguide/images/scorekeep-flow.png)


The sample includes a front-end web app, the API that it calls, and the DynamoDB tables that it uses to store data. Basic instrumentation with [filters](xray-sdk-java-filters.md), [plugins](xray-sdk-java-configuration.md), and [instrumented AWS SDK clients](xray-sdk-java-awssdkclients.md) is shown in the project's `xray-gettingstarted` branch. This is the branch that you deploy in the [getting started tutorial](scorekeep-tutorial.md). Because this branch only includes the basics, you can diff it against the `master` branch to quickly understand the basics.

![\[Service map showing client interaction with Scorekeep container and related AWS services.\]](http://docs.aws.amazon.com/xray/latest/devguide/images/scorekeep-gettingstarted-servicemap-before-ECS.png)


The sample application shows basic instrumentation in these files:
+ **HTTP request filter** – [https://github.com/awslabs/eb-java-scorekeep/tree/xray/src/main/java/scorekeep/WebConfig.java](https://github.com/awslabs/eb-java-scorekeep/tree/xray/src/main/java/scorekeep/WebConfig.java)
+ **AWS SDK client instrumentation** – [https://github.com/awslabs/eb-java-scorekeep/tree/xray/build.gradle](https://github.com/awslabs/eb-java-scorekeep/tree/xray/build.gradle)

The `xray` branch of the application includes the use of [HTTPClient](xray-sdk-java-httpclients.md), [Annotations](xray-sdk-java-segment.md), [SQL queries](xray-sdk-java-sqlclients.md), [custom subsegments](xray-sdk-java-subsegments.md), an instrumented [AWS Lambda](xray-services-lambda.md) function, and [instrumented initialization code and scripts](scorekeep-startup.md).

To support user log-in and AWS SDK for JavaScript use in the browser, the `xray-cognito` branch adds Amazon Cognito to support user authentication and authorization. With credentials retrieved from Amazon Cognito, the web app also sends trace data to X-Ray to record request information from the client's point of view. The browser client appears as its own node on the trace map, and records additional information, including the URL of the page that the user is viewing, and the user's ID.

Finally, the `xray-worker` branch adds an instrumented Python Lambda function that runs independently, processing items from an Amazon SQS queue. Scorekeep adds an item to the queue each time a game ends. The Lambda worker, triggered by CloudWatch Events, pulls items from the queue every few minutes and processes them to store game records in Amazon S3 for analysis.

**Topics**
+ [Getting started with the Scorekeep sample application](scorekeep-tutorial.md)
+ [Manually instrumenting AWS SDK clients](scorekeep-sdkclients.md)
+ [Creating additional subsegments](scorekeep-subsegments.md)
+ [Recording annotations, metadata, and user IDs](scorekeep-annotations.md)
+ [Instrumenting outgoing HTTP calls](scorekeep-httpclient.md)
+ [Instrumenting calls to a PostgreSQL database](scorekeep-postgresql.md)
+ [Instrumenting AWS Lambda functions](scorekeep-lambda.md)
+ [Instrumenting startup code](scorekeep-startup.md)
+ [Instrumenting scripts](scorekeep-scripts.md)
+ [Instrumenting a web app client](scorekeep-client.md)
+ [Using instrumented clients in worker threads](scorekeep-workerthreads.md)

# Getting started with the Scorekeep sample application
<a name="scorekeep-tutorial"></a>

**Note**  
X-Ray SDK/Daemon Maintenance Notice – On February 25th, 2026, the AWS X-Ray SDKs/Daemon will enter maintenance mode, where AWS will limit X-Ray SDK and Daemon releases to address security issues only. For more information on the support timeline, see [X-Ray SDK and Daemon Support timeline](xray-sdk-daemon-timeline.md). We recommend to migrate to OpenTelemetry. For more information on migrating to OpenTelemetry, see [Migrating from X-Ray instrumentation to OpenTelemetry instrumentation ](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-migration.html).

This tutorial uses the `xray-gettingstarted` branch of the [Scorekeep sample application](xray-scorekeep.md), which uses CloudFormation to create and configure the resources that run the sample application and X-Ray daemon on Amazon ECS. The application uses the Spring framework to implement a JSON web API and the AWS SDK for Java to persist data to Amazon DynamoDB. A servlet filter in the application instruments all incoming requests served by the application, and a request handler on the AWS SDK client instruments downstream calls to DynamoDB.

You can follow this tutorial using either the AWS Management Console or the AWS CLI.

**Topics**
+ [Prerequisites](#xray-gettingstarted-prereqs)
+ [Install the Scorekeep application using CloudFormation](#xray-gettingstarted-deploy)
+ [Generate trace data](#xray-gettingstarted-generate-traces)
+ [View the trace map in the AWS Management Console](#xray-gettingstarted-console)
+ [Configuring Amazon SNS notifications](#xray-gettingstarted-notifications)
+ [Explore the sample application](#xray-gettingstarted-sample)
+ [Optional: Least privilege policy](#xray-gettingstarted-security)
+ [Clean up](#xray-gettingstarted-cleanup)
+ [Next steps](#xray-gettingstarted-nextsteps)

## Prerequisites
<a name="xray-gettingstarted-prereqs"></a>

This tutorial uses CloudFormation to create and configure the resources that run the sample application and X-Ray daemon. The following prerequisites are required to install and run through the tutorial: 

1. If you use an IAM user with limited permissions, add the following user policies in the [IAM console](https://console.aws.amazon.com/iam): 
   + `AWSCloudFormationFullAccess` – to access and use CloudFormation
   + `AmazonS3FullAccess` – to upload a template file to CloudFormation using the AWS Management Console
   + `IAMFullAccess` – to create the Amazon ECS and Amazon EC2 instance roles
   + `AmazonEC2FullAccess` – to create the Amazon EC2 resources
   + `AmazonDynamoDBFullAccess` – to create the DynamoDB tables
   + `AmazonECS_FullAccess` – to create Amazon ECS resources
   + `AmazonSNSFullAccess` – to create the Amazon SNS topic
   + `AWSXrayReadOnlyAccess` – for permission to view the trace map and traces in the X-Ray console

1. To run through the tutorial using the AWS CLI, [install the CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) version 2.7.9 or later, and [configure the CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html#cli-configure-quickstart-config) with the user from the previous step. Make sure the region is configured when configuring the AWS CLI with the user. If a region is not configured, you will need to append `--region AWS-REGION` to every CLI command. 

1. Ensure that [Git](https://github.com/git-guides/install-git) is installed, in order to clone the sample application repo. 

1. Use the following code example to clone the `xray-gettingstarted` branch of the Scorekeep repository: 

   ```
   git clone https://github.com/aws-samples/eb-java-scorekeep.git xray-scorekeep -b xray-gettingstarted
   ```

## Install the Scorekeep application using CloudFormation
<a name="xray-gettingstarted-deploy"></a>

------
#### [ AWS Management Console ]

**Install the sample application using the AWS Management Console**

1. Open the [CloudFormation console](https://console.aws.amazon.com/cloudformation/)

1. Choose **Create stack** and then choose **With new resources** from the drop-down menu.

1. In the **Specify template** section, choose **Upload a template file**.

1. Select **Choose file**, navigate to the `xray-scorekeep/cloudformation` folder that was created when you cloned the git repo, and choose the `cf-resources.yaml` file.

1. Choose **Next** to continue.

1. Enter `scorekeep` into the **Stack name** textbox, and then choose **Next** at the bottom of the page to continue. Note that the rest of this tutorial assumes the stack is named `scorekeep`.

1. Scroll to the bottom of the **Configure stack options** page and choose **Next** to continue.

1. Scroll to the bottom of the **Review** page, choose the check-box acknowledging that CloudFormation may create IAM resources with custom names, and choose **Create stack**.

1. The CloudFormation stack is now being created. The stack status will be `CREATE_IN_PROGRESS` for about five minutes before changing to `CREATE_COMPLETE`. The status will refresh periodically, or you can refresh the page.

------
#### [ AWS CLI ]

**Install the sample application using the AWS CLI**

1. Navigate to the `cloudformation` folder of the `xray-scorekeep` repository that you cloned earlier in the tutorial:

   ```
   cd xray-scorekeep/cloudformation/
   ```

1. Enter the following AWS CLI command to create the CloudFormation stack:

   ```
   aws cloudformation create-stack --stack-name scorekeep --capabilities "CAPABILITY_NAMED_IAM" --template-body file://cf-resources.yaml
   ```

1. Wait until the CloudFormation stack status is `CREATE_COMPLETE`, which will take about five minutes. Use the following AWS CLI command to check on the status:

   ```
   aws cloudformation describe-stacks --stack-name scorekeep --query "Stacks[0].StackStatus"
   ```

------

## Generate trace data
<a name="xray-gettingstarted-generate-traces"></a>

The sample application includes a front-end web app. Use the web app to generate traffic to the API and send trace data to X-Ray. First, retrieve the web app URL using the AWS Management Console or the AWS CLI:

------
#### [ AWS Management Console ]

**Find the application URL using the AWS Management Console**

1. Open the [CloudFormation console](https://console.aws.amazon.com/cloudformation/)

1. Choose the `scorekeep` stack from the list.

1. Choose the **Outputs** tab on the `scorekeep` stack page, and choose the `LoadBalancerUrl` URL link to open the web application.

------
#### [ AWS CLI ]

**Find the application URL using the AWS CLI**

1. Use the following command to display the URL of the web application:

   ```
   aws cloudformation describe-stacks --stack-name scorekeep --query "Stacks[0].Outputs[0].OutputValue"
   ```

1. Copy this URL and open in a browser to display the Scorekeep web application.

------

**Use the web application to generate trace data**

1. Choose **Create** to create a user and session.

1. Type a **game name**, set the **Rules** to **Tic Tac Toe**, and then choose **Create** to create a game.

1. Choose **Play** to start the game.

1. Choose a tile to make a move and change the game state.

Each of these steps generates HTTP requests to the API, and downstream calls to DynamoDB to read and write user, session, game, move, and state data.

## View the trace map in the AWS Management Console
<a name="xray-gettingstarted-console"></a>

You can see the trace map and traces generated by the sample application in the X-Ray and CloudWatch consoles.

------
#### [ X-Ray console ]

**Use the X-Ray console**

1. Open the trace map page of the [X-Ray console](https://console.aws.amazon.com/xray/home#/service-map).

1. The console shows a representation of the service graph that X-Ray generates from the trace data sent by the application. Be sure to adjust the time period of the trace map if needed, to make sure that it will display all traces since you first started the web application.  
![\[X-Ray trace map time period\]](http://docs.aws.amazon.com/xray/latest/devguide/images/xray-console-time-period-15-minutes.png)

The trace map shows the web app client, the API running in Amazon ECS, and each DynamoDB table that the application uses. Every request to the application, up to a configurable maximum number of requests per second, is traced as it hits the API, generates requests to downstream services, and completes.

You can choose any node in the service graph to view traces for requests that generated traffic to that node. Currently, the Amazon SNS node is yellow. Drill down to find out why.

![\[X-Ray console trace map page\]](http://docs.aws.amazon.com/xray/latest/devguide/images/scorekeep-gettingstarted-servicemap-before-ECS.png)


**To find the cause of the error**

1. Choose the node named **SNS**. The node details panel is displayed.

1. Choose **View traces** to access the **Trace overview** screen.

1. Choose the trace from the **Trace list**. This trace doesn't have a method or URL because it was recorded during startup instead of in response to an incoming request.  
![\[Choosing a trace from the trace list\]](http://docs.aws.amazon.com/xray/latest/devguide/images/scorekeep-gettingstarted-tracelist-sns.png)

1. Choose the error status icon within the Amazon SNS segment at the bottom of the page, to open the **Exceptions** page for the SNS subsegment.  
![\[Choose the error status icon to open the Exceptions page for the Amazon SNS subsegment\]](http://docs.aws.amazon.com/xray/latest/devguide/images/scorekeep-gettingstarted-timeline-sns-ecs.png)

1. The X-Ray SDK automatically captures exceptions thrown by instrumented AWS SDK clients and records the stack trace.  
![\[Exceptions tab showing captured exceptions and recorded stack trace\]](http://docs.aws.amazon.com/xray/latest/devguide/images/scorekeep-gettingstarted-exception.png)

------
#### [ CloudWatch console ]

**Use the CloudWatch console**

1. Open the [X-Ray trace map](https://console.aws.amazon.com/cloudwatch/home#xray:service-map/map) page of the CloudWatch console.

1. The console shows a representation of the service graph that X-Ray generates from the trace data sent by the application. Be sure to adjust the time period of the trace map if needed, to make sure that it will display all traces since you first started the web application.  
![\[CloudWatch trace map time period\]](http://docs.aws.amazon.com/xray/latest/devguide/images/cw-console-service-map-time-period-15-minutes.png)

The trace map shows the web app client, the API running in Amazon EC2, and each DynamoDB table that the application uses. Every request to the application, up to a configurable maximum number of requests per second, is traced as it hits the API, generates requests to downstream services, and completes.

You can choose any node in the service graph to view traces for requests that generated traffic to that node. Currently, the Amazon SNS node is orange. Drill down to find out why.

![\[X-Ray console trace map page\]](http://docs.aws.amazon.com/xray/latest/devguide/images/scorekeep-gettingstarted-cw-servicemap-before-ECS.png)


**To find the cause of the error**

1. Choose the node named **SNS**. The SNS node details panel is displayed below the map.

1. Choose **View traces** to access the **Traces** page.

1. Add the bottom of the page, choose the trace from the **Traces** list. This trace doesn't have a method or URL because it was recorded during startup instead of in response to an incoming request.  
![\[Choosing a trace from the trace list\]](http://docs.aws.amazon.com/xray/latest/devguide/images/scorekeep-gettingstarted-cw-tracelist-sns-ecs.png)

1. Choose the Amazon SNS subsegment at the bottom of the segments timeline, and choose the **Exceptions** tab for the SNS subsegment to view the exception details.  
![\[View the Exceptions tab for the Amazon SNS subsegment\]](http://docs.aws.amazon.com/xray/latest/devguide/images/scorekeep-gettingstarted-cw-timeline-sns-ecs.png)

------

The cause indicates that the email address provided in a call to `createSubscription` made in the `WebConfig` class was invalid. In the next section, we'll fix that.

## Configuring Amazon SNS notifications
<a name="xray-gettingstarted-notifications"></a>

Scorekeep uses Amazon SNS to send notifications when users complete a game. When the application starts up, it tries to create a subscription for an email address defined in a CloudFormation stack parameter. That call is currently failing. Configure a notification email to enable notifications, and resolve the failures highlighted in the trace map.

------
#### [ AWS Management Console ]

**To configure Amazon SNS notifications using the AWS Management Console**

1. Open the [CloudFormation console](https://console.aws.amazon.com/cloudformation/)

1. Choose the radio button next to the `scorekeep` stack name in the list, and then choose **Update**.

1. Make sure that **Use current template** is chosen, and then click **Next** on the **Update stack** page.

1. Find the **Email** parameter in the list, and replace the default value with a valid email address.  
![\[Update email configuration\]](http://docs.aws.amazon.com/xray/latest/devguide/images/scorekeep-cf-email-update.png)

1. Scroll to the bottom of the page and choose **Next**.

1. Scroll to the bottom of the **Review** page, choose the check-box acknowledging that CloudFormation may create IAM resources with custom names, and choose **Update stack**.

1. The CloudFormation stack is now being updated. The stack status will be `UPDATE_IN_PROGRESS` for about five minutes before changing to `UPDATE_COMPLETE`. The status will refresh periodically, or you can refresh the page.

------
#### [ AWS CLI ]

**To configure Amazon SNS notifications using the AWS CLI**

1. Navigate to the `xray-scorekeep/cloudformation/` folder you previously created, and open the `cf-resources.yaml` file in a text editor.

1. Find the `Default` value within the **Email** parameter and change it from *UPDATE\$1ME* to a valid email address.

   ```
   Parameters:
     Email:
       Type: String
       Default: UPDATE_ME # <- change to a valid abc@def.xyz email address
   ```

1. From the `cloudformation` folder, update the CloudFormation stack with the following AWS CLI command: 

   ```
   aws cloudformation update-stack --stack-name scorekeep --capabilities "CAPABILITY_NAMED_IAM" --template-body file://cf-resources.yaml
   ```

1. Wait until the CloudFormation stack status is `UPDATE_COMPLETE`, which will take a few minutes. Use the following AWS CLI command to check on the status:

   ```
   aws cloudformation describe-stacks --stack-name scorekeep --query "Stacks[0].StackStatus"
   ```

------

When the update completes, Scorekeep restarts and creates a subscription to the SNS topic. Check your email and confirm the subscription to see updates when you complete a game. Open the trace map to verify that the calls to SNS are no longer failing.

## Explore the sample application
<a name="xray-gettingstarted-sample"></a>

The sample application is an HTTP web API in Java that is configured to use the X-Ray SDK for Java. When you deploy the application with the CloudFormation template, it creates the DynamoDB tables, Amazon ECS Cluster, and other services required to run Scorekeep on ECS. A task definition file for ECS is created through CloudFormation. This file defines the container images used per task in an ECS cluster. These images are obtained from the official X-Ray public ECR. The scorekeep API container image has the API compiled with Gradle. The container image of the Scorekeep frontend container serves the frontend using the nginx proxy server. This server routes requests to paths starting with /api to the API.

To instrument incoming HTTP requests, the application adds the `TracingFilter` provided by the SDK.

**Example src/main/java/scorekeep/WebConfig.java - servlet filter**  

```
import javax.servlet.Filter;
import [com.amazonaws.xray.javax.servlet.AWSXRayServletFilter](https://docs.aws.amazon.com/xray-sdk-for-java/latest/javadoc/com/amazonaws/xray/javax/servlet/AWSXRayServletFilter.html);
...

@Configuration
public class WebConfig {

  @Bean
  public Filter TracingFilter() {
    return new AWSXRayServletFilter("Scorekeep");
  }
...
```

This filter sends trace data about all incoming requests that the application serves, including request URL, method, response status, start time, and end time.

The application also makes downstream calls to DynamoDB using the AWS SDK for Java. To instrument these calls, the application simply takes the AWS SDK-related submodules as dependencies, and the X-Ray SDK for Java automatically instruments all AWS SDK clients.

The application uses `Docker` to build the source code on-instance with the `Gradle Docker Image` and the `Scorekeep API Dockerfile` file to run the executable JAR that Gradle generates at its `ENTRYPOINT`. 

**Example use of Docker to build via Gradle Docker Image**  

```
docker run --rm -v /PATH/TO/SCOREKEEP_REPO/home/gradle/project -w /home/gradle/project gradle:4.3 gradle build
```

**Example Dockerfile ENTRYPOINT**  

```
ENTRYPOINT [ "sh", "-c", "java -Dserver.port=5000 -jar scorekeep-api-1.0.0.jar" ]
```

The `build.gradle` file downloads the SDK submodules from Maven during compilation by declaring them as dependencies.

**Example build.gradle -- dependencies**  

```
...
dependencies {
    compile("org.springframework.boot:spring-boot-starter-web")
    testCompile('org.springframework.boot:spring-boot-starter-test')
    compile('com.amazonaws:aws-java-sdk-dynamodb')
    compile("com.amazonaws:aws-xray-recorder-sdk-core")
    compile("com.amazonaws:aws-xray-recorder-sdk-aws-sdk")
    compile("com.amazonaws:aws-xray-recorder-sdk-aws-sdk-instrumentor")
    ...
}
dependencyManagement {
    imports {
        mavenBom("com.amazonaws:aws-java-sdk-bom:1.11.67")
        mavenBom("com.amazonaws:aws-xray-recorder-sdk-bom:2.11.0")
    }
}
```

The core, AWS SDK, and AWS SDK Instrumentor submodules are all that's required to automatically instrument any downstream calls made with the AWS SDK.

To relay the raw segment data to the X-Ray API, the X-Ray daemon is required to listen for traffic on UDP port 2000. To do so, the application has the X-Ray daemon run in a container that is deployed alongside the Scorekeep application on ECS as a *sidecar container*. Check out the [X-Ray daemon](xray-daemon.md) topic for more information.

**Example X-Ray Daemon Container Definition in an ECS Task Definition**  

```
...
Resources:
  ScorekeepTaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties: 
      ContainerDefinitions: 
      ...
      
      - Cpu: '256'
        Essential: true
        Image: amazon/aws-xray-daemon
        MemoryReservation: '128'
        Name: xray-daemon
        PortMappings: 
          - ContainerPort: '2000'
            HostPort: '2000'
            Protocol: udp
      ...
```

The X-Ray SDK for Java provides a class named `AWSXRay` that provides the global recorder, a `TracingHandler` that you can use to instrument your code. You can configure the global recorder to customize the `AWSXRayServletFilter` that creates segments for incoming HTTP calls. The sample includes a static block in the `WebConfig` class that configures the global recorder with plugins and sampling rules.

**Example src/main/java/scorekeep/WebConfig.java - recorder**  

```
import com.amazonaws.xray.AWSXRay;
import com.amazonaws.xray.AWSXRayRecorderBuilder;
import com.amazonaws.xray.javax.servlet.AWSXRayServletFilter;
import com.amazonaws.xray.plugins.ECSPlugin;
import com.amazonaws.xray.plugins.EC2Plugin;
import com.amazonaws.xray.strategy.sampling.LocalizedSamplingStrategy;
...

@Configuration
public class WebConfig {
  ...
  
  static {
    AWSXRayRecorderBuilder builder = AWSXRayRecorderBuilder.standard().withPlugin(new ECSPlugin()).withPlugin(new EC2Plugin());

    URL ruleFile = WebConfig.class.getResource("/sampling-rules.json");
    builder.withSamplingStrategy(new LocalizedSamplingStrategy(ruleFile));

    AWSXRay.setGlobalRecorder(builder.build());
    ...
    
  }
}
```

This example uses the builder to load sampling rules from a file named `sampling-rules.json`. Sampling rules determine the rate at which the SDK records segments for incoming requests. 

**Example src/main/java/resources/sampling-rules.json**  

```
{
  "version": 1,
  "rules": [
    {
      "description": "Resource creation.",
      "service_name": "*",
      "http_method": "POST",
      "url_path": "/api/*",
      "fixed_target": 1,
      "rate": 1.0
    },
    {
      "description": "Session polling.",
      "service_name": "*",
      "http_method": "GET",
      "url_path": "/api/session/*",
      "fixed_target": 0,
      "rate": 0.05
    },
    {
      "description": "Game polling.",
      "service_name": "*",
      "http_method": "GET",
      "url_path": "/api/game/*/*",
      "fixed_target": 0,
      "rate": 0.05
    },
    {
      "description": "State polling.",
      "service_name": "*",
      "http_method": "GET",
      "url_path": "/api/state/*/*/*",
      "fixed_target": 0,
      "rate": 0.05
    }
  ],
  "default": {
    "fixed_target": 1,
    "rate": 0.1
  }
}
```

The sampling rules file defines four custom sampling rules and the default rule. For each incoming request, the SDK evaluates the custom rules in the order in which they are defined. The SDK applies the first rule that matches the request's method, path, and service name. For Scorekeep, the first rule catches all POST requests (resource creation calls) by applying a fixed target of one request per second and a rate of 1.0, or 100 percent of requests after the fixed target is satisfied.

The other three custom rules apply a five percent rate with no fixed target to session, game, and state reads (GET requests). This minimizes the number of traces for periodic calls that the front end makes automatically every few seconds to ensure the content is up to date. For all other requests, the file defines a default rate of one request per second and a rate of 10 percent.

The sample application also shows how to use advanced features such as manual SDK client instrumentation, creating additional subsegments, and outgoing HTTP calls. For more information, see [AWS X-Ray sample application](xray-scorekeep.md).

## Optional: Least privilege policy
<a name="xray-gettingstarted-security"></a>

 The Scorekeep ECS containers access resources using full access policies, such as `AmazonSNSFullAccess` and `AmazonDynamoDBFullAccess`. Using full access policies is not the best practice for production applications. The following example updates the DynamoDB IAM policy to improve the security of the application. To learn more about security best practices in IAM policies, see [Identity and access management for AWS X-Ray](security-iam.md).

**Example cf-resources.yaml template ECSTaskRole definition**  

```
ECSTaskRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"		 	 	 
        Statement: 
          - 
            Effect: "Allow"
            Principal: 
              Service: 
                - "ecs-tasks.amazonaws.com"
            Action: 
              - "sts:AssumeRole"
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess"
        - "arn:aws:iam::aws:policy/AmazonSNSFullAccess"
        - "arn:aws:iam::aws:policy/AWSXrayFullAccess"
      RoleName: "scorekeepRole"
```

To update your policy, first you identify the ARN of your DynamoDB resources. Then you use the ARN in a custom IAM policy. Finally, you apply that policy to your instance profile.

**To identify the ARN of your DynamoDB resource:**

1. Open the [DynamoDB console](https://console.aws.amazon.com/dynamodbv2).

1. Choose **Tables** from the left navigation bar.

1. Choose any of the `scorekeep-*` to display the table detail page.

1. Under the **Overview** tab, choose **Additional info** to expand the section and view the Amazon Resource Name (ARN). Copy this value.

1. Insert the ARN into the following IAM policy, replacing the `AWS_REGION` and `AWS_ACCOUNT_ID` values with your specific region and account ID. This new policy allows only the actions specified, instead of the `AmazonDynamoDBFullAccess` policy which allows any action.  
**Example**  

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Sid": "ScorekeepDynamoDB",
               "Effect": "Allow",
               "Action": [
                   "dynamodb:PutItem",
                   "dynamodb:UpdateItem",
                   "dynamodb:DeleteItem",
                   "dynamodb:GetItem",
                   "dynamodb:Scan",
                   "dynamodb:Query"
               ],
               "Resource": "arn:aws:dynamodb:us-east-1:111122223333:table/scorekeep-*"
           }
       ]
   }
   ```

------

   The tables that the application creates follow a consistent naming convention. You can use the `scorekeep-*` format to indicate all Scorekeep tables.

**Change your IAM policy**

1. Open the [Scorekeep task role (scorekeepRole)](https://console.aws.amazon.com/iamv2/home#/roles/details/scorekeepRole) from the IAM console.

1. Choose the check box next to the `AmazonDynamoDBFullAccess` policy and choose **Remove** to remove this policy. 

1. Choose **Add permissions**, and then **Attach policies**, and finally **Create policy**.

1. Choose the **JSON** tab and paste in the policy created above.

1. Choose **Next: Tags** at the bottom of the page.

1. Choose **Next: Review** at the bottom of the page.

1. For **Name**, assign a name for the policy.

1. Choose **Create policy** at the bottom of the page. 

1. Attach the newly created policy to the `scorekeepRole` role. It may take a few minutes for the attached policy to take effect.

If you have attached the new policy to the `scorekeepRole` role, you must detach it before deleting the CloudFormation stack, since this attached policy will block the stack from being deleted. The policy can be automatically detached by deleting the policy.

**Remove your custom IAM policy**

1. Open the [IAM console](https://console.aws.amazon.com/iam).

1. Choose **Policies** from the left navigation bar.

1. Search for the custom policy name you created earlier in this section, and choose the radio button next to the policy name to highlight it.

1. Choose the **Actions** drop-down and then choose **Delete**.

1. Type the name of the custom policy and then choose **Delete** to confirm deletion . This will automatically detach the policy from the `scorekeepRole` role.

## Clean up
<a name="xray-gettingstarted-cleanup"></a>

Follow these steps to delete the Scorekeep application resources:

**Note**  
If you created and attached custom policies using the prior section of this tutorial, you must remove the policy from the `scorekeepRole` before deleting the CloudFormation stack.

------
#### [ AWS Management Console ]

**Delete the sample application using the AWS Management Console**

1. Open the [CloudFormation console](https://console.aws.amazon.com/cloudformation/)

1. Choose the radio button next to the `scorekeep` stack name in the list, and then choose **Delete**.

1. The CloudFormation stack is now being deleted. The stack status will be `DELETE_IN_PROGRESS` for a few minutes until all resources are deleted. The status will refresh periodically, or you can refresh the page.

------
#### [ AWS CLI ]

**Delete the sample application using the AWS CLI**

1. Enter the following AWS CLI command to delete the CloudFormation stack:

   ```
   aws cloudformation delete-stack --stack-name scorekeep
   ```

1. Wait until the CloudFormation stack no longer exists, which will take about five minutes. Use the following AWS CLI command to check on the status:

   ```
   aws cloudformation describe-stacks --stack-name scorekeep --query "Stacks[0].StackStatus"
   ```

------

## Next steps
<a name="xray-gettingstarted-nextsteps"></a>

Learn more about X-Ray in the next chapter, [AWS X-Ray concepts](xray-concepts.md).

To instrument your own app, learn more about the X-Ray SDK for Java or one of the other X-Ray SDKs:
+ **X-Ray SDK for Java** – [AWS X-Ray SDK for Java](xray-sdk-java.md)
+ **X-Ray SDK for Node.js** – [AWS X-Ray SDK for Node.js](xray-sdk-nodejs.md)
+ **X-Ray SDK for .NET** – [AWS X-Ray SDK for .NET](xray-sdk-dotnet.md)

To run the X-Ray daemon locally or on AWS, see [AWS X-Ray daemon](xray-daemon.md).

To contribute to the sample application on GitHub, see [eb-java-scorekeep](https://github.com/awslabs/eb-java-scorekeep/tree/xray-gettingstarted).

# Manually instrumenting AWS SDK clients
<a name="scorekeep-sdkclients"></a>

**Note**  
X-Ray SDK/Daemon Maintenance Notice – On February 25th, 2026, the AWS X-Ray SDKs/Daemon will enter maintenance mode, where AWS will limit X-Ray SDK and Daemon releases to address security issues only. For more information on the support timeline, see [X-Ray SDK and Daemon Support timeline](xray-sdk-daemon-timeline.md). We recommend to migrate to OpenTelemetry. For more information on migrating to OpenTelemetry, see [Migrating from X-Ray instrumentation to OpenTelemetry instrumentation ](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-migration.html).

The X-Ray SDK for Java automatically instruments all AWS SDK clients when you [include the AWS SDK Instrumentor submodule in your build dependencies](xray-sdk-java.md#xray-sdk-java-dependencies).

You can disable automatic client instrumentation by removing the Instrumentor submodule. This enables you to instrument some clients manually while ignoring others, or use different tracing handlers on different clients.

To illustrate support for instrumenting specific AWS SDK clients, the application passes a tracing handler to `AmazonDynamoDBClientBuilder` as a request handler in the user, game, and session model. This code change tells the SDK to instrument all calls to DynamoDB using those clients.

**Example [https://github.com/awslabs/eb-java-scorekeep/tree/xray/src/main/java/scorekeep/SessionModel.java](https://github.com/awslabs/eb-java-scorekeep/tree/xray/src/main/java/scorekeep/SessionModel.java) – Manual AWS SDK client instrumentation**  

```
import [com.amazonaws.xray.AWSXRay](https://docs.aws.amazon.com/xray-sdk-for-java/latest/javadoc/com/amazonaws/xray/AWSXRay.html);
import [com.amazonaws.xray.handlers.TracingHandler](https://docs.aws.amazon.com/xray-sdk-for-java/latest/javadoc/com/amazonaws/xray/handlers/TracingHandler.html);

public class SessionModel {
  private AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard()
        .withRegion(Constants.REGION)
        .withRequestHandlers(new TracingHandler(AWSXRay.getGlobalRecorder()))
        .build();
  private DynamoDBMapper mapper = new DynamoDBMapper(client);
```

If you remove the AWS SDK Instrumentor submodule from project dependencies, only the manually instrumented AWS SDK clients appear in the trace map.

# Creating additional subsegments
<a name="scorekeep-subsegments"></a>

**Note**  
X-Ray SDK/Daemon Maintenance Notice – On February 25th, 2026, the AWS X-Ray SDKs/Daemon will enter maintenance mode, where AWS will limit X-Ray SDK and Daemon releases to address security issues only. For more information on the support timeline, see [X-Ray SDK and Daemon Support timeline](xray-sdk-daemon-timeline.md). We recommend to migrate to OpenTelemetry. For more information on migrating to OpenTelemetry, see [Migrating from X-Ray instrumentation to OpenTelemetry instrumentation ](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-migration.html).

In the user model class, the application manually creates subsegments to group all downstream calls made within the `saveUser` function and adds metadata.

**Example [https://github.com/awslabs/eb-java-scorekeep/tree/xray/src/main/java/scorekeep/UserModel.java](https://github.com/awslabs/eb-java-scorekeep/tree/xray/src/main/java/scorekeep/UserModel.java) - Custom subsegments**  

```
import [com.amazonaws.xray.AWSXRay](https://docs.aws.amazon.com/xray-sdk-for-java/latest/javadoc/com/amazonaws/xray/AWSXRay.html);
import [com.amazonaws.xray.entities.Subsegment](https://docs.aws.amazon.com/xray-sdk-for-java/latest/javadoc/com/amazonaws/xray/entities/Subsegment.html);
...
    public void saveUser(User user) {
    // Wrap in subsegment
    Subsegment subsegment = AWSXRay.beginSubsegment("## UserModel.saveUser");
    try {
      mapper.save(user);
    } catch (Exception e) {
      subsegment.addException(e);
      throw e;
    } finally {
      AWSXRay.endSubsegment();
    }
  }
```

# Recording annotations, metadata, and user IDs
<a name="scorekeep-annotations"></a>

**Note**  
X-Ray SDK/Daemon Maintenance Notice – On February 25th, 2026, the AWS X-Ray SDKs/Daemon will enter maintenance mode, where AWS will limit X-Ray SDK and Daemon releases to address security issues only. For more information on the support timeline, see [X-Ray SDK and Daemon Support timeline](xray-sdk-daemon-timeline.md). We recommend to migrate to OpenTelemetry. For more information on migrating to OpenTelemetry, see [Migrating from X-Ray instrumentation to OpenTelemetry instrumentation ](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-migration.html).

In the game model class, the application records `Game` objects in a [metadata](xray-sdk-java-segment.md#xray-sdk-java-segment-metadata) block each time it saves a game in DynamoDB. Separately, the application records game IDs in [annotations](xray-sdk-java-segment.md#xray-sdk-java-segment-annotations) for use with [filter expressions](xray-console-filters.md).

**Example [https://github.com/awslabs/eb-java-scorekeep/tree/xray/src/main/java/scorekeep/GameModel.java](https://github.com/awslabs/eb-java-scorekeep/tree/xray/src/main/java/scorekeep/GameModel.java) – Annotations and metadata**  

```
import [com.amazonaws.xray.AWSXRay](https://docs.aws.amazon.com/xray-sdk-for-java/latest/javadoc/com/amazonaws/xray/AWSXRay.html);
import [com.amazonaws.xray.entities.Segment](https://docs.aws.amazon.com/xray-sdk-for-java/latest/javadoc/com/amazonaws/xray/entities/Segment.html);
import [com.amazonaws.xray.entities.Subsegment](https://docs.aws.amazon.com/xray-sdk-for-java/latest/javadoc/com/amazonaws/xray/entities/Subsegment.html);
...
  public void saveGame(Game game) throws SessionNotFoundException {
    // wrap in subsegment
    Subsegment subsegment = AWSXRay.beginSubsegment("## GameModel.saveGame");
    try {
      // check session
      String sessionId = game.getSession();
      if (sessionModel.loadSession(sessionId) == null ) {
        throw new SessionNotFoundException(sessionId);
      }
      Segment segment = AWSXRay.getCurrentSegment();
      subsegment.putMetadata("resources", "game", game);
      segment.putAnnotation("gameid", game.getId());
      mapper.save(game);
    } catch (Exception e) {
      subsegment.addException(e);
      throw e;
    } finally {
      AWSXRay.endSubsegment();
    }
  }
```

In the move controller, the application records [user IDs](xray-sdk-java-segment.md#xray-sdk-java-segment-userid) with `setUser`. User IDs are recorded in a separate field on segments and are indexed for use with search.

**Example [src/main/java/scorekeep/MoveController.java](https://github.com/awslabs/eb-java-scorekeep/tree/xray/src/main/java/scorekeep/MoveController.java) – User ID**  

```
import [com.amazonaws.xray.AWSXRay](https://docs.aws.amazon.com/xray-sdk-for-java/latest/javadoc/com/amazonaws/xray/AWSXRay.html);
...
  @RequestMapping(value="/{userId}", method=RequestMethod.POST)
  public Move newMove(@PathVariable String sessionId, @PathVariable String gameId, @PathVariable String userId, @RequestBody String move) throws SessionNotFoundException, GameNotFoundException, StateNotFoundException, RulesException {
    AWSXRay.getCurrentSegment().setUser(userId);
    return moveFactory.newMove(sessionId, gameId, userId, move);
  }
```

# Instrumenting outgoing HTTP calls
<a name="scorekeep-httpclient"></a>

**Note**  
X-Ray SDK/Daemon Maintenance Notice – On February 25th, 2026, the AWS X-Ray SDKs/Daemon will enter maintenance mode, where AWS will limit X-Ray SDK and Daemon releases to address security issues only. For more information on the support timeline, see [X-Ray SDK and Daemon Support timeline](xray-sdk-daemon-timeline.md). We recommend to migrate to OpenTelemetry. For more information on migrating to OpenTelemetry, see [Migrating from X-Ray instrumentation to OpenTelemetry instrumentation ](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-migration.html).

The user factory class shows how the application uses the X-Ray SDK for Java's version of `HTTPClientBuilder` to instrument outgoing HTTP calls.

**Example [https://github.com/awslabs/eb-java-scorekeep/tree/xray/src/main/java/scorekeep/UserFactory.java](https://github.com/awslabs/eb-java-scorekeep/tree/xray/src/main/java/scorekeep/UserFactory.java) – HTTPClient instrumentation**  

```
import [com.amazonaws.xray.proxies.apache.http.HttpClientBuilder](https://docs.aws.amazon.com/xray-sdk-for-java/latest/javadoc/com/amazonaws/xray/proxies/apache/http/HttpClientBuilder.html);

  public String randomName() throws IOException {
    CloseableHttpClient httpclient = HttpClientBuilder.create().build();
    HttpGet httpGet = new HttpGet("http://uinames.com/api/");
    CloseableHttpResponse response = httpclient.execute(httpGet);
    try {
      HttpEntity entity = response.getEntity();
      InputStream inputStream = entity.getContent();
      ObjectMapper mapper = new ObjectMapper();
      Map<String, String> jsonMap = mapper.readValue(inputStream, Map.class);
      String name = jsonMap.get("name");
      EntityUtils.consume(entity);
      return name;
    } finally {
      response.close();
    }
  }
```

If you currently use `org.apache.http.impl.client.HttpClientBuilder`, you can simply swap out the import statement for that class with one for `com.amazonaws.xray.proxies.apache.http.HttpClientBuilder`.

# Instrumenting calls to a PostgreSQL database
<a name="scorekeep-postgresql"></a>

**Note**  
X-Ray SDK/Daemon Maintenance Notice – On February 25th, 2026, the AWS X-Ray SDKs/Daemon will enter maintenance mode, where AWS will limit X-Ray SDK and Daemon releases to address security issues only. For more information on the support timeline, see [X-Ray SDK and Daemon Support timeline](xray-sdk-daemon-timeline.md). We recommend to migrate to OpenTelemetry. For more information on migrating to OpenTelemetry, see [Migrating from X-Ray instrumentation to OpenTelemetry instrumentation ](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-migration.html).

The `application-pgsql.properties` file adds the X-Ray PostgreSQL tracing interceptor to the data source created in [https://github.com/awslabs/eb-java-scorekeep/tree/xray/src/main/java/scorekeep/RdsWebConfig.java](https://github.com/awslabs/eb-java-scorekeep/tree/xray/src/main/java/scorekeep/RdsWebConfig.java).

**Example [https://github.com/awslabs/eb-java-scorekeep/tree/xray/src/main/resources/application-pgsql.properties](https://github.com/awslabs/eb-java-scorekeep/tree/xray/src/main/resources/application-pgsql.properties) – PostgreSQL database instrumentation**  

```
spring.datasource.continue-on-error=true
spring.jpa.show-sql=false
spring.jpa.hibernate.ddl-auto=create-drop
spring.datasource.jdbc-interceptors=com.amazonaws.xray.sql.postgres.TracingInterceptor
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL94Dialect
```

**Note**  
See [Configuring Databases with Elastic Beanstalk](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features.managing.db.html) in the *AWS Elastic Beanstalk Developer Guide* for details on how to add a PostgreSQL database to the application environment.

The X-Ray demo page in the `xray` branch includes a demo that uses the instrumented data source to generate traces that show information about the SQL queries that it generates. Navigate to the `/#/xray` path in the running application or choose **Powered by AWS X-Ray** in the navigation bar to see the demo page.

![\[AWS X-Ray integration demo page showing game session tracing and SQL query tracing options.\]](http://docs.aws.amazon.com/xray/latest/devguide/images/scorekeep-demo.png)


Choose **Trace SQL queries** to simulate game sessions and store the results in the attached database. Then, choose **View traces in AWS X-Ray** to see a filtered list of traces that hit the API's `/api/history` route.

Choose one of the traces from the list to see the timeline, including the SQL query.

![\[Timeline view of a trace showing method, response, duration, and age for a GET request.\]](http://docs.aws.amazon.com/xray/latest/devguide/images/scorekeep-trace-sql.png)


# Instrumenting AWS Lambda functions
<a name="scorekeep-lambda"></a>

**Note**  
X-Ray SDK/Daemon Maintenance Notice – On February 25th, 2026, the AWS X-Ray SDKs/Daemon will enter maintenance mode, where AWS will limit X-Ray SDK and Daemon releases to address security issues only. For more information on the support timeline, see [X-Ray SDK and Daemon Support timeline](xray-sdk-daemon-timeline.md). We recommend to migrate to OpenTelemetry. For more information on migrating to OpenTelemetry, see [Migrating from X-Ray instrumentation to OpenTelemetry instrumentation ](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-migration.html).

Scorekeep uses two AWS Lambda functions. The first is a Node.js function from the `lambda` branch that generates random names for new users. When a user creates a session without entering a name, the application calls a function named `random-name` with the AWS SDK for Java. The X-Ray SDK for Java records information about the call to Lambda in a subsegment like any other call made with an instrumented AWS SDK client.

**Note**  
Running the `random-name` Lambda function requires the creation of additional resources outside of the Elastic Beanstalk environment. See the readme for more information and instructions: [AWS Lambda Integration](https://github.com/awslabs/eb-java-scorekeep/tree/xray/README.md#aws-lambda-integration).

The second function, `scorekeep-worker`, is a Python function that runs independently of the Scorekeep API. When a game ends, the API writes the session ID and game ID to an SQS queue. The worker function reads items from the queue, and calls the Scorekeep API to construct complete records of each game session for storage in Amazon S3.

Scorekeep includes CloudFormation templates and scripts to create both functions. Because you need to bundle the X-Ray SDK with the function code, the templates create the functions without any code. When you deploy Scorekeep, a configuration file included in the `.ebextensions` folder creates a source bundle that includes the SDK, and updates the function code and configuration with the AWS Command Line Interface.

**Topics**
+ [Random name](#scorekeep-lambda-randomname)
+ [Worker](#scorekeep-lambda-worker)

## Random name
<a name="scorekeep-lambda-randomname"></a>

Scorekeep calls the random name function when a user starts a game session without signing in or specifying a user name. When Lambda processes the call to `random-name`, it reads the [tracing header](xray-concepts.md#xray-concepts-tracingheader), which contains the trace ID and sampling decision written by the X-Ray SDK for Java.

For each sampled request, Lambda runs the X-Ray daemon and writes two segments. The first segment records information about the call to Lambda that invokes the function. This segment contains the same information as the subsegment recorded by Scorekeep, but from the Lambda point of view. The second segment represents the work that the function does.

Lambda passes the function segment to the X-Ray SDK through the function context. When you instrument a Lambda function, you don't use the SDK to [create a segment for incoming requests](xray-sdk-nodejs-middleware.md). Lambda provides the segment, and you use the SDK to instrument clients and write subsegments.

![\[Trace map showing how scorekeep calls a Lambda function to get random names for new users\]](http://docs.aws.amazon.com/xray/latest/devguide/images/scorekeep-servicemap-lambda-node.png)


The `random-name` function is implemented in Node.js. It uses the SDK for JavaScript in Node.js to send notifications with Amazon SNS, and the X-Ray SDK for Node.js to instrument the AWS SDK client. To write annotations, the function creates a custom subsegment with `AWSXRay.captureFunc`, and writes annotations in the instrumented function. In Lambda, you can't write annotations directly to the function segment, only to a subsegment that you create.

**Example [https://github.com/awslabs/eb-java-scorekeep/tree/xray/function/index.js](https://github.com/awslabs/eb-java-scorekeep/tree/xray/function/index.js) -- Random name Lambda function**  

```
var AWSXRay = require('aws-xray-sdk-core');
var AWS = AWSXRay.captureAWS(require('aws-sdk'));

AWS.config.update({region: process.env.AWS_REGION});
var Chance = require('chance');

var myFunction = function(event, context, callback) {
  var sns = new AWS.SNS();
  var chance = new Chance();
  var userid = event.userid;
  var name = chance.first();

  AWSXRay.captureFunc('annotations', function(subsegment){
    subsegment.addAnnotation('Name', name);
    subsegment.addAnnotation('UserID', event.userid);
  });

  // Notify
  var params = {
    Message: 'Created randon name "' + name + '"" for user "' + userid + '".',
    Subject: 'New user: ' + name,
    TopicArn: process.env.TOPIC_ARN
  };
  sns.publish(params, function(err, data) {
    if (err) {
      console.log(err, err.stack);
      callback(err);
    }
    else {
      console.log(data);
      callback(null, {"name": name});
    }
  });
};

exports.handler = myFunction;
```

This function is created automatically when you deploy the sample application to Elastic Beanstalk. The `xray` branch includes a script to create a blank Lambda function. Configuration files in the `.ebextensions` folder build the function package with `npm install` during deployment, and then update the Lambda function with the AWS CLI.

## Worker
<a name="scorekeep-lambda-worker"></a>

The instrumented worker function is provided in its own branch, `xray-worker`, as it cannot run unless you create the worker function and related resources first. See [the branch readme](https://github.com/awslabs/eb-java-scorekeep/tree/xray-worker/README.md) for instructions.

The function is triggered by a bundled Amazon CloudWatch Events event every 5 minutes. When it runs, the function pulls an item from an Amazon SQS queue that Scorekeep manages. Each message contains information about a completed game.

The worker pulls the game record and documents from other tables that the game record references. For example, the game record in DynamoDB includes a list of moves that were executed during the game. The list does not contain the moves themselves, but rather IDs of moves that are stored in a separate table.

Sessions, and states are stored as references as well. This keeps the entries in the game table from being too large, but requires additional calls to get all of the information about the game. The worker dereferences all of these entries and constructs a complete record of the game as a single document in Amazon S3. When you want to do analytics on the data, you can run queries on it directly in Amazon S3 with Amazon Athena without running read-heavy data migrations to get your data out of DynamoDB.

![\[Trace map showing how the scorekeep worker function uses Amazon SQS, Amazon S3, and the scorekeep API.\]](http://docs.aws.amazon.com/xray/latest/devguide/images/scorekeep-servicemap-lambdaworker-node.png)


The worker function has active tracing enabled in its configuration in AWS Lambda. Unlike the random name function, the worker does not receive a request from an instrumented application, so AWS Lambda doesn't receive a tracing header. With active tracing, Lambda creates the trace ID and makes sampling decisions.

The X-Ray SDK for Python is just a few lines at the top of the function that import the SDK and run its `patch_all` function to patch the AWS SDK for Python (Boto) and HTTclients that it uses to call Amazon SQS and Amazon S3. When the worker calls the Scorekeep API, the SDK adds the [tracing header](xray-concepts.md#xray-concepts-tracingheader) to the request to trace calls through the API.

**Example [https://github.com/awslabs/eb-java-scorekeep/tree/xray-worker/_lambda/scorekeep-worker/scorekeep-worker.py](https://github.com/awslabs/eb-java-scorekeep/tree/xray-worker/_lambda/scorekeep-worker/scorekeep-worker.py) -- Worker Lambda function**  

```
import os
import boto3
import json
import requests
import time
from aws_xray_sdk.core import xray_recorder
from aws_xray_sdk.core import patch_all

patch_all()
queue_url = os.environ['WORKER_QUEUE']

def lambda_handler(event, context):
    # Create SQS client
    sqs = boto3.client('sqs')
    s3client = boto3.client('s3')

    # Receive message from SQS queue
    response = sqs.receive_message(
        QueueUrl=queue_url,
        AttributeNames=[
            'SentTimestamp'
        ],
        MaxNumberOfMessages=1,
        MessageAttributeNames=[
            'All'
        ],
        VisibilityTimeout=0,
        WaitTimeSeconds=0
    )
   ...
```

# Instrumenting startup code
<a name="scorekeep-startup"></a>

**Note**  
X-Ray SDK/Daemon Maintenance Notice – On February 25th, 2026, the AWS X-Ray SDKs/Daemon will enter maintenance mode, where AWS will limit X-Ray SDK and Daemon releases to address security issues only. For more information on the support timeline, see [X-Ray SDK and Daemon Support timeline](xray-sdk-daemon-timeline.md). We recommend to migrate to OpenTelemetry. For more information on migrating to OpenTelemetry, see [Migrating from X-Ray instrumentation to OpenTelemetry instrumentation ](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-migration.html).

The X-Ray SDK for Java automatically creates segments for incoming requests. As long as a request is in scope, you can use instrumented clients and record subsegments without issue. If you try to use an instrumented client in startup code, though, you'll get a [SegmentNotFoundException](https://docs.aws.amazon.com/xray-sdk-for-java/latest/javadoc/com/amazonaws/xray/exceptions/SegmentNotFoundException.html).

Startup code runs outside of the standard request/response flow of a web application, so you need to create segments manually to instrument it. Scorekeep shows the instrumentation of startup code in its `WebConfig` files. Scorekeep calls an SQL database and Amazon SNS during startup.

![\[Diagram showing client requests to Scorekeeper-init, which connects to SQL database and SNS.\]](http://docs.aws.amazon.com/xray/latest/devguide/images/scorekeep-servicemap-init.png)


The default `WebConfig` class creates an Amazon SNS subscription for notifications. To provide a segment for the X-Ray SDK to write to when the Amazon SNS client is used, Scorekeep calls `beginSegment` and `endSegment` on the global recorder.

**Example [https://github.com/awslabs/eb-java-scorekeep/tree/xray/src/main/java/scorekeep/WebConfig.java#L49](https://github.com/awslabs/eb-java-scorekeep/tree/xray/src/main/java/scorekeep/WebConfig.java#L49) – Instrumented AWS SDK client in startup code**  

```
AWSXRay.beginSegment("Scorekeep-init");
if ( System.getenv("NOTIFICATION_EMAIL") != null ){
  try { Sns.createSubscription(); }
  catch (Exception e ) {
    logger.warn("Failed to create subscription for email "+  System.getenv("NOTIFICATION_EMAIL"));
  }
}
AWSXRay.endSegment();
```

In `RdsWebConfig`, which Scorekeep uses when an Amazon RDS database is connected, the configuration also creates a segment for the SQL client that Hibernate uses when it applies the database schema during startup.

**Example [https://github.com/awslabs/eb-java-scorekeep/tree/xray/src/main/java/scorekeep/RdsWebConfig.java#L83](https://github.com/awslabs/eb-java-scorekeep/tree/xray/src/main/java/scorekeep/RdsWebConfig.java#L83) – Instrumented SQL database client in startup code**  

```
@PostConstruct
public void schemaExport() {
  EntityManagerFactoryImpl entityManagerFactoryImpl = (EntityManagerFactoryImpl) localContainerEntityManagerFactoryBean.getNativeEntityManagerFactory();
  SessionFactoryImplementor sessionFactoryImplementor = entityManagerFactoryImpl.getSessionFactory();
  StandardServiceRegistry standardServiceRegistry = sessionFactoryImplementor.getSessionFactoryOptions().getServiceRegistry();
  MetadataSources metadataSources = new MetadataSources(new BootstrapServiceRegistryBuilder().build());
  metadataSources.addAnnotatedClass(GameHistory.class);
  MetadataImplementor metadataImplementor = (MetadataImplementor) metadataSources.buildMetadata(standardServiceRegistry);
  SchemaExport schemaExport = new SchemaExport(standardServiceRegistry, metadataImplementor);

  AWSXRay.beginSegment("Scorekeep-init");
  schemaExport.create(true, true);
  AWSXRay.endSegment();
}
```

`SchemaExport` runs automatically and uses an SQL client. Since the client is instrumented, Scorekeep must override the default implementation and provide a segment for the SDK to use when the client is invoked.

# Instrumenting scripts
<a name="scorekeep-scripts"></a>

**Note**  
X-Ray SDK/Daemon Maintenance Notice – On February 25th, 2026, the AWS X-Ray SDKs/Daemon will enter maintenance mode, where AWS will limit X-Ray SDK and Daemon releases to address security issues only. For more information on the support timeline, see [X-Ray SDK and Daemon Support timeline](xray-sdk-daemon-timeline.md). We recommend to migrate to OpenTelemetry. For more information on migrating to OpenTelemetry, see [Migrating from X-Ray instrumentation to OpenTelemetry instrumentation ](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-migration.html).

You can also instrument code that isn't part of your application. When the X-Ray daemon is running, it will relay any segments that it receives to X-Ray, even if they are not generated by the X-Ray SDK. Scorekeep uses its own scripts to instrument the build that compiles the application during deployment.

**Example [https://github.com/awslabs/eb-java-scorekeep/tree/xray/bin/build.sh](https://github.com/awslabs/eb-java-scorekeep/tree/xray/bin/build.sh) – Instrumented build script**  

```
SEGMENT=$(python bin/xray_start.py)
gradle build --quiet --stacktrace &> /var/log/gradle.log; GRADLE_RETURN=$?
if (( GRADLE_RETURN != 0 )); then 
  echo "Gradle failed with exit status $GRADLE_RETURN" >&2
  python bin/xray_error.py "$SEGMENT" "$(cat /var/log/gradle.log)"
  exit 1
fi
python bin/xray_success.py "$SEGMENT"
```

[https://github.com/awslabs/eb-java-scorekeep/tree/xray/bin/xray_start.py](https://github.com/awslabs/eb-java-scorekeep/tree/xray/bin/xray_start.py), [https://github.com/awslabs/eb-java-scorekeep/tree/xray/bin/xray_error.py](https://github.com/awslabs/eb-java-scorekeep/tree/xray/bin/xray_error.py) and [https://github.com/awslabs/eb-java-scorekeep/tree/xray/bin/xray_success.py](https://github.com/awslabs/eb-java-scorekeep/tree/xray/bin/xray_success.py) are simple Python scripts that construct segment objects, convert them to JSON documents, and send them to the daemon over UDP. If the Gradle build fails, you can find the error message by clicking on the **scorekeep-build** node in the X-Ray console trace map.

![\[Diagram showing client connection to Scorekeep-build with average time of 14.6s and 0.07/min.\]](http://docs.aws.amazon.com/xray/latest/devguide/images/scorekeep-servicemap-builderror.png)


![\[Timeline view showing Scorekeep-build process with 14.6 second duration and warning icon.\]](http://docs.aws.amazon.com/xray/latest/devguide/images/scorekeep-timeline-builderror.png)


![\[Error message showing build failure due to missing ElasticBeanstalkPlugin symbol in RdsWebConfig class.\]](http://docs.aws.amazon.com/xray/latest/devguide/images/scorekeep-exception-builderror.png)


# Instrumenting a web app client
<a name="scorekeep-client"></a>

**Note**  
X-Ray SDK/Daemon Maintenance Notice – On February 25th, 2026, the AWS X-Ray SDKs/Daemon will enter maintenance mode, where AWS will limit X-Ray SDK and Daemon releases to address security issues only. For more information on the support timeline, see [X-Ray SDK and Daemon Support timeline](xray-sdk-daemon-timeline.md). We recommend to migrate to OpenTelemetry. For more information on migrating to OpenTelemetry, see [Migrating from X-Ray instrumentation to OpenTelemetry instrumentation ](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-migration.html).

In the [https://github.com/awslabs/eb-java-scorekeep/tree/xray-cognito](https://github.com/awslabs/eb-java-scorekeep/tree/xray-cognito) branch, Scorekeep uses Amazon Cognito to enable users to create an account and sign in with it to retrieve their user information from an Amazon Cognito user pool. When a user signs in, Scorekeep uses an Amazon Cognito identity pool to get temporary AWS credentials for use with the AWS SDK for JavaScript.

The identity pool is configured to let signed-in users write trace data to AWS X-Ray. The web app uses these credentials to record the signed-in user's ID, the browser path, and the client's view of calls to the Scorekeep API.

Most of the work is done in a service class named `xray`. This service class provides methods for generating the required identifiers, creating in-progress segments, finalizing segments, and sending segment documents to the X-Ray API.

**Example [https://github.com/awslabs/eb-java-scorekeep/tree/xray-cognito/public/app/xray.js](https://github.com/awslabs/eb-java-scorekeep/tree/xray-cognito/public/app/xray.js) – Record and upload segments**  

```
...
  service.beginSegment = function() {
    var segment = {};
    var traceId = '1-' + service.getHexTime() + '-' + service.getHexId(24);

    var id = service.getHexId(16);
    var startTime = service.getEpochTime();

    segment.trace_id = traceId;
    segment.id = id;
    segment.start_time = startTime;
    segment.name = 'Scorekeep-client';
    segment.in_progress = true;
    segment.user =  sessionStorage['userid'];
    segment.http = {
      request: {
        url: window.location.href
      }
    };

    var documents = [];
    documents[0] = JSON.stringify(segment);
    service.putDocuments(documents);
    return segment;
  }

  service.endSegment = function(segment) {
    var endTime = service.getEpochTime();
    segment.end_time = endTime;
    segment.in_progress = false;
    var documents = [];
    documents[0] = JSON.stringify(segment);
    service.putDocuments(documents);
  }

  service.putDocuments = function(documents) {
    var xray = new AWS.XRay();
    var params = {
      TraceSegmentDocuments: documents
    };
    xray.putTraceSegments(params, function(err, data) {
      if (err) {
        console.log(err, err.stack);
      } else {
        console.log(data);
      }
    })
  }
```

These methods are called in header and `transformResponse` functions in the resource services that the web app uses to call the Scorekeep API. To include the client segment in the same trace as the segment that the API generates, the web app must include the trace ID and segment ID in a [tracing header](xray-concepts.md#xray-concepts-tracingheader) (`X-Amzn-Trace-Id`) that the X-Ray SDK can read. When the instrumented Java application receives a request with this header, the X-Ray SDK for Java uses the same trace ID and makes the segment from the web app client the parent of its segment. 

**Example [https://github.com/awslabs/eb-java-scorekeep/tree/xray-cognito/public/app/services.js](https://github.com/awslabs/eb-java-scorekeep/tree/xray-cognito/public/app/services.js) – Recording segments for angular resource calls and writing tracing headers**  

```
var module = angular.module('scorekeep');
module.factory('SessionService', function($resource, api, XRay) {
  return $resource(api + 'session/:id', { id: '@_id' }, {
    segment: {},
    get: {
      method: 'GET',
      headers: {
        'X-Amzn-Trace-Id': function(config) {
          segment = XRay.beginSegment();
          return XRay.getTraceHeader(segment);
        }
      },
      transformResponse: function(data) {
        XRay.endSegment(segment);
        return angular.fromJson(data);
      },
    },
...
```

The resulting trace map includes a node for the web app client.

![\[\]](http://docs.aws.amazon.com/xray/latest/devguide/images/scorekeep-servicemap-client.png)


Traces that include segments from the web app show the URL that the user sees in the browser (paths starting with `/#/`). Without client instrumentation, you only get the URL of the API resource that the web app calls (paths starting with `/api/`).

![\[\]](http://docs.aws.amazon.com/xray/latest/devguide/images/scorekeep-traces-client.png)


# Using instrumented clients in worker threads
<a name="scorekeep-workerthreads"></a>

**Note**  
X-Ray SDK/Daemon Maintenance Notice – On February 25th, 2026, the AWS X-Ray SDKs/Daemon will enter maintenance mode, where AWS will limit X-Ray SDK and Daemon releases to address security issues only. For more information on the support timeline, see [X-Ray SDK and Daemon Support timeline](xray-sdk-daemon-timeline.md). We recommend to migrate to OpenTelemetry. For more information on migrating to OpenTelemetry, see [Migrating from X-Ray instrumentation to OpenTelemetry instrumentation ](https://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-migration.html).

Scorekeep uses a worker thread to publish a notification to Amazon SNS when a user wins a game. Publishing the notification takes longer than the rest of the request operations combined, and doesn't affect the client or user. Therefore, performing the task asynchronously is a good way to improve response time.

However, the X-Ray SDK for Java doesn't know which segment was active when the thread is created. As a result, when you try to use the instrumented AWS SDK for Java client within the thread, it throws a `SegmentNotFoundException`, crashing the thread.

**Example Web-1.error.log**  

```
Exception in thread "Thread-2" com.amazonaws.xray.exceptions.SegmentNotFoundException: Failed to begin subsegment named 'AmazonSNS': segment cannot be found.
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
...
```

To fix this, the application uses `GetTraceEntity` to get a reference to the segment in the main thread, and `Entity.run()` to safely run the worker thread code with access to the segment's context.

**Example [https://github.com/awslabs/eb-java-scorekeep/tree/xray/src/main/java/scorekeep/MoveFactory.java#L70](https://github.com/awslabs/eb-java-scorekeep/tree/xray/src/main/java/scorekeep/MoveFactory.java#L70) – Passing trace context to a worker thread**  

```
import [com.amazonaws.xray.AWSXRay](https://docs.aws.amazon.com/xray-sdk-for-java/latest/javadoc/com/amazonaws/xray/AWSXRay.html);
import [com.amazonaws.xray.AWSXRayRecorder](https://docs.aws.amazon.com/xray-sdk-for-java/latest/javadoc/com/amazonaws/xray/AWSXRayRecorder.html);
import [com.amazonaws.xray.entities.Entity](https://docs.aws.amazon.com/xray-sdk-for-java/latest/javadoc/com/amazonaws/xray/entities/Entity.html);
import [com.amazonaws.xray.entities.Segment](https://docs.aws.amazon.com/xray-sdk-for-java/latest/javadoc/com/amazonaws/xray/entities/Segment.html);
import [com.amazonaws.xray.entities.Subsegment](https://docs.aws.amazon.com/xray-sdk-for-java/latest/javadoc/com/amazonaws/xray/entities/Subsegment.html);
...
      Entity segment = recorder.getTraceEntity();
      Thread comm = new Thread() {
        public void run() {
         segment.run(() -> {
            Subsegment subsegment = AWSXRay.beginSubsegment("## Send notification");
            Sns.sendNotification("Scorekeep game completed", "Winner: " + userId);
            AWSXRay.endSubsegment();
          }
        }
```

Because the request is now resolved before the call to Amazon SNS, the application creates a separate subsegment for the thread. This prevents the X-Ray SDK from closing the segment before it records the response from Amazon SNS. If no subsegment is open when Scorekeep resolved the request, the response from Amazon SNS could be lost.

![\[\]](http://docs.aws.amazon.com/xray/latest/devguide/images/scorekeep-workerthread.png)


See [Passing segment context between threads in a multithreaded application](xray-sdk-java-multithreading.md) for more information about multithreading.