zking2000 / NotePad

1 stars 0 forks source link

api e2e call tracing #40

Closed zking2000 closed 3 months ago

zking2000 commented 3 months ago
# 文件结构

e-commerce-app/ ├── frontend/ │ ├── app.py │ ├── templates/ │ │ ├── index.html │ │ ├── products.html │ │ └── orders.html │ ├── static/ │ │ └── styles.css │ ├── requirements.txt │ └── Dockerfile ├── backend/ │ ├── main.py │ ├── models.py │ ├── database.py │ ├── requirements.txt │ └── Dockerfile └── docker-compose.yml


# frontend/app.py
```python
from flask import Flask, render_template, request, redirect, url_for
import requests
from appdynamics.agent import api as appd

app = Flask(__name__)

# AppDynamics 配置
appd.init({
    'app_name': 'E-commerce Application',
    'tier_name': 'Frontend',
    'node_name': 'frontend-node-1',
    'controller_host': 'your-appdynamics-controller.com',
    'controller_port': 8090,
    'controller_ssl': True,
    'account_name': 'your-account-name',
    'account_access_key': 'your-access-key',
})

BACKEND_URL = "http://backend:8000"

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/products')
def products():
    bt = appd.start_bt("Get Products", "Frontend")
    try:
        response = requests.get(f"{BACKEND_URL}/products")
        products = response.json()
        return render_template('products.html', products=products)
    finally:
        appd.end_bt(bt)

@app.route('/orders', methods=['GET', 'POST'])
def orders():
    if request.method == 'POST':
        bt = appd.start_bt("Create Order", "Frontend")
        try:
            order_data = {
                "product_id": request.form['product_id'],
                "quantity": int(request.form['quantity'])
            }
            response = requests.post(f"{BACKEND_URL}/orders", json=order_data)
            return redirect(url_for('orders'))
        finally:
            appd.end_bt(bt)
    else:
        bt = appd.start_bt("Get Orders", "Frontend")
        try:
            response = requests.get(f"{BACKEND_URL}/orders")
            orders = response.json()
            return render_template('orders.html', orders=orders)
        finally:
            appd.end_bt(bt)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

frontend/templates/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>E-commerce App</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body>
    <h1>Welcome to Our E-commerce Store</h1>
    <nav>
        <a href="{{ url_for('products') }}">Products</a>
        <a href="{{ url_for('orders') }}">Orders</a>
    </nav>
</body>
</html>

frontend/templates/products.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Products</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body>
    <h1>Our Products</h1>
    <ul>
    {% for product in products %}
        <li>
            {{ product.name }} - ${{ product.price }}
            <form action="{{ url_for('orders') }}" method="post">
                <input type="hidden" name="product_id" value="{{ product.id }}">
                <input type="number" name="quantity" value="1" min="1">
                <button type="submit">Order</button>
            </form>
        </li>
    {% endfor %}
    </ul>
    <a href="{{ url_for('index') }}">Back to Home</a>
</body>
</html>

frontend/templates/orders.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Orders</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body>
    <h1>Your Orders</h1>
    <ul>
    {% for order in orders %}
        <li>Order ID: {{ order.id }} - Product ID: {{ order.product_id }} - Quantity: {{ order.quantity }}</li>
    {% endfor %}
    </ul>
    <a href="{{ url_for('index') }}">Back to Home</a>
</body>
</html>

backend/main.py

from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from . import models, database
from appdynamics.agent import api as appd
from pydantic import BaseModel

app = FastAPI()

# AppDynamics 配置
appd.init({
    'app_name': 'E-commerce Application',
    'tier_name': 'Backend',
    'node_name': 'backend-node-1',
    'controller_host': 'your-appdynamics-controller.com',
    'controller_port': 8090,
    'controller_ssl': True,
    'account_name': 'your-account-name',
    'account_access_key': 'your-access-key',
})

# 依赖项
def get_db():
    db = database.SessionLocal()
    try:
        yield db
    finally:
        db.close()

class ProductCreate(BaseModel):
    name: str
    price: float

class OrderCreate(BaseModel):
    product_id: int
    quantity: int

@app.post("/products")
def create_product(product: ProductCreate, db: Session = Depends(get_db)):
    bt = appd.start_bt("Create Product", "Backend")
    try:
        db_product = models.Product(name=product.name, price=product.price)
        db.add(db_product)
        db.commit()
        db.refresh(db_product)
        return db_product
    finally:
        appd.end_bt(bt)

@app.get("/products")
def read_products(db: Session = Depends(get_db)):
    bt = appd.start_bt("Get Products", "Backend")
    try:
        return db.query(models.Product).all()
    finally:
        appd.end_bt(bt)

@app.post("/orders")
def create_order(order: OrderCreate, db: Session = Depends(get_db)):
    bt = appd.start_bt("Create Order", "Backend")
    try:
        db_order = models.Order(product_id=order.product_id, quantity=order.quantity)
        db.add(db_order)
        db.commit()
        db.refresh(db_order)
        return db_order
    finally:
        appd.end_bt(bt)

@app.get("/orders")
def read_orders(db: Session = Depends(get_db)):
    bt = appd.start_bt("Get Orders", "Backend")
    try:
        return db.query(models.Order).all()
    finally:
        appd.end_bt(bt)

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

backend/models.py

from sqlalchemy import Column, Integer, String, Float
from .database import Base

class Product(Base):
    __tablename__ = "products"

    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)
    price = Column(Float)

class Order(Base):
    __tablename__ = "orders"

    id = Column(Integer, primary_key=True, index=True)
    product_id = Column(Integer)
    quantity = Column(Integer)

backend/database.py

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

SQLALCHEMY_DATABASE_URL = "postgresql://user:password@db/dbname"

engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()

frontend/requirements.txt

Flask
requests
appdynamics

backend/requirements.txt

fastapi
uvicorn
sqlalchemy
psycopg2-binary
appdynamics

frontend/Dockerfile

FROM python:3.9

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD ["python", "app.py"]

backend/Dockerfile

FROM python:3.9

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

docker-compose.yml

version: '3'

services:
  frontend:
    build: ./frontend
    ports:
      - "5000:5000"
    depends_on:
      - backend
    environment:
      - BACKEND_URL=http://backend:8000

  backend:
    build: ./backend
    depends_on:
      - db
    environment:
      - DATABASE_URL=postgresql://user:password@db/dbname

  db:
    image: postgres:13
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=dbname
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:
zking2000 commented 3 months ago
/* frontend/static/styles.css */

body {
    font-family: Arial, sans-serif;
    line-height: 1.6;
    color: #333;
    max-width: 800px;
    margin: 0 auto;
    padding: 20px;
    background-color: #f4f4f4;
}

h1 {
    color: #2c3e50;
    text-align: center;
    margin-bottom: 30px;
}

nav {
    background-color: #3498db;
    padding: 10px;
    margin-bottom: 20px;
}

nav a {
    color: white;
    text-decoration: none;
    padding: 5px 10px;
    margin-right: 10px;
}

nav a:hover {
    background-color: #2980b9;
}

ul {
    list-style-type: none;
    padding: 0;
}

li {
    background-color: white;
    margin-bottom: 10px;
    padding: 15px;
    border-radius: 5px;
    box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}

form {
    display: inline-block;
    margin-left: 10px;
}

input[type="number"] {
    width: 50px;
    padding: 5px;
}

button {
    background-color: #2ecc71;
    color: white;
    border: none;
    padding: 5px 10px;
    cursor: pointer;
    border-radius: 3px;
}

button:hover {
    background-color: #27ae60;
}

a {
    color: #3498db;
    text-decoration: none;
}

a:hover {
    text-decoration: underline;
}
zking2000 commented 3 months ago

文件结构调整

backend/
│
├── app/
│   ├── __init__.py
│   ├── main.py
│   ├── models.py
│   └── database.py
│
├── requirements.txt
└── Dockerfile

app/init.py

# 这个文件可以为空,它使 app 成为一个包

app/main.py

from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from app import models, database
from appdynamics.agent import api as appd
from pydantic import BaseModel

app = FastAPI()

# AppDynamics 配置
appd.init({
    'app_name': 'E-commerce Application',
    'tier_name': 'Backend',
    'node_name': 'backend-node-1',
    'controller_host': 'your-appdynamics-controller.com',
    'controller_port': 8090,
    'controller_ssl': True,
    'account_name': 'your-account-name',
    'account_access_key': 'your-access-key',
})

# 初始化数据库
models.Base.metadata.create_all(bind=database.engine)

# 依赖项
def get_db():
    db = database.SessionLocal()
    try:
        yield db
    finally:
        db.close()

class ProductCreate(BaseModel):
    name: str
    price: float

class OrderCreate(BaseModel):
    product_id: int
    quantity: int

@app.post("/products")
def create_product(product: ProductCreate, db: Session = Depends(get_db)):
    bt = appd.start_bt("Create Product", "Backend")
    try:
        db_product = models.Product(name=product.name, price=product.price)
        db.add(db_product)
        db.commit()
        db.refresh(db_product)
        return db_product
    finally:
        appd.end_bt(bt)

@app.get("/products")
def read_products(db: Session = Depends(get_db)):
    bt = appd.start_bt("Get Products", "Backend")
    try:
        return db.query(models.Product).all()
    finally:
        appd.end_bt(bt)

@app.post("/orders")
def create_order(order: OrderCreate, db: Session = Depends(get_db)):
    bt = appd.start_bt("Create Order", "Backend")
    try:
        db_order = models.Order(product_id=order.product_id, quantity=order.quantity)
        db.add(db_order)
        db.commit()
        db.refresh(db_order)
        return db_order
    finally:
        appd.end_bt(bt)

@app.get("/orders")
def read_orders(db: Session = Depends(get_db)):
    bt = appd.start_bt("Get Orders", "Backend")
    try:
        return db.query(models.Order).all()
    finally:
        appd.end_bt(bt)

if __name__ == "__main__":
    import uvicorn
    uvicorn.run("app.main:app", host="0.0.0.0", port=8000, reload=True)

app/models.py

from sqlalchemy import Column, Integer, String, Float
from app.database import Base

class Product(Base):
    __tablename__ = "products"

    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)
    price = Column(Float)

class Order(Base):
    __tablename__ = "orders"

    id = Column(Integer, primary_key=True, index=True)
    product_id = Column(Integer)
    quantity = Column(Integer)

app/database.py

from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

SQLALCHEMY_DATABASE_URL = "postgresql://user:password@db/dbname"

engine = create_engine(SQLALCHEMY_DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()

Dockerfile 调整

FROM python:3.9

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
zking2000 commented 3 months ago

我理解您的需求了。您希望保持现有的 Python Flask 应用程序,但需要一个与之兼容的 AppDynamics 客户端。这是一个很好的选择,因为我们可以使用 AppDynamics 的 Python SDK 来监控您的 Flask 应用程序。

让我们更新代码以集成 AppDynamics 的 Python SDK:

import os
from flask import Flask, jsonify, request, render_template
from flask_sqlalchemy import SQLAlchemy
from appdynamics.agent import api as appd

# AppDynamics Configuration
appd_config = {
    "APP_NAME": "YOUR_APP_NAME",
    "TIER_NAME": "YOUR_TIER_NAME",
    "NODE_NAME": "YOUR_NODE_NAME",
    "CONTROLLER_HOST": "your-controller-host.saas.appdynamics.com",
    "CONTROLLER_PORT": 443,
    "CONTROLLER_SSL": True,
    "ACCOUNT_NAME": "YOUR_ACCOUNT_NAME",
    "ACCESS_KEY": "YOUR_ACCESS_KEY",
    "HTTP_PROXY_HOST": "your-proxy-host",
    "HTTP_PROXY_PORT": "YOUR_PROXY_PORT"
}

# Initialize AppDynamics
appd.init(
    appname=appd_config["APP_NAME"],
    tiername=appd_config["TIER_NAME"],
    nodename=appd_config["NODE_NAME"]
)

# Set AppDynamics controller information
appd.set_controller_host(appd_config["CONTROLLER_HOST"])
appd.set_controller_port(appd_config["CONTROLLER_PORT"])
appd.set_controller_use_ssl(appd_config["CONTROLLER_SSL"])
appd.set_controller_account(appd_config["ACCOUNT_NAME"])
appd.set_controller_access_key(appd_config["ACCESS_KEY"])

# Set proxy if needed
if appd_config["HTTP_PROXY_HOST"] and appd_config["HTTP_PROXY_PORT"]:
    appd.set_proxy_properties(appd_config["HTTP_PROXY_HOST"], appd_config["HTTP_PROXY_PORT"])

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///example.db'
db = SQLAlchemy(app)

class Item(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)
    description = db.Column(db.String(200))

@app.route('/')
@appd.bt
def index():
    return render_template('index.html')

@app.route('/api/items', methods=['GET', 'POST'])
@appd.bt
def handle_items():
    if request.method == 'POST':
        data = request.json
        new_item = Item(name=data['name'], description=data.get('description', ''))
        db.session.add(new_item)
        db.session.commit()
        return jsonify({"message": "Item created successfully"}), 201
    else:
        items = Item.query.all()
        return jsonify([{"id": item.id, "name": item.name, "description": item.description} for item in items])

if __name__ == '__main__':
    with app.app_context():
        db.create_all()
    appd.start()  # Start AppDynamics monitoring
    app.run(debug=True)

这个更新后的代码包含以下主要变化:

  1. 导入 AppDynamics Python SDK:from appdynamics.agent import api as appd

  2. 创建一个 appd_config 字典来存储 AppDynamics 配置。这些值应该替换为您的实际配置。

  3. 使用 appd.init() 初始化 AppDynamics。

  4. 设置 AppDynamics 控制器信息和代理设置(如果需要)。

  5. 在每个路由函数上使用 @appd.bt 装饰器来创建业务事务。

  6. 在应用程序启动时调用 appd.start() 来开始 AppDynamics 监控。

要使用这段代码,您需要:

  1. 安装 AppDynamics Python SDK:pip install appdynamics

  2. 用您的实际 AppDynamics 配置替换 appd_config 中的占位符值。

  3. 确保您的 AppDynamics 账户已经配置好,可以接收来自 Python 应用程序的数据。

这个设置将允许 AppDynamics 监控您的 Flask 应用程序,跟踪请求,测量性能,并提供可见性到您的应用程序运行状况。

如果您需要进一步的自定义,比如添加自定义指标或更详细的事务跟踪,我们可以进一步修改代码。您有任何其他需求或问题吗?​​​​​​​​​​​​​​​​

zking2000 commented 3 months ago

非常感谢您提供这段代码。我理解您希望我基于这个 FastAPI 应用程序进行修改。这是一个很好的起点,因为它已经集成了 AppDynamics 并使用了异步编程。让我们对代码进行一些优化和扩展。

from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import JSONResponse
import httpx
from appdynamics.agent import api as appd
import logging
from typing import Dict

app = FastAPI()

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# AppDynamics 配置
appd_config = {
    'app_name': 'E-commerce Application',
    'tier_name': 'Frontend Service',
    'node_name': 'frontend-node-1',
    'controller_host': 'your-appdynamics-controller.com',
    'controller_port': 8090,
    'controller_ssl': True,
    'account_name': 'your-account-name',
    'account_access_key': 'your-access-key',
}

appd.init(appd_config)

PRODUCT_SERVICE_URL = "http://product-service:8000"
ORDER_SERVICE_URL = "http://order-service:8000"

@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    bt = appd.start_bt(request.url.path, "Frontend")
    try:
        response = await call_next(request)
        return response
    finally:
        appd.end_bt(bt)

async def make_service_call(url: str, method: str, data: Dict = None) -> Dict:
    async with httpx.AsyncClient() as client:
        if method.lower() == 'get':
            response = await client.get(url)
        elif method.lower() == 'post':
            response = await client.post(url, json=data)
        else:
            raise ValueError(f"Unsupported HTTP method: {method}")

        response.raise_for_status()
        return response.json()

@app.get("/products")
async def get_products():
    logger.info("Fetching products")
    try:
        return await make_service_call(f"{PRODUCT_SERVICE_URL}/products", 'get')
    except httpx.HTTPError as e:
        logger.error(f"Error fetching products: {str(e)}")
        raise HTTPException(status_code=500, detail="Error fetching products")

@app.post("/orders")
async def create_order(order: Dict):
    logger.info("Creating new order")
    try:
        return await make_service_call(f"{ORDER_SERVICE_URL}/orders", 'post', order)
    except httpx.HTTPError as e:
        logger.error(f"Error creating order: {str(e)}")
        raise HTTPException(status_code=500, detail="Error creating order")

@app.exception_handler(HTTPException)
async def http_exception_handler(request: Request, exc: HTTPException):
    appd.add_bt_error(str(exc))
    return JSONResponse(
        status_code=exc.status_code,
        content={"message": exc.detail},
    )

if __name__ == "__main__":
    import uvicorn
    logger.info("Starting the application")
    uvicorn.run(app, host="0.0.0.0", port=8000)

这个更新后的代码包含以下主要改进:

  1. 添加了日志记录,使用 Python 的 logging 模块来记录重要的事件和错误。

  2. 将 AppDynamics 配置移到了一个单独的字典中,使其更易于管理和更新。

  3. 添加了一个中间件来自动为所有请求启动和结束 AppDynamics 业务事务(BT)。这样,我们不需要在每个路由函数中手动启动和结束 BT。

  4. 创建了一个通用的 make_service_call 函数来处理 HTTP 请求,减少代码重复。

  5. 改进了错误处理,添加了一个全局异常处理器来记录 HTTP 异常并将它们添加到 AppDynamics BT 错误中。

  6. 使用类型提示来提高代码的可读性和可维护性。

  7. 在主函数中添加了启动日志。

使用这段代码时,您需要:

  1. 确保已安装所有必要的依赖:fastapi, httpx, uvicorn, 和 appdynamics

  2. 用您的实际 AppDynamics 配置替换 appd_config 中的占位符值。

  3. 根据您的实际服务 URL 更新 PRODUCT_SERVICE_URLORDER_SERVICE_URL

这个设置将允许 AppDynamics 监控您的 FastAPI 应用程序,跟踪所有 HTTP 请求,测量性能,并提供应用程序运行状况的可见性。

如果您需要进一步的自定义,比如添加更多路由、自定义指标,或者对特定端点进行更详细的监控,我们可以进一步修改代码。您对这个实现有什么想法或其他需求吗?​​​​​​​​​​​​​​​​

zking2000 commented 3 months ago
from fastapi import FastAPI, HTTPException
import httpx
from appdynamics.agent import api as appd
import os

app = FastAPI()

# AppDynamics 配置
appd.init({
    'app_name': os.environ.get('APPDYNAMICS_AGENT_APPLICATION_NAME', 'default_app_name'),
    'tier_name': os.environ.get('APPDYNAMICS_AGENT_TIER_NAME', 'default_tier_name'),
    'node_name': os.environ.get('APPDYNAMICS_AGENT_NODE_NAME', 'default_node_name'),
    'controller_host': os.environ.get('APPDYNAMICS_CONTROLLER_HOST_NAME', 'your_controller_host'),
    'controller_port': int(os.environ.get('APPDYNAMICS_CONTROLLER_PORT', '443')),
    'controller_ssl': os.environ.get('APPDYNAMICS_CONTROLLER_SSL_ENABLED', 'true').lower() == 'true',
    'account_name': os.environ.get('APPDYNAMICS_AGENT_ACCOUNT_NAME', 'your_account_name'),
    'account_access_key': os.environ.get('APPDYNAMICS_AGENT_ACCOUNT_ACCESS_KEY', 'your_access_key'),
    'http_proxy_host': os.environ.get('APPDYNAMICS_HTTP_PROXY_HOST', 'your_proxy_host'),
    'http_proxy_port': int(os.environ.get('APPDYNAMICS_HTTP_PROXY_PORT', '0')),
})

PRODUCT_SERVICE_URL = "http://product-service:8000"
ORDER_SERVICE_URL = "http://order-service:8000"

@app.get("/products")
async def get_products():
    bt = appd.start_bt("Get Products", "Frontend")
    try:
        async with httpx.AsyncClient() as client:
            response = await client.get(f"{PRODUCT_SERVICE_URL}/products")
            response.raise_for_status()
            return response.json()
    except httpx.HTTPError as e:
        raise HTTPException(status_code=500, detail=str(e))
    finally:
        appd.end_bt(bt)

@app.post("/orders")
async def create_order(order: dict):
    bt = appd.start_bt("Create Order", "Frontend")
    try:
        async with httpx.AsyncClient() as client:
            response = await client.post(f"{ORDER_SERVICE_URL}/orders", json=order)
            response.raise_for_status()
            return response.json()
    except httpx.HTTPError as e:
        raise HTTPException(status_code=500, detail=str(e))
    finally:
        appd.end_bt(bt)

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)
zking2000 commented 3 months ago
from flask import Flask, render_template, request, redirect, url_for
import requests
from appdynamics.agent import api as appd
import os

app = Flask(__name__)

# AppDynamics 配置
appd.init({
    'app_name': os.environ.get('APPDYNAMICS_AGENT_APPLICATION_NAME', 'default_app_name'),
    'tier_name': os.environ.get('APPDYNAMICS_AGENT_TIER_NAME', 'default_tier_name'),
    'node_name': os.environ.get('APPDYNAMICS_AGENT_NODE_NAME', 'default_node_name'),
    'controller_host': os.environ.get('APPDYNAMICS_CONTROLLER_HOST_NAME', 'your_controller_host'),
    'controller_port': int(os.environ.get('APPDYNAMICS_CONTROLLER_PORT', '443')),
    'controller_ssl': os.environ.get('APPDYNAMICS_CONTROLLER_SSL_ENABLED', 'true').lower() == 'true',
    'account_name': os.environ.get('APPDYNAMICS_AGENT_ACCOUNT_NAME', 'your_account_name'),
    'account_access_key': os.environ.get('APPDYNAMICS_AGENT_ACCOUNT_ACCESS_KEY', 'your_access_key'),
    'http_proxy_host': os.environ.get('APPDYNAMICS_HTTP_PROXY_HOST', 'your_proxy_host'),
    'http_proxy_port': int(os.environ.get('APPDYNAMICS_HTTP_PROXY_PORT', '0')),
})

BACKEND_URL = os.environ.get('BACKEND_URL', "http://backend:8000")

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/products')
def products():
    bt = appd.start_bt("Get Products", "Frontend")
    try:
        response = requests.get(f"{BACKEND_URL}/products")
        products = response.json()
        return render_template('products.html', products=products)
    finally:
        appd.end_bt(bt)

@app.route('/orders', methods=['GET', 'POST'])
def orders():
    if request.method == 'POST':
        bt = appd.start_bt("Create Order", "Frontend")
        try:
            order_data = {
                "product_id": request.form['product_id'],
                "quantity": int(request.form['quantity'])
            }
            response = requests.post(f"{BACKEND_URL}/orders", json=order_data)
            return redirect(url_for('orders'))
        finally:
            appd.end_bt(bt)
    else:
        bt = appd.start_bt("Get Orders", "Frontend")
        try:
            response = requests.get(f"{BACKEND_URL}/orders")
            orders = response.json()
            return render_template('orders.html', orders=orders)
        finally:
            appd.end_bt(bt)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 5000)))