Distributed Tracing with AWS X-Ray in Flask Application
Table of contents
Integrate AWS X-Ray to the Flask application for tracing
AWS X-Ray SDK for Python
Add library
aws-xray-sdk
to therequirements.txt
aws-xray-sdk
Run this command in a terminal to install the libraries for local development
pip install -r requirements.txt
Add code in
app.py
to add X-Ray Flask middleware to instruct for X-RAYfrom aws_xray_sdk.core import xray_recorder from aws_xray_sdk.ext.flask.middleware import XRayMiddleware app = Flask(__name__) xray_url = os.getenv("AWS_XRAY_URL") xray_recorder.configure(service='Cruddur', dynamic_naming=xray_url) XRayMiddleware(app, xray_recorder)
Use AWS CLI to create a group for X-RAY
$ aws xray create-group --group-name "Cruddur" --filter-expression "service(\"backend-flask\")" { "Group": { "GroupName": "Cruddur", "GroupARN": "arn:aws:xray:ap-southeast-2:461075076403:group/Cruddur/ELVUCCCTCVRE7VMACDBN3ST2HQ3NVW73EGPW6JUYMDJZH3VKJ3ZA", "FilterExpression": "service(\"backend-flask\")", "InsightsConfiguration": { "InsightsEnabled": false, "NotificationsEnabled": false } } }
The group
Cruddur
is created.Create a sampling rule
Add a file
aws/json/xray.json
{ "SamplingRule": { "RuleName": "Cruddur", "ResourceARN": "*", "Priority": 9000, "FixedRate": 0.1, "ReservoirSize": 5, "ServiceName": "Cruddur", "ServiceType": "*", "Host": "*", "HTTPMethod": "*", "URLPath": "*", "Version": 1 } }
$ aws xray create-sampling-rule --cli-input-json file://aws/json/xray.json { "SamplingRuleRecord": { "SamplingRule": { "RuleName": "Cruddur", "RuleARN": "arn:aws:xray:ap-southeast-2:461075076403:sampling-rule/Cruddur", "ResourceARN": "*", "Priority": 9000, "FixedRate": 0.1, "ReservoirSize": 5, "ServiceName": "backend-flask", "ServiceType": "*", "Host": "*", "HTTPMethod": "*", "URLPath": "*", "Version": 1, "Attributes": {} }, "CreatedAt": "2023-03-20T07:37:58+00:00", "ModifiedAt": "2023-03-20T07:37:58+00:00" } }
Add daemon service to Docker Compose
xray-daemon: image: "amazon/aws-xray-daemon" environment: AWS_ACCESS_KEY_ID: "${AWS_ACCESS_KEY_ID}" AWS_SECRET_ACCESS_KEY: "${AWS_SECRET_ACCESS_KEY}" AWS_REGION: "us-east-1" command: - "xray -o -b xray-daemon:2000" ports: - 2000:2000/udp
Add two env vars to
backend-flask
indocker-compose.yml
fileAWS_XRAY_URL: "*4567-${GITPOD_WORKSPACE_ID}.${GITPOD_WORKSPACE_CLUSTER_HOST}*" AWS_XRAY_DAEMON_ADDRESS: "xray-daemon:2000"
Add segments to X-RAY
The AWS X-Ray SDK for Python provides a predefined decorator that can be used to instrument Flask application endpoints. The decorator is called
xray_recorder.capture()
and it can be used to automatically trace your endpoint function with an X-Ray segment.Here's an example of how to use the xray_recorder.capture() decorator:
from flask import Flask from aws_xray_sdk.core import xray_recorder app = Flask(__name__) @app.route('/example_endpoint') @xray_recorder.capture('example_endpoint') def example_endpoint(): # Your code here return 'Hello, World!'
In this example, we apply the xray_recorder.capture() decorator to our example_endpoint() function. We provide a name for the segment as an argument to the decorator.
When the endpoint function is called, the xray_recorder.capture() decorator automatically creates a new X-Ray segment with the provided name, records the start and end times of the segment, and captures any subsegments or metadata associated with the endpoint's execution.
The xray_recorder.capture() decorator can be a convenient way to add X-Ray tracing to your Flask application without having to manually create and manage X-Ray segments in your code.
Add the decorator to the home activity endpoint
@app.route("/api/activities/home", methods=['GET']) @xray_recorder.capture('home_endpoint') def data_home(): data = HomeActivities.run() return data, 200
The segment
home_endpoint
is added.- No need to call
xray_recorder.begin_segment
explicitely when usingxray_recorder.capture()
decoratorIt's worth noting that using begin_segment() and end_segment() can be more error-prone and less convenient than using the capture() decorator or the in_segment() context manager. These methods automatically manage the lifecycle of segments and subsegments for you, making it easier to trace the execution of your code without introducing potential bugs.
- No need to call
Add subsegments
- You may use
xray_recorder.begin_subsegment()
to add a subsegment. But remember to usexray_recorder.end_subsegment()
to close it. Otherwise it doesn't work. Or alternatively, you may use
xray_recorder.in_subsegment()
context managerwith xray_recorder.in_subsegment('subsegment_mock') as subsegment_mock: dict = { "now": now.isoformat(), "results-size": len(model['data']) } subsegment_mock.put_metadata('key', dict, 'namespace')
The subsegment
subsegment_mock
is added.
- You may use
Subscribe to my newsletter
Read articles from Bin Li directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by