Skip to content

Commit 0d196de

Browse files
instrument API with open telemetry/uptrace
1 parent ca0eda6 commit 0d196de

10 files changed

Lines changed: 111 additions & 47 deletions

File tree

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ RUN apt-get update && \
1616

1717
COPY ./lib /app/lib
1818

19-
CMD ["gunicorn", "-w 1", "--threads=2", "-k", "uvicorn.workers.UvicornWorker", "lib:app", "--log-level", "Debug", "-b", "0.0.0.0:3000", "--timeout", "120"]
19+
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"]

lib/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@ def parse_error(error):
2525
return f"{exc_type} exception: {exc_obj}"
2626

2727

28-
from .api import app # noqa
28+
from .api import app # noqa

lib/api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def custom_openapi():
3535
return app.openapi_schema
3636
openapi_schema = get_openapi(
3737
title="RocketPy Infinity-API",
38-
version="1.1.0 BETA",
38+
version="1.2.0 BETA",
3939
description=(
4040
"<p style='font-size: 18px;'>RocketPy Infinity-API is a RESTful Open API for RocketPy, a rocket flight simulator.</p>"
4141
"<br/>"

lib/routes/environment.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44

55
from fastapi import APIRouter
6+
from opentelemetry import trace
67

78
from lib.views.environment import (
89
EnvSummary,
@@ -24,6 +25,8 @@
2425
},
2526
)
2627

28+
tracer = trace.get_tracer(__name__)
29+
2730

2831
@router.post("/")
2932
async def create_env(env: Env) -> EnvCreated:
@@ -33,7 +36,8 @@ async def create_env(env: Env) -> EnvCreated:
3336
## Args
3437
``` models.Env JSON ```
3538
"""
36-
return await EnvController(env).create_env()
39+
with tracer.start_as_current_span("create_env"):
40+
return await EnvController(env).create_env()
3741

3842

3943
@router.get("/{env_id}")
@@ -44,7 +48,8 @@ async def read_env(env_id: str) -> Env:
4448
## Args
4549
``` env_id: str ```
4650
"""
47-
return await EnvController.get_env_by_id(env_id)
51+
with tracer.start_as_current_span("read_env"):
52+
return await EnvController.get_env_by_id(env_id)
4853

4954

5055
@router.put("/{env_id}")
@@ -58,7 +63,8 @@ async def update_env(env_id: str, env: Env) -> EnvUpdated:
5863
env: models.Env JSON
5964
```
6065
"""
61-
return await EnvController(env).update_env_by_id(env_id)
66+
with tracer.start_as_current_span("update_env"):
67+
return await EnvController(env).update_env_by_id(env_id)
6268

6369

6470
@router.delete("/{env_id}")
@@ -69,7 +75,8 @@ async def delete_env(env_id: str) -> EnvDeleted:
6975
## Args
7076
``` env_id: Environment ID hash ```
7177
"""
72-
return await EnvController.delete_env_by_id(env_id)
78+
with tracer.start_as_current_span("delete_env"):
79+
return await EnvController.delete_env_by_id(env_id)
7380

7481

7582
@router.get("/rocketpy/{env_id}")
@@ -80,7 +87,8 @@ async def read_rocketpy_env(env_id: str) -> EnvPickle:
8087
## Args
8188
``` env_id: str ```
8289
"""
83-
return await EnvController.get_rocketpy_env_as_jsonpickle(env_id)
90+
with tracer.start_as_current_span("read_rocketpy_env"):
91+
return await EnvController.get_rocketpy_env_as_jsonpickle(env_id)
8492

8593

8694
@router.get("/{env_id}/simulate")
@@ -91,4 +99,5 @@ async def simulate_env(env_id: str) -> EnvSummary:
9199
## Args
92100
``` env_id: str ```
93101
"""
94-
return await EnvController.simulate_env(env_id)
102+
with tracer.start_as_current_span("simulate_env"):
103+
return await EnvController.simulate_env(env_id)

lib/routes/flight.py

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44

55
from fastapi import APIRouter
6+
from opentelemetry import trace
67

78
from lib.views.flight import (
89
FlightSummary,
@@ -27,6 +28,8 @@
2728
},
2829
)
2930

31+
tracer = trace.get_tracer(__name__)
32+
3033

3134
@router.post("/")
3235
async def create_flight(
@@ -38,9 +41,10 @@ async def create_flight(
3841
## Args
3942
``` Flight object as JSON ```
4043
"""
41-
return await FlightController(
42-
flight, rocket_option=rocket_option, motor_kind=motor_kind
43-
).create_flight()
44+
with tracer.start_as_current_span("create_flight"):
45+
return await FlightController(
46+
flight, rocket_option=rocket_option, motor_kind=motor_kind
47+
).create_flight()
4448

4549

4650
@router.get("/{flight_id}")
@@ -51,7 +55,8 @@ async def read_flight(flight_id: str) -> Flight:
5155
## Args
5256
``` flight_id: Flight ID hash ```
5357
"""
54-
return await FlightController.get_flight_by_id(flight_id)
58+
with tracer.start_as_current_span("read_flight"):
59+
return await FlightController.get_flight_by_id(flight_id)
5560

5661

5762
@router.get("/rocketpy/{flight_id}")
@@ -62,7 +67,10 @@ async def read_rocketpy_flight(flight_id: str) -> FlightPickle:
6267
## Args
6368
``` flight_id: Flight ID hash. ```
6469
"""
65-
return await FlightController.get_rocketpy_flight_as_jsonpickle(flight_id)
70+
with tracer.start_as_current_span("read_rocketpy_flight"):
71+
return await FlightController.get_rocketpy_flight_as_jsonpickle(
72+
flight_id
73+
)
6674

6775

6876
@router.put("/{flight_id}/env")
@@ -76,7 +84,10 @@ async def update_flight_env(flight_id: str, env: Env) -> FlightUpdated:
7684
env: env object as JSON
7785
```
7886
"""
79-
return await FlightController.update_env_by_flight_id(flight_id, env=env)
87+
with tracer.start_as_current_span("update_flight_env"):
88+
return await FlightController.update_env_by_flight_id(
89+
flight_id, env=env
90+
)
8091

8192

8293
@router.put("/{flight_id}/rocket")
@@ -95,12 +106,13 @@ async def update_flight_rocket(
95106
rocket: Rocket object as JSON
96107
```
97108
"""
98-
return await FlightController.update_rocket_by_flight_id(
99-
flight_id,
100-
rocket=rocket,
101-
rocket_option=rocket_option,
102-
motor_kind=motor_kind,
103-
)
109+
with tracer.start_as_current_span("update_flight_rocket"):
110+
return await FlightController.update_rocket_by_flight_id(
111+
flight_id,
112+
rocket=rocket,
113+
rocket_option=rocket_option,
114+
motor_kind=motor_kind,
115+
)
104116

105117

106118
@router.put("/{flight_id}")
@@ -119,9 +131,10 @@ async def update_flight(
119131
flight: Flight object as JSON
120132
```
121133
"""
122-
return await FlightController(
123-
flight, rocket_option=rocket_option, motor_kind=motor_kind
124-
).update_flight_by_id(flight_id)
134+
with tracer.start_as_current_span("update_flight"):
135+
return await FlightController(
136+
flight, rocket_option=rocket_option, motor_kind=motor_kind
137+
).update_flight_by_id(flight_id)
125138

126139

127140
@router.delete("/{flight_id}")
@@ -132,7 +145,8 @@ async def delete_flight(flight_id: str) -> FlightDeleted:
132145
## Args
133146
``` flight_id: Flight ID hash ```
134147
"""
135-
return await FlightController.delete_flight_by_id(flight_id)
148+
with tracer.start_as_current_span("delete_flight"):
149+
return await FlightController.delete_flight_by_id(flight_id)
136150

137151

138152
@router.get("/{flight_id}/simulate")
@@ -143,4 +157,5 @@ async def simulate_flight(flight_id: str) -> FlightSummary:
143157
## Args
144158
``` flight_id: Flight ID hash ```
145159
"""
146-
return await FlightController.simulate_flight(flight_id)
160+
with tracer.start_as_current_span("simulate_flight"):
161+
return await FlightController.simulate_flight(flight_id)

lib/routes/motor.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44

55
from fastapi import APIRouter
6+
from opentelemetry import trace
67

78
from lib.views.motor import (
89
MotorSummary,
@@ -24,6 +25,8 @@
2425
},
2526
)
2627

28+
tracer = trace.get_tracer(__name__)
29+
2730

2831
@router.post("/")
2932
async def create_motor(motor: Motor, motor_kind: MotorKinds) -> MotorCreated:
@@ -33,9 +36,10 @@ async def create_motor(motor: Motor, motor_kind: MotorKinds) -> MotorCreated:
3336
## Args
3437
``` Motor object as a JSON ```
3538
"""
36-
return await MotorController(
37-
motor=motor, motor_kind=motor_kind
38-
).create_motor()
39+
with tracer.start_as_current_span("create_motor"):
40+
return await MotorController(
41+
motor=motor, motor_kind=motor_kind
42+
).create_motor()
3943

4044

4145
@router.get("/{motor_id}")
@@ -46,7 +50,8 @@ async def read_motor(motor_id: str) -> Motor:
4650
## Args
4751
``` motor_id: Motor ID hash ```
4852
"""
49-
return await MotorController.get_motor_by_id(motor_id)
53+
with tracer.start_as_current_span("read_motor"):
54+
return await MotorController.get_motor_by_id(motor_id)
5055

5156

5257
@router.put("/{motor_id}")
@@ -62,9 +67,10 @@ async def update_motor(
6267
motor: Motor object as JSON
6368
```
6469
"""
65-
return await MotorController(
66-
motor=motor, motor_kind=motor_kind
67-
).update_motor_by_id(motor_id)
70+
with tracer.start_as_current_span("update_motor"):
71+
return await MotorController(
72+
motor=motor, motor_kind=motor_kind
73+
).update_motor_by_id(motor_id)
6874

6975

7076
@router.delete("/{motor_id}")
@@ -75,7 +81,8 @@ async def delete_motor(motor_id: str) -> MotorDeleted:
7581
## Args
7682
``` motor_id: Motor ID hash ```
7783
"""
78-
return await MotorController.delete_motor_by_id(motor_id)
84+
with tracer.start_as_current_span("delete_motor"):
85+
return await MotorController.delete_motor_by_id(motor_id)
7986

8087

8188
@router.get("/rocketpy/{motor_id}")
@@ -86,7 +93,8 @@ async def read_rocketpy_motor(motor_id: str) -> MotorPickle:
8693
## Args
8794
``` motor_id: Motor ID hash ```
8895
"""
89-
return await MotorController.get_rocketpy_motor_as_jsonpickle(motor_id)
96+
with tracer.start_as_current_span("read_rocketpy_motor"):
97+
return await MotorController.get_rocketpy_motor_as_jsonpickle(motor_id)
9098

9199

92100
@router.get("/{motor_id}/simulate")
@@ -97,4 +105,5 @@ async def simulate_motor(motor_id: str) -> MotorSummary:
97105
## Args
98106
``` motor_id: Motor ID hash ```
99107
"""
100-
return await MotorController.simulate_motor(motor_id)
108+
with tracer.start_as_current_span("simulate_motor"):
109+
return await MotorController.simulate_motor(motor_id)

lib/routes/rocket.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44

55
from fastapi import APIRouter
6+
from opentelemetry import trace
67

78
from lib.views.rocket import (
89
RocketSummary,
@@ -25,6 +26,8 @@
2526
},
2627
)
2728

29+
tracer = trace.get_tracer(__name__)
30+
2831

2932
@router.post("/")
3033
async def create_rocket(
@@ -36,9 +39,10 @@ async def create_rocket(
3639
## Args
3740
``` Rocket object as a JSON ```
3841
"""
39-
return await RocketController(
40-
rocket=rocket, rocket_option=rocket_option, motor_kind=motor_kind
41-
).create_rocket()
42+
with tracer.start_as_current_span("create_rocket"):
43+
return await RocketController(
44+
rocket=rocket, rocket_option=rocket_option, motor_kind=motor_kind
45+
).create_rocket()
4246

4347

4448
@router.get("/{rocket_id}")
@@ -49,7 +53,8 @@ async def read_rocket(rocket_id: str) -> Rocket:
4953
## Args
5054
``` rocket_id: Rocket ID hash ```
5155
"""
52-
return await RocketController.get_rocket_by_id(rocket_id)
56+
with tracer.start_as_current_span("read_rocket"):
57+
return await RocketController.get_rocket_by_id(rocket_id)
5358

5459

5560
@router.put("/{rocket_id}")
@@ -68,9 +73,10 @@ async def update_rocket(
6873
rocket: Rocket object as JSON
6974
```
7075
"""
71-
return await RocketController(
72-
rocket=rocket, rocket_option=rocket_option, motor_kind=motor_kind
73-
).update_rocket_by_id(rocket_id)
76+
with tracer.start_as_current_span("update_rocket"):
77+
return await RocketController(
78+
rocket=rocket, rocket_option=rocket_option, motor_kind=motor_kind
79+
).update_rocket_by_id(rocket_id)
7480

7581

7682
@router.delete("/{rocket_id}")
@@ -81,7 +87,8 @@ async def delete_rocket(rocket_id: str) -> RocketDeleted:
8187
## Args
8288
``` rocket_id: Rocket ID hash ```
8389
"""
84-
return await RocketController.delete_rocket_by_id(rocket_id)
90+
with tracer.start_as_current_span("delete_rocket"):
91+
return await RocketController.delete_rocket_by_id(rocket_id)
8592

8693

8794
@router.get("/rocketpy/{rocket_id}")
@@ -92,7 +99,10 @@ async def read_rocketpy_rocket(rocket_id: str) -> RocketPickle:
9299
## Args
93100
``` rocket_id: Rocket ID hash ```
94101
"""
95-
return await RocketController.get_rocketpy_rocket_as_jsonpickle(rocket_id)
102+
with tracer.start_as_current_span("read_rocketpy_rocket"):
103+
return await RocketController.get_rocketpy_rocket_as_jsonpickle(
104+
rocket_id
105+
)
96106

97107

98108
@router.get("/{rocket_id}/simulate")
@@ -103,4 +113,5 @@ async def simulate_rocket(rocket_id: str) -> RocketSummary:
103113
## Args
104114
``` rocket_id: Rocket ID hash ```
105115
"""
106-
return await RocketController.simulate_rocket(rocket_id)
116+
with tracer.start_as_current_span("simulate_rocket"):
117+
return await RocketController.simulate_rocket(rocket_id)

lib/settings/gunicorn.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import uptrace
2+
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
3+
from lib.secrets import Secrets
4+
5+
6+
def post_fork(server, worker): # pylint: disable=unused-argument
7+
uptrace.configure_opentelemetry(
8+
dsn=Secrets.get_secret("UPTRACE_DSN"),
9+
service_name="infinity-api",
10+
service_version="1.2.0",
11+
deployment_environment="production",
12+
)
13+
from app.lib import ( # pylint: disable=import-outside-toplevel
14+
server as fastapi_server,
15+
)
16+
17+
FastAPIInstrumentor.instrument_app(fastapi_server)

0 commit comments

Comments
 (0)