awslabs.cloudwatch-applicationsignals-mcp-server 0.1.21__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. awslabs/__init__.py +17 -0
  2. awslabs/cloudwatch_applicationsignals_mcp_server/__init__.py +17 -0
  3. awslabs/cloudwatch_applicationsignals_mcp_server/audit_presentation_utils.py +288 -0
  4. awslabs/cloudwatch_applicationsignals_mcp_server/audit_utils.py +912 -0
  5. awslabs/cloudwatch_applicationsignals_mcp_server/aws_clients.py +120 -0
  6. awslabs/cloudwatch_applicationsignals_mcp_server/canary_utils.py +910 -0
  7. awslabs/cloudwatch_applicationsignals_mcp_server/enablement_guides/templates/ec2/ec2-dotnet-enablement.md +435 -0
  8. awslabs/cloudwatch_applicationsignals_mcp_server/enablement_guides/templates/ec2/ec2-java-enablement.md +321 -0
  9. awslabs/cloudwatch_applicationsignals_mcp_server/enablement_guides/templates/ec2/ec2-nodejs-enablement.md +420 -0
  10. awslabs/cloudwatch_applicationsignals_mcp_server/enablement_guides/templates/ec2/ec2-python-enablement.md +598 -0
  11. awslabs/cloudwatch_applicationsignals_mcp_server/enablement_guides/templates/ecs/ecs-dotnet-enablement.md +264 -0
  12. awslabs/cloudwatch_applicationsignals_mcp_server/enablement_guides/templates/ecs/ecs-java-enablement.md +193 -0
  13. awslabs/cloudwatch_applicationsignals_mcp_server/enablement_guides/templates/ecs/ecs-nodejs-enablement.md +198 -0
  14. awslabs/cloudwatch_applicationsignals_mcp_server/enablement_guides/templates/ecs/ecs-python-enablement.md +236 -0
  15. awslabs/cloudwatch_applicationsignals_mcp_server/enablement_guides/templates/eks/eks-dotnet-enablement.md +166 -0
  16. awslabs/cloudwatch_applicationsignals_mcp_server/enablement_guides/templates/eks/eks-java-enablement.md +166 -0
  17. awslabs/cloudwatch_applicationsignals_mcp_server/enablement_guides/templates/eks/eks-nodejs-enablement.md +166 -0
  18. awslabs/cloudwatch_applicationsignals_mcp_server/enablement_guides/templates/eks/eks-python-enablement.md +169 -0
  19. awslabs/cloudwatch_applicationsignals_mcp_server/enablement_guides/templates/lambda/lambda-dotnet-enablement.md +336 -0
  20. awslabs/cloudwatch_applicationsignals_mcp_server/enablement_guides/templates/lambda/lambda-java-enablement.md +336 -0
  21. awslabs/cloudwatch_applicationsignals_mcp_server/enablement_guides/templates/lambda/lambda-nodejs-enablement.md +336 -0
  22. awslabs/cloudwatch_applicationsignals_mcp_server/enablement_guides/templates/lambda/lambda-python-enablement.md +336 -0
  23. awslabs/cloudwatch_applicationsignals_mcp_server/enablement_tools.py +147 -0
  24. awslabs/cloudwatch_applicationsignals_mcp_server/server.py +1505 -0
  25. awslabs/cloudwatch_applicationsignals_mcp_server/service_audit_utils.py +231 -0
  26. awslabs/cloudwatch_applicationsignals_mcp_server/service_tools.py +659 -0
  27. awslabs/cloudwatch_applicationsignals_mcp_server/sli_report_client.py +333 -0
  28. awslabs/cloudwatch_applicationsignals_mcp_server/slo_tools.py +386 -0
  29. awslabs/cloudwatch_applicationsignals_mcp_server/trace_tools.py +784 -0
  30. awslabs/cloudwatch_applicationsignals_mcp_server/utils.py +172 -0
  31. awslabs_cloudwatch_applicationsignals_mcp_server-0.1.21.dist-info/METADATA +808 -0
  32. awslabs_cloudwatch_applicationsignals_mcp_server-0.1.21.dist-info/RECORD +36 -0
  33. awslabs_cloudwatch_applicationsignals_mcp_server-0.1.21.dist-info/WHEEL +4 -0
  34. awslabs_cloudwatch_applicationsignals_mcp_server-0.1.21.dist-info/entry_points.txt +2 -0
  35. awslabs_cloudwatch_applicationsignals_mcp_server-0.1.21.dist-info/licenses/LICENSE +174 -0
  36. awslabs_cloudwatch_applicationsignals_mcp_server-0.1.21.dist-info/licenses/NOTICE +2 -0
@@ -0,0 +1,598 @@
1
+ # Task: Enable AWS Application Signals for Python on EC2
2
+
3
+ Your task is to modify Infrastructure as Code (IaC) files to enable AWS Application Signals for a Python application running on EC2 instances. You will update IAM permissions, install monitoring agents, and configure OpenTelemetry instrumentation through UserData scripts.
4
+
5
+ ## What You Will Accomplish
6
+
7
+ After completing this task:
8
+ - The EC2 instance will have permissions to send telemetry data to CloudWatch
9
+ - The CloudWatch Agent will be installed and configured for Application Signals
10
+ - The Python application will be automatically instrumented with AWS Distro for OpenTelemetry (ADOT)
11
+ - Traces, metrics, and performance data will appear in the CloudWatch Application Signals console
12
+ - The user will be able to see service maps, SLOs, and application performance metrics without manual code instrumentation
13
+
14
+ ## Critical Requirements
15
+
16
+ **Error Handling:**
17
+ - If you cannot determine required values from the IaC, STOP and ask the user
18
+ - For multiple EC2 instances, ask which one(s) to modify
19
+ - Preserve all existing UserData commands; add new ones in sequence
20
+
21
+ **Do NOT:**
22
+ - Run deployment commands automatically (`cdk deploy`, `terraform apply`, etc.)
23
+ - Remove existing application startup logic
24
+ - Skip the user approval step before deployment
25
+
26
+ ## IaC Tool Support
27
+
28
+ **Code examples use CDK TypeScript syntax**. If you are working with Terraform or CloudFormation, translate the CDK syntax to the appropriate format while keeping all bash commands identical. The UserData bash commands (CloudWatch Agent installation, ADOT installation, environment variables) are universal across all IaC tools - only the wrapper syntax differs.
29
+
30
+ ## Before You Start: Gather Required Information
31
+
32
+ Execute these steps to collect the information needed for configuration:
33
+
34
+ ### Step 1: Determine Deployment Type
35
+
36
+ Read the UserData script and look for the application startup command. This is typically one of the last commands in UserData.
37
+
38
+ **If you see:**
39
+ - `docker run` or `docker start` → **Docker deployment**
40
+ - `python`, `gunicorn`, `uvicorn`, `flask run`, or similar → **Non-Docker deployment**
41
+
42
+ **If unclear:**
43
+ - Ask the user: "Is your Python application running in a Docker container or directly on the EC2 instance?" DO NOT GUESS
44
+
45
+ **Critical distinction:** Where does the Python process run?
46
+ - **Docker:** Python runs inside a container → Modify Dockerfile
47
+ - **Non-Docker:** Python runs directly on EC2 → Modify UserData
48
+
49
+ ### Step 2: Extract Placeholder Values
50
+
51
+ Analyze the existing IaC to determine these values for Application Signals enablement:
52
+
53
+ - `{{SERVICE_NAME}}`:
54
+ - **Why It Matters:** Sets the service name displayed in Application Signals console via `OTEL_RESOURCE_ATTRIBUTES=service.name={{SERVICE_NAME}}`
55
+ - **How to Find It:** Use the application name, stack name, or construct ID. Look for service/app names in the IaC.
56
+ - **Example Value:** `my-python-app`
57
+ - **Required For:** Both Docker and non-Docker
58
+ - `{{ENTRY_POINT}}`
59
+ - **Why It Matters:** Used to wrap the application startup with OpenTelemetry instrumentation: `opentelemetry-instrument python {{ENTRY_POINT}}`
60
+ - **How to Find It:** Find the Python file that starts the application (look for `python` commands in UserData)
61
+ - **Example Value:** `app.py` or `main.py`
62
+ - **Required For:** non-Docker
63
+ - `{{APP_DIR}}`
64
+ - **Why It Matters:** Python needs to run from the correct directory to find application files and dependencies
65
+ - **How to Find It:** Find where the application code is deployed (look for `cd`, `git clone`, or file copy commands in UserData)
66
+ - **Example Value:** `/opt/myapp`
67
+ - **Required For:** non-Docker
68
+
69
+
70
+ For Docker-based deployments you will also need to find these additional values:
71
+
72
+ - `{{PORT}}`
73
+ - **Why It Matters:** Docker port mapping that ensures the container is accessible on the correct port
74
+ - **How to Find It:** Find port mappings in `docker run -p` commands or security group ingress rules
75
+ - **Example Value:** `5000`
76
+ - **Required For:** Docker
77
+ - `{{APP_NAME}}`
78
+ - **Why It Matters:** Used to reference the container for operations like `docker logs {{APP_NAME}}`, `docker exec`, health checks, etc.
79
+ - **How to Find It:** Find container name in `docker run --name` or use `{{SERVICE_NAME}}-container`
80
+ - **Example Value:** `python-flask-app`
81
+ - **Required For:** Docker
82
+ - `{{IMAGE_URI}}`
83
+ - **Why It Matters:** This is the identifier for the application that Docker will run
84
+ - **How to Find It:** Find the Docker image in `docker run` or `docker pull` commands
85
+ - **Example Value:** `123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest`
86
+ - **Required For:** Docker
87
+
88
+ **If you cannot determine a value:** Ask the user for clarification before proceeding. Do not guess or make up values.
89
+
90
+ ### Step 3: Identify Python Framework
91
+
92
+ Search the IaC UserData and application files for framework indicators:
93
+
94
+ - **Django:** `django`, `manage.py`, `DJANGO_SETTINGS_MODULE`, `settings.py`
95
+ - **Flask:** `flask`, `Flask(`, `@app.route`
96
+ - **FastAPI:** `fastapi`, `FastAPI(`, `uvicorn`
97
+ - **WSGI Server:** `gunicorn`, `uwsgi` in startup commands or `requirements.txt`
98
+ - **Other:** Generic Python application
99
+
100
+ **If you cannot determine a value:** Ask the user for clarification before proceeding. Do not guess or make up values.
101
+
102
+ ### Step 4: Framework-Specific Requirements
103
+
104
+ Only complete the relevant subsections based on what you identified in Step 3.
105
+
106
+ #### 4a. Django Applications
107
+
108
+ If you identified Django in Step 3, extract the Django settings module path:
109
+
110
+ - `{{DJANGO_SETTINGS_MODULE}}`: The Python module path to `settings.py`
111
+ - **How to Find:** Look for existing `DJANGO_SETTINGS_MODULE` in UserData/Dockerfile, or search for `settings.py` location
112
+ - **Common Patterns:** `myproject.settings` (if `settings.py` at `myproject/settings.py`)
113
+ - **If not found:** Ask the user for the Django settings module path
114
+
115
+ #### 4b. WSGI Server Applications (Gunicorn/uWSGI)
116
+
117
+ If you identified a WSGI server in Step 3, note that additional worker instrumentation is required:
118
+
119
+ - Gunicorn requires a `post_fork` hook in `gunicorn.conf.py`
120
+ - uWSGI requires `import` directive in `uwsgi.ini`
121
+ - Both require `OTEL_AWS_PYTHON_DEFER_TO_WORKERS_ENABLED=true` environment variable
122
+ - Implementation details are covered in the Docker/non-Docker configuration sections below
123
+
124
+ ### Step 5: Identify Instance OS
125
+
126
+ Determine the operating system to use the correct package manager and installation commands.
127
+
128
+ **Amazon Linux**
129
+ - **Amazon Linux 2:** Use `yum` package manager
130
+ - **Amazon Linux 2023:** Use `dnf` package manager
131
+ - **How to detect:** Look for existing package install commands in UserData (check for `yum` or `dnf`), or look for AMI references containing `al2` or `al2023`
132
+
133
+ **Other Linux distributions:**
134
+ - **Ubuntu/Debian:** Use `apt` package manager
135
+ - **Fedora/RHEL/CentOS:** Use `dnf` or `yum` package manager
136
+
137
+ **If unclear:** Look for AMI name/ID in the IaC or ask the user which OS the EC2 instance is running. Do not guess or make up values.
138
+
139
+ ## Instructions
140
+
141
+ Follow these steps in sequence:
142
+
143
+ ### Step 1: Locate the IaC Files
144
+
145
+ **Search for EC2 instance definitions** using these patterns:
146
+
147
+ **CDK:**
148
+
149
+ ```
150
+ new ec2.Instance(
151
+ ec2.Instance(
152
+ CfnInstance(
153
+ ```
154
+
155
+ **Terraform:**
156
+ ```
157
+ resource "aws_instance"
158
+ ```
159
+
160
+ **CloudFormation:**
161
+ ```
162
+ AWS::EC2::Instance
163
+ ```
164
+
165
+ **Read the file(s)** containing the EC2 instance definition. You need to identify:
166
+ 1. The instance resource/construct
167
+ 2. The IAM role attached to the instance
168
+ 3. The UserData script or property
169
+
170
+ ### Step 2: Locate the IAM Role
171
+
172
+ Find the IAM role attached to the EC2 instance.
173
+
174
+ **CDK:**
175
+
176
+ ```typescript
177
+ role: someRole
178
+ new iam.Role(this, 'RoleName'
179
+ ```
180
+
181
+ ### Step 3: Update the IAM Role
182
+
183
+ Add the CloudWatch Agent Server Policy to the IAM role's managed policies.
184
+
185
+ **CDK:**
186
+
187
+ ```typescript
188
+ const role = new iam.Role(this, 'AppRole', {
189
+ assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'),
190
+ managedPolicies: [
191
+ iam.ManagedPolicy.fromAwsManagedPolicyName('CloudWatchAgentServerPolicy'),
192
+ // ... keep existing policies
193
+ ],
194
+ });
195
+ ```
196
+
197
+ ### Step 4: Modify UserData - Add Prerequisites
198
+
199
+ Add a CloudWatch Agent installation command to the UserData script.
200
+
201
+ **CRITICAL for Terraform Users:** When modifying Terraform `user_data` heredocs, you MUST preserve the EXACT indentation of existing lines. Terraform's `<<-EOF` syntax strips leading whitespace, but only if indentation is consistent. When adding new bash commands:
202
+ - Count the leading spaces/tabs on existing lines in the heredoc
203
+ - Apply the SAME amount of leading whitespace to all new lines you add
204
+ - Do NOT modify the indentation of any existing lines
205
+
206
+ If indentation is inconsistent, Terraform will NOT strip the whitespace, causing the deployed script to have leading spaces before `#!/bin/bash`, which will cause cloud-init to fail.
207
+
208
+ **CDK TypeScript example:**
209
+ ```typescript
210
+ instance.userData.addCommands(
211
+ 'dnf install -y amazon-cloudwatch-agent', // Use dnf for AL2023, yum for AL2
212
+ // ... rest of UserData follows
213
+ );
214
+ ```
215
+
216
+ **Placement:** Add this command early in the UserData script:
217
+ - If system update commands exist (like `dnf update -y`, `apt-get update`), add it immediately after those
218
+ - If no system update commands exist, add it at the very beginning of UserData
219
+ - This should come before any application dependency installations or application setup commands
220
+
221
+ **For other Linux distributions:** CloudWatch Agent may not be available via the OS package manager. Refer to [AWS CloudWatch Agent installation docs](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/manual-installation.html) for distribution-specific instructions.
222
+
223
+ ### Step 5: Modify UserData - Configure CloudWatch Agent
224
+
225
+ The CloudWatch Agent was installed in Step 4. Now configure it for Application Signals:
226
+
227
+ **CDK TypeScript example:**
228
+
229
+ ```typescript
230
+ instance.userData.addCommands(
231
+ '# Create CloudWatch Agent configuration for Application Signals',
232
+ "cat > /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json << 'EOF'",
233
+ '{',
234
+ ' "traces": {',
235
+ ' "traces_collected": {',
236
+ ' "application_signals": {}',
237
+ ' }',
238
+ ' },',
239
+ ' "logs": {',
240
+ ' "metrics_collected": {',
241
+ ' "application_signals": {}',
242
+ ' }',
243
+ ' }',
244
+ '}',
245
+ 'EOF',
246
+ '',
247
+ '# Start CloudWatch Agent with Application Signals configuration',
248
+ '/opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \\',
249
+ ' -a fetch-config \\',
250
+ ' -m ec2 \\',
251
+ ' -s \\',
252
+ ' -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json',
253
+ );
254
+ ```
255
+
256
+ ### Step 6: Install ADOT Python Auto-Instrumentation SDK
257
+
258
+ Choose based on deployment type identified in "Before You Start".
259
+
260
+ #### Option A: Docker Deployment - Modify Dockerfile
261
+
262
+ For Docker deployments, modify the `Dockerfile` in the application directory.
263
+
264
+ **1. Install aws-opentelemetry-distro:**
265
+
266
+ Find the line that installs Python dependencies (usually `RUN pip install ` or `RUN pip install -r requirements.txt`). Add ADOT installation AFTER it:
267
+
268
+ ```dockerfile
269
+ # Add this line after the existing pip install command
270
+ RUN pip install --no-cache-dir aws-opentelemetry-distro
271
+ ```
272
+
273
+ **2. Wrap the CMD with opentelemetry-instrument:**
274
+
275
+ Find the `CMD` line at the end of the `Dockerfile` and wrap the command with `opentelemetry-instrument`:
276
+
277
+ ```dockerfile
278
+ # Before (Flask):
279
+ CMD ["flask", "run"]
280
+
281
+ # After:
282
+ CMD ["opentelemetry-instrument", "flask", "run"]
283
+
284
+ # Before (any Python app):
285
+ CMD ["python", "app.py"]
286
+
287
+ # After:
288
+ CMD ["opentelemetry-instrument", "python", "app.py"]
289
+ ```
290
+
291
+ **Django-specific examples:**
292
+
293
+ For Django with Gunicorn (production):
294
+ ```dockerfile
295
+ # Before:
296
+ CMD ["gunicorn", "-c", "gunicorn.conf.py", "djangoapp.wsgi:application"]
297
+
298
+ # After:
299
+ CMD ["opentelemetry-instrument", "gunicorn", "-c", "gunicorn.conf.py", "djangoapp.wsgi:application"]
300
+ ```
301
+
302
+ For Django development server, add the `--noreload` flag to prevent auto-reloader conflicts with OpenTelemetry:
303
+ ```dockerfile
304
+ # Before:
305
+ CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
306
+
307
+ # After:
308
+ CMD ["opentelemetry-instrument", "python", "manage.py", "runserver", "0.0.0.0:8000", "--noreload"]
309
+ ```
310
+
311
+ **Why modify Dockerfile, not UserData:** The ADOT package must be installed inside the container image, not on the EC2 host. UserData commands run on the host and won't affect the containerized application.
312
+
313
+ #### Option B: Non-Docker Deployment - Modify UserData
314
+
315
+ For non-Docker deployments, add to UserData AFTER CloudWatch Agent installation:
316
+
317
+ ```typescript
318
+ instance.userData.addCommands(
319
+ '# Install ADOT Python auto-instrumentation',
320
+ 'pip3 install aws-opentelemetry-distro',
321
+ );
322
+ ```
323
+
324
+ ### Step 7: Modify UserData - Configure Application (Docker Deployment)
325
+
326
+ **Only follow this step if you identified Docker deployment in "Before You Start".**
327
+
328
+ **Critical Docker configuration:** The `--network host` flag is REQUIRED for all Docker deployments. Without it, the container cannot reach the CloudWatch Agent at `localhost:4316` because `localhost` inside a container refers to the container's network namespace, not the EC2 host.
329
+
330
+ #### Step 7A: Base Framework Configuration
331
+
332
+ Choose the appropriate option based on the framework you identified in Step 3.
333
+
334
+ ##### Option 1: Standard Python (Flask, FastAPI, Other)
335
+
336
+ **Use this for Flask, FastAPI, or other Python frameworks NOT using Django.**
337
+
338
+ Find the existing `docker run` command in UserData. Replace it with:
339
+
340
+ ```typescript
341
+ instance.userData.addCommands(
342
+ '# Run container with Application Signals environment variables',
343
+ `docker run -d --name {{APP_NAME}} \\`,
344
+ ` -p {{PORT}}:{{PORT}} \\`,
345
+ ` -e PORT={{PORT}} \\`,
346
+ ` -e SERVICE_NAME={{SERVICE_NAME}} \\`,
347
+ ` -e OTEL_METRICS_EXPORTER=none \\`,
348
+ ` -e OTEL_LOGS_EXPORTER=none \\`,
349
+ ` -e OTEL_AWS_APPLICATION_SIGNALS_ENABLED=true \\`,
350
+ ` -e OTEL_PYTHON_DISTRO=aws_distro \\`,
351
+ ` -e OTEL_PYTHON_CONFIGURATOR=aws_configurator \\`,
352
+ ` -e OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf \\`,
353
+ ` -e OTEL_TRACES_SAMPLER=xray \\`,
354
+ ` -e OTEL_TRACES_SAMPLER_ARG=endpoint=http://localhost:2000 \\`,
355
+ ` -e OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT=http://localhost:4316/v1/metrics \\`,
356
+ ` -e OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4316/v1/traces \\`,
357
+ ` -e OTEL_RESOURCE_ATTRIBUTES=service.name={{SERVICE_NAME}} \\`,
358
+ ` --network host \\`,
359
+ ` {{IMAGE_URI}}`,
360
+ );
361
+ ```
362
+
363
+ ##### Option 2: Django Applications
364
+
365
+ **Use this if you identified Django in Step 3.**
366
+
367
+ Find the existing `docker run` command in UserData. Replace it with:
368
+
369
+ ```typescript
370
+ instance.userData.addCommands(
371
+ `docker run -d --name {{APP_NAME}} \\`,
372
+ ` -p {{PORT}}:{{PORT}} \\`,
373
+ ` -e PORT={{PORT}} \\`,
374
+ ` -e SERVICE_NAME={{SERVICE_NAME}} \\`,
375
+ ` -e DJANGO_SETTINGS_MODULE={{DJANGO_SETTINGS_MODULE}} \\`,
376
+ ` -e OTEL_METRICS_EXPORTER=none \\`,
377
+ ` -e OTEL_LOGS_EXPORTER=none \\`,
378
+ ` -e OTEL_AWS_APPLICATION_SIGNALS_ENABLED=true \\`,
379
+ ` -e OTEL_PYTHON_DISTRO=aws_distro \\`,
380
+ ` -e OTEL_PYTHON_CONFIGURATOR=aws_configurator \\`,
381
+ ` -e OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf \\`,
382
+ ` -e OTEL_TRACES_SAMPLER=xray \\`,
383
+ ` -e OTEL_TRACES_SAMPLER_ARG=endpoint=http://localhost:2000 \\`,
384
+ ` -e OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT=http://localhost:4316/v1/metrics \\`,
385
+ ` -e OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4316/v1/traces \\`,
386
+ ` -e OTEL_RESOURCE_ATTRIBUTES=service.name={{SERVICE_NAME}} \\`,
387
+ ` --network host \\`,
388
+ ` {{IMAGE_URI}}`,
389
+ );
390
+ ```
391
+
392
+ #### Step 7B: WSGI Additional Configuration
393
+
394
+ **Only complete this section if you identified a WSGI server (Gunicorn/uWSGI) in Step 3.**
395
+
396
+ If you are using a WSGI server, you must add additional worker instrumentation on top of the configuration from Step 7A.
397
+
398
+ **1. Ensure WSGI configuration file is in the Docker image.**
399
+
400
+ Your `Dockerfile` must include the appropriate configuration file:
401
+
402
+ For **Gunicorn** - Create `gunicorn.conf.py`:
403
+
404
+ ```python
405
+ def post_fork(server, worker):
406
+ from opentelemetry.instrumentation.auto_instrumentation import sitecustomize
407
+ ```
408
+
409
+ For **uWSGI** - Create or modify `uwsgi.ini`:
410
+ ```ini
411
+ [uwsgi]
412
+ enable-threads = true
413
+ lazy-apps = true
414
+ import = opentelemetry.instrumentation.auto_instrumentation.sitecustomize
415
+ ```
416
+
417
+ **2. Add WSGI-specific environment variable to your docker run command.**
418
+
419
+ Go back to the `docker run` command you configured in Step 7A and add this environment variable:
420
+
421
+ ```typescript
422
+ ` -e OTEL_AWS_PYTHON_DEFER_TO_WORKERS_ENABLED=true \\`,
423
+ ```
424
+
425
+ Add it right after the `OTEL_RESOURCE_ATTRIBUTES` line and before `--network host`.
426
+
427
+ **WSGI requirements:**
428
+ - `OTEL_AWS_PYTHON_DEFER_TO_WORKERS_ENABLED=true` is REQUIRED for all WSGI servers
429
+ - The `gunicorn.conf.py` or `uwsgi.ini` file with worker instrumentation is REQUIRED
430
+
431
+ ### Step 8: Modify UserData - Configure Application (Non-Docker Deployment)
432
+
433
+ **Only follow this step if you identified non-Docker deployment in "Before You Start".**
434
+
435
+ #### Step 8A: Base Framework Configuration
436
+
437
+ Choose the appropriate option based on the framework you identified in Step 3.
438
+
439
+ ##### Option 1: Standard Python (Flask, FastAPI, Other)
440
+
441
+ **Use this for Flask, FastAPI, or other Python frameworks NOT using Django.**
442
+
443
+ Find the existing command that starts the Python application. Replace it with:
444
+
445
+ ```typescript
446
+ instance.userData.addCommands(
447
+ '# Set OpenTelemetry environment variables',
448
+ 'export OTEL_METRICS_EXPORTER=none',
449
+ 'export OTEL_LOGS_EXPORTER=none',
450
+ 'export OTEL_AWS_APPLICATION_SIGNALS_ENABLED=true',
451
+ 'export OTEL_PYTHON_DISTRO=aws_distro',
452
+ 'export OTEL_PYTHON_CONFIGURATOR=aws_configurator',
453
+ 'export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf',
454
+ 'export OTEL_TRACES_SAMPLER=xray',
455
+ 'export OTEL_TRACES_SAMPLER_ARG=endpoint=http://localhost:2000',
456
+ 'export OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT=http://localhost:4316/v1/metrics',
457
+ 'export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4316/v1/traces',
458
+ 'export OTEL_RESOURCE_ATTRIBUTES=service.name={{SERVICE_NAME}}',
459
+ '',
460
+ '# Start application with ADOT instrumentation',
461
+ 'cd {{APP_DIR}}',
462
+ 'opentelemetry-instrument python {{ENTRY_POINT}}',
463
+ );
464
+ ```
465
+
466
+ ##### Option 2: Django Applications
467
+
468
+ **Use this if you identified Django in Step 3.**
469
+
470
+ Find the existing command that starts the Django application. Replace it with:
471
+
472
+ ```typescript
473
+ instance.userData.addCommands(
474
+ 'export DJANGO_SETTINGS_MODULE={{DJANGO_SETTINGS_MODULE}}',
475
+ 'export OTEL_METRICS_EXPORTER=none',
476
+ 'export OTEL_LOGS_EXPORTER=none',
477
+ 'export OTEL_AWS_APPLICATION_SIGNALS_ENABLED=true',
478
+ 'export OTEL_PYTHON_DISTRO=aws_distro',
479
+ 'export OTEL_PYTHON_CONFIGURATOR=aws_configurator',
480
+ 'export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf',
481
+ 'export OTEL_TRACES_SAMPLER=xray',
482
+ 'export OTEL_TRACES_SAMPLER_ARG=endpoint=http://localhost:2000',
483
+ 'export OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT=http://localhost:4316/v1/metrics',
484
+ 'export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4316/v1/traces',
485
+ 'export OTEL_RESOURCE_ATTRIBUTES=service.name={{SERVICE_NAME}}',
486
+ '',
487
+ '# Start Django application with ADOT instrumentation',
488
+ 'cd {{APP_DIR}}',
489
+ 'opentelemetry-instrument python manage.py runserver 0.0.0.0:{{PORT}} --noreload',
490
+ );
491
+ ```
492
+
493
+ **Django-specific notes:**
494
+ - `--noreload` flag is REQUIRED to prevent auto-reloader conflicts with OpenTelemetry
495
+
496
+ #### Step 8B: WSGI Additional Configuration
497
+
498
+ **Only complete this section if you identified a WSGI server (Gunicorn/uWSGI) in Step 3.**
499
+
500
+ If you are using a WSGI server, you must add additional worker instrumentation on top of the configuration from Step 8A.
501
+
502
+ **1. Ensure WSGI configuration file exists on the EC2 instance.**
503
+
504
+ Your application directory must include the appropriate configuration file:
505
+
506
+ For **Gunicorn** - Create `gunicorn.conf.py`:
507
+
508
+ ```python
509
+ def post_fork(server, worker):
510
+ from opentelemetry.instrumentation.auto_instrumentation import sitecustomize
511
+ ```
512
+
513
+ For **uWSGI** - Create or modify `uwsgi.ini`:
514
+ ```ini
515
+ [uwsgi]
516
+ enable-threads = true
517
+ lazy-apps = true
518
+ import = opentelemetry.instrumentation.auto_instrumentation.sitecustomize
519
+ ```
520
+
521
+ **2. Add WSGI-specific environment variable to your configuration.**
522
+
523
+ Go back to the commands you configured in Step 8A and add this environment variable:
524
+
525
+ ```typescript
526
+ 'export OTEL_AWS_PYTHON_DEFER_TO_WORKERS_ENABLED=true',
527
+ ```
528
+
529
+ Add it right after the `export OTEL_RESOURCE_ATTRIBUTES` line.
530
+
531
+ **3. Update the application startup command.**
532
+
533
+ Replace the application startup command with the WSGI server command wrapped with OpenTelemetry instrumentation.
534
+
535
+ **General examples (Flask, FastAPI, etc.):**
536
+ ```typescript
537
+ // Flask with Gunicorn
538
+ 'opentelemetry-instrument gunicorn -c gunicorn.conf.py app:app',
539
+
540
+ // Generic Python app with uWSGI
541
+ 'opentelemetry-instrument uwsgi --ini uwsgi.ini',
542
+ ```
543
+
544
+ **Django-specific examples:**
545
+
546
+ For Django with Gunicorn:
547
+ ```typescript
548
+ // The cd command is from Step 8A, this replaces the startup command
549
+ 'opentelemetry-instrument gunicorn -c gunicorn.conf.py myproject.wsgi:application',
550
+ ```
551
+
552
+ For Django with uWSGI:
553
+ ```typescript
554
+ 'opentelemetry-instrument uwsgi --ini uwsgi.ini --module myproject.wsgi:application',
555
+ ```
556
+
557
+ **WSGI requirements:**
558
+ - `OTEL_AWS_PYTHON_DEFER_TO_WORKERS_ENABLED=true` is REQUIRED for all WSGI servers
559
+ - The `gunicorn.conf.py` or `uwsgi.ini` file with worker instrumentation is REQUIRED
560
+ - The startup command must use `opentelemetry-instrument` wrapper with your WSGI server
561
+
562
+ ## Completion
563
+
564
+ **Tell the user:**
565
+
566
+ "I've completed the Application Signals enablement for your Python application. Here's what I modified:
567
+
568
+ **Files Changed:**
569
+ - IAM role: Added CloudWatchAgentServerPolicy
570
+ - UserData: Installed and configured CloudWatch Agent
571
+ - UserData: Installed ADOT Python SDK
572
+ - UserData/Service file: Added OpenTelemetry environment variables and instrumentation wrapper
573
+ - Dockerfile: Installed ADOT Python SDK and modified CMD with instrumentation wrapper (if using Docker)
574
+ - WSGI configuration: Added worker instrumentation (if using Gunicorn/uWSGI)
575
+
576
+ **Next Steps:**
577
+ 1. Ensure that [Application Signals is enabled in AWS account](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-Application-Signals-Enable.html).
578
+ 2. Review the changes I made using `git diff`
579
+ 3. Deploy your infrastructure:
580
+ - For CDK: `cdk deploy`
581
+ - For Terraform: `terraform apply`
582
+ - For CloudFormation: Deploy your stack
583
+ 4. After deployment, wait 5-10 minutes for telemetry data to start flowing
584
+
585
+ **Verification:**
586
+ Once deployed, you can verify Application Signals is working by:
587
+ - Opening the AWS CloudWatch Console
588
+ - Navigating to Application Signals → Services
589
+ - Looking for your service (named: {{SERVICE_NAME}})
590
+ - Checking that traces and metrics are being collected
591
+
592
+ **Monitor Application Health:**
593
+ After enablement, you can monitor your application's operational health using Application Signals dashboards. For more information, see [Monitor the operational health of your applications with Application Signals](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Services.html).
594
+
595
+ **Troubleshooting**
596
+ If you encounter any other issues, refer to the [CloudWatch APM troubleshooting guide](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-Application-Signals-Enable-Troubleshoot.html).
597
+
598
+ Let me know if you'd like me to make any adjustments before you deploy!"