From a862efaa478b4846dea35a35b4d182aba65cf5b8 Mon Sep 17 00:00:00 2001 From: yukaty <254470+yukaty@users.noreply.github.com> Date: Mon, 29 Jun 2026 10:54:27 -0600 Subject: [PATCH 1/2] Add materials for FastAPI OpenTelemetry tutorial --- fastapi-opentelemetry/README.md | 3 ++ fastapi-opentelemetry/main.py | 54 ++++++++++++++++++++++++++ fastapi-opentelemetry/requirements.txt | 36 +++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 fastapi-opentelemetry/README.md create mode 100644 fastapi-opentelemetry/main.py create mode 100644 fastapi-opentelemetry/requirements.txt diff --git a/fastapi-opentelemetry/README.md b/fastapi-opentelemetry/README.md new file mode 100644 index 0000000000..4fb290aad9 --- /dev/null +++ b/fastapi-opentelemetry/README.md @@ -0,0 +1,3 @@ +# How to Integrate OpenTelemetry With FastAPI App + +This folder contains sample code for the Real Python tutorial [How to Integrate OpenTelemetry With FastAPI App](https://realpython.com/). diff --git a/fastapi-opentelemetry/main.py b/fastapi-opentelemetry/main.py new file mode 100644 index 0000000000..a7b08a0aff --- /dev/null +++ b/fastapi-opentelemetry/main.py @@ -0,0 +1,54 @@ +import logging + +from opentelemetry import trace +from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor + +from fastapi import FastAPI + +app = FastAPI() +FastAPIInstrumentor.instrument_app(app) + +tracer = trace.get_tracer(__name__) + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +def simulate_db_query(user_id: int): + with tracer.start_as_current_span("db_query") as span: + span.set_attribute("db.system", "postgresql") + span.set_attribute("user.id", user_id) + + trace_id = format(span.get_span_context().trace_id, "032x") + span_id = format(span.get_span_context().span_id, "016x") + + if user_id == 999: + span.record_exception(Exception("Database connection lost!")) + span.set_status(trace.status.Status(trace.status.StatusCode.ERROR)) + logger.error( + "Database connection lost! " + f"trace_id={trace_id}, span_id={span_id}" + ) + raise ValueError("DB Error") + + logger.info( + f"Executing DB query for user {user_id}. " + f"trace_id={trace_id}, span_id={span_id}" + ) + return {"name": "Demo User", "role": "admin"} + + +@app.get("/users/{user_id}") +def get_user(user_id: int): + logger.info(f"Received request for user {user_id}") + + try: + data = simulate_db_query(user_id) + return {"status": "success", "data": data} + except ValueError: + return {"status": "error", "message": "Failed to fetch user"} + + +@app.get("/") +def read_root(): + return {"message": "Hello, OpenTelemetry!"} diff --git a/fastapi-opentelemetry/requirements.txt b/fastapi-opentelemetry/requirements.txt new file mode 100644 index 0000000000..0a9c0b1045 --- /dev/null +++ b/fastapi-opentelemetry/requirements.txt @@ -0,0 +1,36 @@ +annotated-doc==0.0.4 +annotated-types==0.7.0 +anyio==4.14.0 +asgiref==3.11.1 +certifi==2026.6.17 +charset-normalizer==3.4.7 +click==8.4.1 +fastapi==0.138.0 +googleapis-common-protos==1.75.0 +grpcio==1.81.1 +h11==0.16.0 +idna==3.18 +opentelemetry-api==1.42.1 +opentelemetry-distro==0.63b1 +opentelemetry-exporter-otlp==1.42.1 +opentelemetry-exporter-otlp-proto-common==1.42.1 +opentelemetry-exporter-otlp-proto-grpc==1.42.1 +opentelemetry-exporter-otlp-proto-http==1.42.1 +opentelemetry-instrumentation==0.63b1 +opentelemetry-instrumentation-asgi==0.63b1 +opentelemetry-instrumentation-fastapi==0.63b1 +opentelemetry-proto==1.42.1 +opentelemetry-sdk==1.42.1 +opentelemetry-semantic-conventions==0.63b1 +opentelemetry-util-http==0.63b1 +packaging==26.2 +protobuf==6.33.6 +pydantic==2.13.4 +pydantic_core==2.46.4 +requests==2.34.2 +starlette==1.3.1 +typing-inspection==0.4.2 +typing_extensions==4.15.0 +urllib3==2.7.0 +uvicorn==0.49.0 +wrapt==2.2.2 From b12418e671eae29566e61b27a266b11a8cc9bf52 Mon Sep 17 00:00:00 2001 From: yukaty <254470+yukaty@users.noreply.github.com> Date: Thu, 9 Jul 2026 13:32:37 -0600 Subject: [PATCH 2/2] Sync with tutorial after TR --- fastapi-opentelemetry/main.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/fastapi-opentelemetry/main.py b/fastapi-opentelemetry/main.py index a7b08a0aff..8f05c48dc1 100644 --- a/fastapi-opentelemetry/main.py +++ b/fastapi-opentelemetry/main.py @@ -1,13 +1,10 @@ import logging from opentelemetry import trace -from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor from fastapi import FastAPI app = FastAPI() -FastAPIInstrumentor.instrument_app(app) - tracer = trace.get_tracer(__name__) logging.basicConfig(level=logging.INFO) @@ -23,25 +20,26 @@ def simulate_db_query(user_id: int): span_id = format(span.get_span_context().span_id, "016x") if user_id == 999: - span.record_exception(Exception("Database connection lost!")) - span.set_status(trace.status.Status(trace.status.StatusCode.ERROR)) logger.error( - "Database connection lost! " - f"trace_id={trace_id}, span_id={span_id}" + "DB query failed for user %s. trace_id=%s, span_id=%s", + user_id, + trace_id, + span_id, ) - raise ValueError("DB Error") + raise ValueError("Database connection lost!") logger.info( - f"Executing DB query for user {user_id}. " - f"trace_id={trace_id}, span_id={span_id}" + "Executing DB query for user %s. trace_id=%s, span_id=%s", + user_id, + trace_id, + span_id, ) return {"name": "Demo User", "role": "admin"} @app.get("/users/{user_id}") def get_user(user_id: int): - logger.info(f"Received request for user {user_id}") - + logger.info("Received request for user %s", user_id) try: data = simulate_db_query(user_id) return {"status": "success", "data": data}