Trace Python Requests with AWS X-Ray
Trace Python requests with AWS X-Ray in this practical tutorial. Learn to instrument your Python app, send trace data to AWS X-Ray, and debug performance issues step by step.
Focus: trace python requests with aws x-ray
You've deployed a Python app on AWS, it's handling requests, but when users complain about slow responses, you have no idea which service — database, cache, or API — is the culprit. Manually checking logs across EC2, Lambda, and DynamoDB is a nightmare, and by the time you find the bottleneck, your customers have already moved on. Tracing Python requests with AWS X-Ray gives you end-to-end visibility into every request, showing you exactly where time is spent, so you can fix performance issues before they become outages.
1. The Problem This Lesson Solves
Modern Python applications are rarely a single monolithic script. They consist of multiple components: an API gateway, Lambda functions, EC2 instances running Flask or Django, and managed services like DynamoDB, S3, or RDS. When a request comes in, it might fan out across several of these services, and a slowdown in any one of them can bottleneck the entire flow. Traditional logging only tells you what happened in each service — it doesn't connect the dots across services.
Without tracing, you're flying blind. You might see a 500 error in your logs but have no idea which downstream call failed or took too long. You might know that a Lambda function is slow, but you can't tell if it's the function logic or a database query. Debugging becomes a guessing game, and in a distributed system, guessing is expensive.
AWS X-Ray solves this by providing a distributed tracing service that captures, analyzes, and visualizes the path of a request as it travels through your application. It shows you a service map and a trace timeline, so you can quickly identify bottlenecks, errors, and latency issues across all your AWS resources and services.
2. Core Concept / Mental Model
Think of AWS X-Ray as a flight recorder for your application. Every time a request comes in, X-Ray creates a trace — a record of all the work done to process that request. A trace is composed of segments and subsegments. A segment represents the work done by a single service (e.g., your EC2 instance), while a subsegment represents a smaller unit of work within that service (e.g., an HTTP call to DynamoDB). Together, these form a service map that shows how services interact and where time is spent.
X-Ray works by using tracing headers that propagate the trace context across services. When one service makes an HTTP call to another, it adds a header like X-Amzn-Trace-Id to the request. The receiving service picks up this header and continues the same trace, so you get a complete picture of the request's journey.
You can instrument your Python application in two ways: using the AWS X-Ray SDK for Python to add tracing directly in your code, or using auto-instrumentation for frameworks like Flask and Django. The SDK intercepts incoming and outgoing requests, records timing and metadata, and sends the data to X-Ray via the X-Ray daemon.
3. How It Works Step by Step
Here's the high-level flow of how tracing with AWS X-Ray works:
- Your Python app receives a request — The X-Ray SDK's middleware (e.g.,
AWSXRayMiddlewarefor Flask or Django) intercepts the incoming HTTP request. - A trace is created — The SDK creates a new trace or picks up an existing one from the
X-Amzn-Trace-Idheader. It creates a segment for the incoming request. - Outgoing calls are captured — When your code makes an HTTP request to another service (e.g., via
requestsor boto3), the SDK patches those libraries and adds the trace header to the outgoing request. It also creates a subsegment to record the time spent on that call. - Subsegments are recorded — Each subsegment captures metadata like the service name, URL, status code, and duration.
- Data is sent to the X-Ray daemon — The SDK batches and sends the trace data to a local daemon process (running on the same instance or as a sidecar in Lambda) via UDP.
- The daemon forwards to X-Ray API — The daemon uploads the data to the AWS X-Ray service.
- You view and analyze traces — In the AWS Console, you can open individual traces, see the timeline, and identify bottlenecks.
For this to work, you need to set up the X-Ray SDK, enable it for your framework, and ensure the X-Ray daemon is running in your environment (e.g., as a systemd service on EC2 or as a layer in Lambda).
4. Hands-On Walkthrough
Let's walk through a practical example: tracing a Flask app that calls an external API and runs a query against DynamoDB.
Prerequisites
- AWS account with X-Ray enabled (free tier covers small usage)
- Python 3.10+ and pip
- An EC2 instance or local environment with AWS credentials configured
Step 1: Install the SDK and dependencies
pip install aws-xray-sdk flask requests boto3
Step 2: Run the X-Ray daemon
If you're on EC2, install and start the daemon:
wget https://s3.dualstack.us-east-1.amazonaws.com/aws-xray-assets.us-east-1/xray-daemon/aws-xray-daemon-3.x.deb
sudo dpkg -i aws-xray-daemon-3.x.deb
sudo systemctl start xray
For local testing, run the daemon in a Docker container:
docker run -p 2000:2000 -p 2000:2000/udp --name xray-daemon -v ~/.aws/credentials:/aws/credentials amazon/aws-xray-daemon
Step 3: Instrument your Flask app
Create a simple Flask app that uses the X-Ray middleware and boto3 to query DynamoDB:
from flask import Flask
import requests
from aws_xray_sdk.core import xray_recorder, patch_all
from aws_xray_sdk.ext.flask.middleware import XRayMiddleware
app = Flask(__name__)
# Patch boto3 to trace DynamoDB calls and requests for HTTP calls
patch_all()
# Add X-Ray middleware
app.config['XRAY_MIDDLEWARE_SEGMENT_NAME'] = 'my-flask-app'
XRayMiddleware(app, xray_recorder)
@app.route('/')
def index():
# Trace a call to an external API
resp = requests.get('https://jsonplaceholder.typicode.com/todos/1')
# Trace a DynamoDB query (make sure table exists)
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('Todos')
item = table.get_item(Key={'id': '1'})
return f"API status: {resp.status_code}, DB item: {item.get('Item')}"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Step 4: Make requests and view traces
Run the app and make a few requests:
python app.py &
curl http://localhost:5000/
Then open the AWS Console, navigate to X-Ray > Traces, and filter by your app's service name. You should see traces for each request. Click on a trace to see the timeline: you'll see the my-flask-app segment, a subsegment for the requests call to jsonplaceholder, and another subsegment for the DynamoDB call. The service map will show lines between your app, the external API, and DynamoDB.
Expected Output
When you click a trace representing a request GET /, you see a timeline like this:
Total time: 120 ms
- my-flask-app (120 ms)
- requests to jsonplaceholder.com (80 ms)
- dynamodb table Todos (get_item) (30 ms)
Yes, the numbers are illustrative, but the key is you can now see the relative time each dependency takes.
5. Compare Options / When to Choose What
You have multiple ways to instrument Python apps with X-Ray:
| Approach | Effort | Control | Best for | Examples |
|---|---|---|---|---|
SDK auto-instrumentation (patch_all()) |
Low | Medium | Quick wins, standard frameworks like Flask/Django | Patching boto3, urllib, requests |
| Middleware for Flask/Django | Low | Medium | Any web server that uses WSGI/ASGI | Incoming requests traced automatically |
Manual instrumentation (xray_recorder.begin_subsegment()) |
High | High | Custom code paths, non-HTTP code | Database layer, background jobs |
| AWS Lambda layers | Low | Medium | Serverless apps | Attach X-Ray SDK layer, no code changes needed |
For most projects, start with auto-instrumentation plus middleware — it gives you 80% of the value with minimal effort. Manual instrumentation is useful when you have a complex custom function that you want to break down into finer-grained steps.
6. Troubleshooting & Edge Cases
- No traces appearing — Make sure the X-Ray daemon is running and that your AWS credentials have
xray:PutTraceSegmentspermission. Check if the daemon is listening on port 2000 (UDP). - Trace data incomplete in Lambda — For Lambda, you don't need a separate daemon; the SDK sends directly to X-Ray. Ensure you've enabled Active tracing in the Lambda function configuration.
- Missing subsegments for boto3 calls — If you forget to call
patch_all()orpatch(('boto3',)), the SDK won't instrument boto3, and you'll miss DynamoDB or S3 calls. - High performance overhead — The SDK adds a small overhead. If you're worried, turn off sampling (set
xray_recorder.configure(sampling=False)) for low-traffic apps, but be aware that you'll incur costs for every trace. - Trace context not propagated — If you're using an HTTP client that isn't patched (e.g.,
httpx), the SDK won't add tracing headers. You need to manually add theX-Amzn-Trace-Idheader to propagate context. - Latency in your trace is not in your app — If you see high latency in subsegments like DynamoDB, check if you're using provisioned or on-demand capacity; cold starts for Lambda or a subnet/VPC issue could also be the cause.
7. What You Learned & What's Next
You now understand how trace Python requests with AWS X-Ray, the core concept of segments and subsegments, and how to instrument a Flask app step by step. You've seen how to use the SDK, middleware, and boto3 patching to capture traces, and you know how to view them in the AWS Console. You also learned about the options for instrumentation and common pitfalls.
Next in the track, you'll likely dive into architecting microservices with Python and AWS, where you'll need this tracing skill to debug performance issues across multiple services. You can also explore integrating X-Ray with AWS Lambda to trace serverless requests, or use the X-Ray API to query traces programmatically.
Now practice: instrument a Django app or a Lambda function using X-Ray, or set up manual subsegments for a custom function in your code.
Practice recap
Now try instrumenting a simple endpoint that calls two external APIs and a DynamoDB table. Use the AWS X-Ray SDK to trace it, and then open the trace timeline in the AWS Console to see which call is slowest. Next, manually add a subsegment around a custom function to break down its timing.
Common mistakes
- Forgetting to call
patch_all()or patch specific libraries like boto3, resulting in missing subsegments for AWS SDK calls. - Not running the X-Ray daemon on EC2 or not having the required IAM permissions, so trace data is never sent
- Using an HTTP client that isn't patched (e.g., httpx) and missing trace context propagation across services
- Enabling X-Ray on Lambda without turning on Active tracing, so no trace data appears
- Ignoring sampling settings and accidentally paying for excessive traces on high-traffic applications
Variations
- Use the AWS X-Ray SDK for Django by adding
XRayMiddlewareto your Django MIDDLEWARE settings instead of Flask middleware - Use the AWS X-Ray SDK's
segmentandsubsegmentcontext managers for manual instrumentation of custom code paths - For serverless apps, attach the
AWS-X-Ray-SDK-Pythonlayer to your Lambda function and enable Active tracing in the Lambda console, no code changes required
Real-world use cases
- Debugging a slow REST API in production by tracing requests from the API Gateway through Lambda and DynamoDB to identify the faulting component
- Monitoring a Python microservice that calls multiple external APIs to detect which third-party dependencies are violating SLAs and causing user-facing latency
- Investigating an epidemic of 503 errors during peak load by analyzing the X-Ray service map to confirm that a specific RDS read replica is the bottleneck
Key takeaways
- AWS X-Ray provides distributed tracing that helps you debug performance and errors in Python apps
- A trace consists of segments (service-level) and subsegments (within a service) that map the request path
- To start tracing, you need to install the AWS X-Ray SDK, patch libraries like boto3 and requests, and run the X-Ray daemon
- You can use automatic middleware for Flask/Django or manual subsegments for custom code
- Sampling is important for cost control; adjust it based on traffic and budget
- You now have a practical approach to trace Python requests with AWS X-Ray and can apply it to real-world debugging
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.