Skip to content

Commit d65cee4

Browse files
committed
python instrument demo
1 parent 6ad6136 commit d65cee4

File tree

174 files changed

+34670
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

174 files changed

+34670
-0
lines changed

basic/README.md

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Python APM Guide
2+
You can follow our [documentation](https://docs.middleware.io/docs/apm-configuration/python/python-apm-setup) to setup APM for your Python application.
3+
4+
[![PyPI - Version](https://img.shields.io/pypi/v/middleware-apm)](https://pypi.org/project/middleware-apm/)
5+
6+
7+
| Traces | Metrics | Profiling | Logs (App/Custom) |
8+
|:--------:|:---------:|:-----------:|:-------------------:|
9+
| Yes | Yes | Yes | Yes/Yes |
10+
11+
## Prerequisites
12+
Ensure that you have the Middleware Host Agent installed to view Python demo data on your dashboard.
13+
14+
---------------------
15+
16+
## Installing the Package
17+
Run the following commands in your terminal:
18+
### Step 1: Install Middleware APM package
19+
```shell
20+
pip install middleware-apm
21+
```
22+
23+
## Your Sample Code
24+
By using all the MW's APM functionalities like: Distributed-tracing, Logs, Metrics and Profiling, your code will look like this:
25+
```python
26+
import logging
27+
28+
from middleware import MwTracker
29+
tracker=MwTracker()
30+
31+
logging.warning("Sample Warning Log.")
32+
logging.error("Sample Error Log.", extra={'tester': 'Alex'})
33+
```
34+
## Setup middleware.ini File
35+
Setup your `middleware.ini` file, based on below features that you want to observe in your project. Place the file at the root of your project.
36+
```ini
37+
# ---------------------------------------------------------------------------
38+
# This file contains settings for the Middleware Python-APM Agent.
39+
# Here are the settings that are common to all environments.
40+
# ---------------------------------------------------------------------------
41+
42+
[middleware.common]
43+
44+
# The name of your application as service-name, as it will appear in the UI to filter out your data.
45+
service_name = Python-APM-Service
46+
47+
# This Token binds the Python Agent's data and profiling data to your account.
48+
access_token = {YOUR-ACCESS-TOKEN}
49+
50+
# The service name, where Middleware Agent is running, in case of K8s.
51+
;mw_agent_service = mw-service.mw-agent-ns.svc.cluster.local
52+
53+
# Toggle to enable/disable distributed traces for your application.
54+
collect_traces = true
55+
56+
# Toggle to enable/disable the collection of metrics for your application.
57+
collect_metrics = true
58+
59+
# Toggle to enable/disable the collection of logs for your application.
60+
collect_logs = true
61+
62+
# Toggle to enable/disable the collection of profiling data for your application.
63+
collect_profiling = true
64+
65+
# ---------------------------------------------------------------------------
66+
```
67+
#### Note: You need to replace <strong>\{YOUR-ACCESS-TOKEN\}</strong> with your APM's Access Token.
68+
69+
70+
## Setup Virtual Environment
71+
```
72+
python -m venv newenv
73+
```
74+
```
75+
source newenv/bin/activate
76+
```
77+
```
78+
pip install -r requirements.txt
79+
```
80+
81+
## Run Your Application
82+
83+
### Option 1 : With Host Agent
84+
To run your application, use the following command:
85+
```shell
86+
middleware-apm run python app.py
87+
```
88+
### Option 2 : Serverless Setup
89+
```shell
90+
MW_API_KEY=********** MW_TARGET=https://*****.middleware.io:443 middleware-apm run python app.py
91+
```
92+
#### Note: If `middleware.ini` isn't in your project's root, set `MIDDLEWARE_CONFIG_FILE=./path/to/middleware.ini` along with the above run command.
93+
94+
---------------------------------
95+
96+
## Troubleshooting Demo
97+
If you face any protoc specific errors, Try setting ...
98+
```
99+
export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python
100+
```
101+
---------------------------------
102+
## Run on Docker
103+
1. Build: `docker build -t demo-python .`
104+
2. Run: `docker run demo-python`
105+
3. Debug: `docker run -it demo-python sh`

basic/app.py

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
from flask import Flask, jsonify, request
2+
import traceback
3+
import logging
4+
from functools import wraps
5+
from opentelemetry import trace, metrics
6+
from opentelemetry.trace.status import StatusCode
7+
8+
9+
from middleware import MwTracker
10+
tracker=MwTracker()
11+
12+
# Initialize Flask app
13+
app = Flask(__name__)
14+
15+
# Trace provider
16+
tracer = trace.get_tracer("custom-tracer")
17+
18+
# Metric provider
19+
meter = metrics.get_meter("custom-meter")
20+
21+
# Setup Python logging
22+
logging.basicConfig(level=logging.INFO)
23+
logger = logging.getLogger(__name__)
24+
25+
# Custom Metrics: Counter
26+
request_counter = meter.create_counter(
27+
name="requests_total",
28+
description="Total number of requests",
29+
unit="1"
30+
)
31+
32+
# Custom Traces: Tracing decorator with error handling
33+
def trace_request(func):
34+
@wraps(func) # Preserve the original function's name and docstring
35+
def wrapper(*args, **kwargs):
36+
with tracer.start_as_current_span(func.__name__) as span:
37+
span.set_attribute("custom.attribute", "example_value")
38+
39+
# Log the trace and span IDs
40+
span_context = span.get_span_context()
41+
logger.info(f"Start processing {func.__name__}: trace_id={span_context.trace_id}, span_id={span_context.span_id}")
42+
43+
try:
44+
result = func(*args, **kwargs)
45+
46+
# Only check status code for POST request handling
47+
if request.method == 'POST':
48+
if result.status_code >= 400:
49+
span.set_status(StatusCode.ERROR)
50+
logger.error(f"Error occurred in {func.__name__}: status_code={result.status_code}")
51+
52+
return result
53+
54+
except Exception as e:
55+
span.set_status(StatusCode.ERROR)
56+
span.record_exception(e)
57+
logger.error(f"Exception occurred in {func.__name__}: {str(e)}")
58+
logger.error(f"Stack Trace:\n{traceback.format_exc()}") # Log the full stack trace
59+
return jsonify({"error": "An internal error occurred"}), 500
60+
return wrapper
61+
62+
@app.route('/')
63+
@trace_request
64+
def home():
65+
request_counter.add(1, {"endpoint": "home"})
66+
logger.info("Home endpoint accessed")
67+
return jsonify({"message": "Welcome to the Flask app!"})
68+
69+
@app.route('/process', methods=['GET', 'POST'])
70+
@trace_request
71+
def process_data():
72+
if request.method == 'GET':
73+
# Render a simple HTML form for demonstration
74+
return '''
75+
<form method="POST">
76+
<label for="data">Enter some data (JSON format):</label><br>
77+
<textarea id="data" name="data" rows="4" cols="50"></textarea><br>
78+
<input type="submit" value="Submit">
79+
</form>
80+
'''
81+
82+
if request.method == 'POST':
83+
# Process JSON data submitted via the form
84+
try:
85+
data = request.json if request.is_json else request.form.get('data')
86+
request_counter.add(1, {"endpoint": "process"})
87+
logger.info(f"Processing data: {data}")
88+
89+
with tracer.start_as_current_span("data_processing") as span:
90+
span.set_attribute("request.data", str(data))
91+
# Simulate processing
92+
processed_data = {"processed": data}
93+
logger.info("Data processed successfully")
94+
95+
response = jsonify(processed_data) # Create response object
96+
97+
return response # Return the response object
98+
99+
except Exception as e:
100+
span.set_status(StatusCode.ERROR)
101+
span.record_exception(e)
102+
logger.error(f"Exception occurred in process_data: {str(e)}")
103+
logger.error(f"Stack Trace:\n{traceback.format_exc()}")
104+
return jsonify({"error": "An internal error occurred"}), 500
105+
106+
@app.route('/error')
107+
@trace_request
108+
def error():
109+
request_counter.add(1, {"endpoint": "error"})
110+
logger.warning("Error endpoint accessed, simulating an error")
111+
112+
# Simulate an exception to trigger stack trace logging
113+
raise ValueError("Simulated internal server error")
114+
115+
if __name__ == '__main__':
116+
app.run(port=5000)

basic/middleware.ini

+156
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# ---------------------------------------------------------------------------
2+
# This file contains settings for the Middleware Python-APM Agent.
3+
# some settings are commented out to use uncomment as per requirement.
4+
# Add middleware.ini to root folder or set env MIDDLEWARE_CONFIG_FILE with file path.
5+
# Note: Envs can not be used in config file middleware.ini below are shown for reference only.
6+
# ---------------------------------------------------------------------------
7+
8+
# Here are the settings that are common to all environments.
9+
[middleware.common]
10+
11+
# Desc: The name of your application as service-name, as it will appear in the UI to filter out your data.
12+
# Env: MW_SERVICE_NAME or OTEL_SERVICE_NAME
13+
# Type: String
14+
# Required (uses 'service-pid' if not declared )
15+
16+
service_name = python-apm-service-noinstrument
17+
18+
19+
20+
# Desc: This Token binds the Python Agent's data and required for profiling data and serverless usage.
21+
# Env: MW_API_KEY
22+
# Type: String (abcdefghijklmnopqrstuvwxyz)
23+
# Required
24+
25+
access_token = {replace-access-token}
26+
27+
28+
29+
# Desc: Toggle to enable/disable traces for your application.
30+
# Env: MW_APM_COLLECT_TRACES
31+
# Type: Bool
32+
# Optional (Default True)
33+
34+
collect_traces = true
35+
36+
37+
38+
# Desc:Toggle to enable/disable the collection of metrics for your application.
39+
# Env: MW_APM_COLLECT_METRICS
40+
# Type: Bool
41+
# Optional (Default False)
42+
43+
collect_metrics = true
44+
45+
46+
47+
# Desc: Toggle to enable/disable the collection of logs for your application.
48+
# Env: MW_APM_COLLECT_LOGS
49+
# Type: Bool
50+
# Optional (Default True)
51+
52+
collect_logs = true
53+
54+
55+
56+
# Desc: Toggle to enable/disable the collection of profiling data for your application.
57+
# Env: MW_APM_COLLECT_PROFILING
58+
# Type: Bool
59+
# Optional (Default False)
60+
61+
collect_profiling = true
62+
63+
64+
65+
# Desc: To capture logs based on level change logging log_level. Default is FATAL.
66+
# Env: MW_LOG_LEVEL or OTEL_LOG_LEVEL
67+
# Type: String ( DEBUG, INFO, WARNING, ERROR, CRITICAL, FATAL)
68+
# Optional (Default INFO)
69+
70+
; log_level= ERROR
71+
72+
73+
74+
# --------------For Host based only(with agent)---------------------
75+
# Desc: Need to set with agent for K8s or docker based application.
76+
# Should not be used for serveless. Either use "mw_target" or "mw_agent_service".
77+
# Env: MW_AGENT_SERVICE
78+
# Type: String
79+
# Optional (Default localhost)
80+
81+
# Sample for K8s application
82+
83+
; mw_agent_service = mw-service.mw-agent-ns.svc.cluster.local
84+
85+
# Sample for Docker Container, it is IP address of the gateway.
86+
87+
; mw_agent_service = 172.17.0.1
88+
89+
90+
91+
# ------------- For Serverless only(without agent)-----------------------
92+
# Desc: Set target if instrumentation is required without installing agent.(change "myuid" with project id found in app site).
93+
# Env: MW_TARGET or OTEL_EXPORTER_OTLP_ENDPOINT
94+
# Type: String
95+
# Optional
96+
97+
; target = https://myuid.middleware.io:443
98+
99+
100+
101+
# Desc: To set custom resource attributes for traces, metrics and logs.
102+
# Env: MW_CUSTOM_RESOURCE_ATTRIBUTES
103+
# Type: String(key1=value1,key2=value2)
104+
# Optional
105+
# Sample to add key1 with value1 and so on.
106+
107+
; custom_resource_attributes = 'key1=value1,key2=value2'
108+
109+
110+
111+
# Desc: To enable and change Context Propagators, default B3 is used.
112+
# Env: MW_PROPAGATORS or OTEL_PROPAGATORS
113+
# Type: String
114+
# Optional
115+
116+
; otel_propagators = b3
117+
118+
119+
# ------------- For Debugging & Troubleshooting-----------------------
120+
# Desc: Disable Information for APM. By default is false.
121+
# Env: MW_DISABLE_INFO
122+
# Type: Bool
123+
# Optional (Default false)
124+
125+
; disable_info = true
126+
127+
128+
129+
# ------------- For Debugging & Troubleshooting-----------------------
130+
# Desc: Enable exporting traces, metrics, and logs
131+
# in console.
132+
# Env: MW_CONSOLE_EXPORTER
133+
# Type: Bool
134+
# Optional (Default false)
135+
136+
; console_exporter = true
137+
138+
139+
140+
# ------------- For Debugging & Troubleshooting-----------------------
141+
# Desc: To Save console exported metrics, traces, and logs to separate files.
142+
# work only if console_exporter is enabled.
143+
# Env: MW_DEBUG_LOG_FILE
144+
# Type: Bool
145+
# Optional (Default false)
146+
147+
; debug_log_file = true
148+
149+
150+
151+
# Desc: To give a project name to the service.
152+
# Env: MW_PROJECT_NAME
153+
# Type: String
154+
# Optional
155+
156+
; project_name = my-python-project

basic/requirements.txt

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
middleware-apm==1.2.1
2+
flask==2.3.2

django/.gitignore

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
*~
2+
*.egg-info
3+
*.EGG-INFO
4+
*.log
5+
*.pot
6+
*.pyc
7+
*.pyo
8+
build/
9+
dist/
10+
helloworld.db
11+
local_settings.py

0 commit comments

Comments
 (0)