diff --git a/Dockerfile b/Dockerfile
index 9ddbeb9..282f994 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -16,4 +16,4 @@ RUN apt-get update && \
COPY ./lib /app/lib
-CMD ["gunicorn", "-w 1", "--threads=2", "-k", "uvicorn.workers.UvicornWorker", "lib:app", "--log-level", "Debug", "-b", "0.0.0.0:3000", "--timeout", "120"]
+CMD ["gunicorn", "-c", "lib/settings/gunicorn.py", "-w", "1", "--threads=2", "-k", "uvicorn.workers.UvicornWorker", "lib:app", "--log-level", "Debug", "-b", "0.0.0.0:3000", "--timeout", "30"]
diff --git a/lib/__init__.py b/lib/__init__.py
index 5213401..db869db 100644
--- a/lib/__init__.py
+++ b/lib/__init__.py
@@ -25,4 +25,4 @@ def parse_error(error):
return f"{exc_type} exception: {exc_obj}"
-from .api import app # noqa
+from .api import app # noqa
diff --git a/lib/api.py b/lib/api.py
index 332e575..e91e166 100644
--- a/lib/api.py
+++ b/lib/api.py
@@ -35,7 +35,7 @@ def custom_openapi():
return app.openapi_schema
openapi_schema = get_openapi(
title="RocketPy Infinity-API",
- version="1.1.0 BETA",
+ version="1.2.0 BETA",
description=(
"
RocketPy Infinity-API is a RESTful Open API for RocketPy, a rocket flight simulator.
"
"
"
diff --git a/lib/routes/environment.py b/lib/routes/environment.py
index 6647073..2417022 100644
--- a/lib/routes/environment.py
+++ b/lib/routes/environment.py
@@ -3,6 +3,7 @@
"""
from fastapi import APIRouter
+from opentelemetry import trace
from lib.views.environment import (
EnvSummary,
@@ -24,6 +25,8 @@
},
)
+tracer = trace.get_tracer(__name__)
+
@router.post("/")
async def create_env(env: Env) -> EnvCreated:
@@ -33,7 +36,8 @@ async def create_env(env: Env) -> EnvCreated:
## Args
``` models.Env JSON ```
"""
- return await EnvController(env).create_env()
+ with tracer.start_as_current_span("create_env"):
+ return await EnvController(env).create_env()
@router.get("/{env_id}")
@@ -44,7 +48,8 @@ async def read_env(env_id: str) -> Env:
## Args
``` env_id: str ```
"""
- return await EnvController.get_env_by_id(env_id)
+ with tracer.start_as_current_span("read_env"):
+ return await EnvController.get_env_by_id(env_id)
@router.put("/{env_id}")
@@ -58,7 +63,8 @@ async def update_env(env_id: str, env: Env) -> EnvUpdated:
env: models.Env JSON
```
"""
- return await EnvController(env).update_env_by_id(env_id)
+ with tracer.start_as_current_span("update_env"):
+ return await EnvController(env).update_env_by_id(env_id)
@router.delete("/{env_id}")
@@ -69,7 +75,8 @@ async def delete_env(env_id: str) -> EnvDeleted:
## Args
``` env_id: Environment ID hash ```
"""
- return await EnvController.delete_env_by_id(env_id)
+ with tracer.start_as_current_span("delete_env"):
+ return await EnvController.delete_env_by_id(env_id)
@router.get("/rocketpy/{env_id}")
@@ -80,7 +87,8 @@ async def read_rocketpy_env(env_id: str) -> EnvPickle:
## Args
``` env_id: str ```
"""
- return await EnvController.get_rocketpy_env_as_jsonpickle(env_id)
+ with tracer.start_as_current_span("read_rocketpy_env"):
+ return await EnvController.get_rocketpy_env_as_jsonpickle(env_id)
@router.get("/{env_id}/simulate")
@@ -91,4 +99,5 @@ async def simulate_env(env_id: str) -> EnvSummary:
## Args
``` env_id: str ```
"""
- return await EnvController.simulate_env(env_id)
+ with tracer.start_as_current_span("simulate_env"):
+ return await EnvController.simulate_env(env_id)
diff --git a/lib/routes/flight.py b/lib/routes/flight.py
index 863862b..2d597c6 100644
--- a/lib/routes/flight.py
+++ b/lib/routes/flight.py
@@ -3,6 +3,7 @@
"""
from fastapi import APIRouter
+from opentelemetry import trace
from lib.views.flight import (
FlightSummary,
@@ -27,6 +28,8 @@
},
)
+tracer = trace.get_tracer(__name__)
+
@router.post("/")
async def create_flight(
@@ -38,9 +41,10 @@ async def create_flight(
## Args
``` Flight object as JSON ```
"""
- return await FlightController(
- flight, rocket_option=rocket_option, motor_kind=motor_kind
- ).create_flight()
+ with tracer.start_as_current_span("create_flight"):
+ return await FlightController(
+ flight, rocket_option=rocket_option, motor_kind=motor_kind
+ ).create_flight()
@router.get("/{flight_id}")
@@ -51,7 +55,8 @@ async def read_flight(flight_id: str) -> Flight:
## Args
``` flight_id: Flight ID hash ```
"""
- return await FlightController.get_flight_by_id(flight_id)
+ with tracer.start_as_current_span("read_flight"):
+ return await FlightController.get_flight_by_id(flight_id)
@router.get("/rocketpy/{flight_id}")
@@ -62,7 +67,10 @@ async def read_rocketpy_flight(flight_id: str) -> FlightPickle:
## Args
``` flight_id: Flight ID hash. ```
"""
- return await FlightController.get_rocketpy_flight_as_jsonpickle(flight_id)
+ with tracer.start_as_current_span("read_rocketpy_flight"):
+ return await FlightController.get_rocketpy_flight_as_jsonpickle(
+ flight_id
+ )
@router.put("/{flight_id}/env")
@@ -76,7 +84,10 @@ async def update_flight_env(flight_id: str, env: Env) -> FlightUpdated:
env: env object as JSON
```
"""
- return await FlightController.update_env_by_flight_id(flight_id, env=env)
+ with tracer.start_as_current_span("update_flight_env"):
+ return await FlightController.update_env_by_flight_id(
+ flight_id, env=env
+ )
@router.put("/{flight_id}/rocket")
@@ -95,12 +106,13 @@ async def update_flight_rocket(
rocket: Rocket object as JSON
```
"""
- return await FlightController.update_rocket_by_flight_id(
- flight_id,
- rocket=rocket,
- rocket_option=rocket_option,
- motor_kind=motor_kind,
- )
+ with tracer.start_as_current_span("update_flight_rocket"):
+ return await FlightController.update_rocket_by_flight_id(
+ flight_id,
+ rocket=rocket,
+ rocket_option=rocket_option,
+ motor_kind=motor_kind,
+ )
@router.put("/{flight_id}")
@@ -119,9 +131,10 @@ async def update_flight(
flight: Flight object as JSON
```
"""
- return await FlightController(
- flight, rocket_option=rocket_option, motor_kind=motor_kind
- ).update_flight_by_id(flight_id)
+ with tracer.start_as_current_span("update_flight"):
+ return await FlightController(
+ flight, rocket_option=rocket_option, motor_kind=motor_kind
+ ).update_flight_by_id(flight_id)
@router.delete("/{flight_id}")
@@ -132,7 +145,8 @@ async def delete_flight(flight_id: str) -> FlightDeleted:
## Args
``` flight_id: Flight ID hash ```
"""
- return await FlightController.delete_flight_by_id(flight_id)
+ with tracer.start_as_current_span("delete_flight"):
+ return await FlightController.delete_flight_by_id(flight_id)
@router.get("/{flight_id}/simulate")
@@ -143,4 +157,5 @@ async def simulate_flight(flight_id: str) -> FlightSummary:
## Args
``` flight_id: Flight ID hash ```
"""
- return await FlightController.simulate_flight(flight_id)
+ with tracer.start_as_current_span("simulate_flight"):
+ return await FlightController.simulate_flight(flight_id)
diff --git a/lib/routes/motor.py b/lib/routes/motor.py
index 3f74825..5c4cbff 100644
--- a/lib/routes/motor.py
+++ b/lib/routes/motor.py
@@ -3,6 +3,7 @@
"""
from fastapi import APIRouter
+from opentelemetry import trace
from lib.views.motor import (
MotorSummary,
@@ -24,6 +25,8 @@
},
)
+tracer = trace.get_tracer(__name__)
+
@router.post("/")
async def create_motor(motor: Motor, motor_kind: MotorKinds) -> MotorCreated:
@@ -33,9 +36,10 @@ async def create_motor(motor: Motor, motor_kind: MotorKinds) -> MotorCreated:
## Args
``` Motor object as a JSON ```
"""
- return await MotorController(
- motor=motor, motor_kind=motor_kind
- ).create_motor()
+ with tracer.start_as_current_span("create_motor"):
+ return await MotorController(
+ motor=motor, motor_kind=motor_kind
+ ).create_motor()
@router.get("/{motor_id}")
@@ -46,7 +50,8 @@ async def read_motor(motor_id: str) -> Motor:
## Args
``` motor_id: Motor ID hash ```
"""
- return await MotorController.get_motor_by_id(motor_id)
+ with tracer.start_as_current_span("read_motor"):
+ return await MotorController.get_motor_by_id(motor_id)
@router.put("/{motor_id}")
@@ -62,9 +67,10 @@ async def update_motor(
motor: Motor object as JSON
```
"""
- return await MotorController(
- motor=motor, motor_kind=motor_kind
- ).update_motor_by_id(motor_id)
+ with tracer.start_as_current_span("update_motor"):
+ return await MotorController(
+ motor=motor, motor_kind=motor_kind
+ ).update_motor_by_id(motor_id)
@router.delete("/{motor_id}")
@@ -75,7 +81,8 @@ async def delete_motor(motor_id: str) -> MotorDeleted:
## Args
``` motor_id: Motor ID hash ```
"""
- return await MotorController.delete_motor_by_id(motor_id)
+ with tracer.start_as_current_span("delete_motor"):
+ return await MotorController.delete_motor_by_id(motor_id)
@router.get("/rocketpy/{motor_id}")
@@ -86,7 +93,8 @@ async def read_rocketpy_motor(motor_id: str) -> MotorPickle:
## Args
``` motor_id: Motor ID hash ```
"""
- return await MotorController.get_rocketpy_motor_as_jsonpickle(motor_id)
+ with tracer.start_as_current_span("read_rocketpy_motor"):
+ return await MotorController.get_rocketpy_motor_as_jsonpickle(motor_id)
@router.get("/{motor_id}/simulate")
@@ -97,4 +105,5 @@ async def simulate_motor(motor_id: str) -> MotorSummary:
## Args
``` motor_id: Motor ID hash ```
"""
- return await MotorController.simulate_motor(motor_id)
+ with tracer.start_as_current_span("simulate_motor"):
+ return await MotorController.simulate_motor(motor_id)
diff --git a/lib/routes/rocket.py b/lib/routes/rocket.py
index b8b9687..1d7301e 100644
--- a/lib/routes/rocket.py
+++ b/lib/routes/rocket.py
@@ -3,6 +3,7 @@
"""
from fastapi import APIRouter
+from opentelemetry import trace
from lib.views.rocket import (
RocketSummary,
@@ -25,6 +26,8 @@
},
)
+tracer = trace.get_tracer(__name__)
+
@router.post("/")
async def create_rocket(
@@ -36,9 +39,10 @@ async def create_rocket(
## Args
``` Rocket object as a JSON ```
"""
- return await RocketController(
- rocket=rocket, rocket_option=rocket_option, motor_kind=motor_kind
- ).create_rocket()
+ with tracer.start_as_current_span("create_rocket"):
+ return await RocketController(
+ rocket=rocket, rocket_option=rocket_option, motor_kind=motor_kind
+ ).create_rocket()
@router.get("/{rocket_id}")
@@ -49,7 +53,8 @@ async def read_rocket(rocket_id: str) -> Rocket:
## Args
``` rocket_id: Rocket ID hash ```
"""
- return await RocketController.get_rocket_by_id(rocket_id)
+ with tracer.start_as_current_span("read_rocket"):
+ return await RocketController.get_rocket_by_id(rocket_id)
@router.put("/{rocket_id}")
@@ -68,9 +73,10 @@ async def update_rocket(
rocket: Rocket object as JSON
```
"""
- return await RocketController(
- rocket=rocket, rocket_option=rocket_option, motor_kind=motor_kind
- ).update_rocket_by_id(rocket_id)
+ with tracer.start_as_current_span("update_rocket"):
+ return await RocketController(
+ rocket=rocket, rocket_option=rocket_option, motor_kind=motor_kind
+ ).update_rocket_by_id(rocket_id)
@router.delete("/{rocket_id}")
@@ -81,7 +87,8 @@ async def delete_rocket(rocket_id: str) -> RocketDeleted:
## Args
``` rocket_id: Rocket ID hash ```
"""
- return await RocketController.delete_rocket_by_id(rocket_id)
+ with tracer.start_as_current_span("delete_rocket"):
+ return await RocketController.delete_rocket_by_id(rocket_id)
@router.get("/rocketpy/{rocket_id}")
@@ -92,7 +99,10 @@ async def read_rocketpy_rocket(rocket_id: str) -> RocketPickle:
## Args
``` rocket_id: Rocket ID hash ```
"""
- return await RocketController.get_rocketpy_rocket_as_jsonpickle(rocket_id)
+ with tracer.start_as_current_span("read_rocketpy_rocket"):
+ return await RocketController.get_rocketpy_rocket_as_jsonpickle(
+ rocket_id
+ )
@router.get("/{rocket_id}/simulate")
@@ -103,4 +113,5 @@ async def simulate_rocket(rocket_id: str) -> RocketSummary:
## Args
``` rocket_id: Rocket ID hash ```
"""
- return await RocketController.simulate_rocket(rocket_id)
+ with tracer.start_as_current_span("simulate_rocket"):
+ return await RocketController.simulate_rocket(rocket_id)
diff --git a/lib/settings/gunicorn.py b/lib/settings/gunicorn.py
new file mode 100644
index 0000000..70742c6
--- /dev/null
+++ b/lib/settings/gunicorn.py
@@ -0,0 +1,17 @@
+import uptrace
+from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
+from lib.secrets import Secrets
+
+
+def post_fork(server, worker): # pylint: disable=unused-argument
+ uptrace.configure_opentelemetry(
+ dsn=Secrets.get_secret("UPTRACE_DSN"),
+ service_name="infinity-api",
+ service_version="1.2.0",
+ deployment_environment="production",
+ )
+ from app.lib import ( # pylint: disable=import-outside-toplevel
+ server as fastapi_server,
+ )
+
+ FastAPIInstrumentor.instrument_app(fastapi_server)
diff --git a/pyproject.toml b/pyproject.toml
index b78b26d..70a9e16 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -7,7 +7,7 @@ dependencies = {file = ["requirements.txt"]}
[project]
name = "Infinity-API"
-version = "1.1.0"
+version = "1.2.0"
description = "RESTFULL open API for rocketpy"
dynamic = ["dependencies"]
requires-python = ">=3.12"
diff --git a/requirements.txt b/requirements.txt
index be65cb7..9abaebd 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -7,3 +7,6 @@ jsonpickle
gunicorn
uvicorn
rocketpy
+opentelemetry.instrumentation.fastapi
+opentelemetry-api
+opentelemetry-sdk