diff --git a/.env.example b/.env.example index 453c8d2..95f4f57 100644 --- a/.env.example +++ b/.env.example @@ -3,3 +3,4 @@ DB_PORT= DB_DATABASE= DB_USERNAME= DB_PASSWORD= +OTLD_SUPPORT=false diff --git a/app.py b/app.py index 246eed5..fc15daf 100644 --- a/app.py +++ b/app.py @@ -3,7 +3,7 @@ from generate import on_load from fastapi import FastAPI, Response from routes.auth import router as auth from contextlib import asynccontextmanager -from util.config import conn_param, db_url +from util.config import conn_param, db_url, get_otld from routes.balance import router as balance from fastapi.middleware.cors import CORSMiddleware @@ -40,3 +40,14 @@ async def index(resp: Response): app.include_router(router=auth) app.include_router(router=balance) + +if get_otld(): + import logging + from otlp_tracing import configure_otel_otlp + from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor + + logging.basicConfig(level=logging.INFO) + tracer = configure_otel_otlp() + logger = logging.getLogger(__name__) + + FastAPIInstrumentor.instrument_app(app) diff --git a/otlp_tracing.py b/otlp_tracing.py new file mode 100644 index 0000000..ee5a514 --- /dev/null +++ b/otlp_tracing.py @@ -0,0 +1,42 @@ +import logging +from opentelemetry import metrics, trace + +from opentelemetry._logs import set_logger_provider +from opentelemetry.exporter.otlp.proto.grpc._log_exporter import ( + OTLPLogExporter, +) +from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter +from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter +from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler +from opentelemetry.sdk._logs.export import BatchLogRecordProcessor +from opentelemetry.sdk.metrics import MeterProvider +from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export import BatchSpanProcessor + +def configure_oltp_grpc_tracing(endpoint: str = None) -> trace.Tracer: + # Configure Tracing + traceProvider = TracerProvider() + processor = BatchSpanProcessor(OTLPSpanExporter(endpoint=endpoint)) + traceProvider.add_span_processor(processor) + trace.set_tracer_provider(traceProvider) + + # Configure Metrics + reader = PeriodicExportingMetricReader(OTLPMetricExporter(endpoint=endpoint)) + meterProvider = MeterProvider(metric_readers=[reader]) + metrics.set_meter_provider(meterProvider) + + # Configure Logging + logger_provider = LoggerProvider() + set_logger_provider(logger_provider) + + exporter = OTLPLogExporter(endpoint=endpoint) + logger_provider.add_log_record_processor(BatchLogRecordProcessor(exporter)) + handler = LoggingHandler(level=logging.NOTSET, logger_provider=logger_provider) + handler.setFormatter(logging.Formatter("Python: %(message)s")) + + # Attach OTLP handler to root logger + logging.getLogger().addHandler(handler) + + tracer = trace.get_tracer(__name__) + return tracer diff --git a/requirements.txt b/requirements.txt index 2d47fbc..f8cb6f7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,20 +1,36 @@ annotated-types==0.7.0 anyio==4.4.0 +asgiref==3.8.1 certifi==2024.8.30 click==8.1.7 +Deprecated==1.2.14 dnspython==2.6.1 email_validator==2.2.0 fastapi==0.114.0 fastapi-cli==0.0.5 +googleapis-common-protos==1.65.0 +grpcio==1.66.2 h11==0.14.0 httpcore==1.0.5 httptools==0.6.1 httpx==0.27.2 idna==3.8 +importlib_metadata==8.4.0 Jinja2==3.1.4 markdown-it-py==3.0.0 MarkupSafe==2.1.5 mdurl==0.1.2 +opentelemetry-api==1.27.0 +opentelemetry-exporter-otlp-proto-common==1.27.0 +opentelemetry-exporter-otlp-proto-grpc==1.27.0 +opentelemetry-instrumentation==0.48b0 +opentelemetry-instrumentation-asgi==0.48b0 +opentelemetry-instrumentation-fastapi==0.48b0 +opentelemetry-proto==1.27.0 +opentelemetry-sdk==1.27.0 +opentelemetry-semantic-conventions==0.48b0 +opentelemetry-util-http==0.48b0 +protobuf==4.25.5 psycopg2-binary==2.9.9 pydantic==2.9.1 pydantic_core==2.23.3 @@ -23,6 +39,7 @@ python-dotenv==1.0.1 python-multipart==0.0.9 PyYAML==6.0.2 rich==13.8.0 +setuptools==75.1.0 shellingham==1.5.4 sniffio==1.3.1 starlette==0.38.5 @@ -32,3 +49,5 @@ uvicorn==0.30.6 uvloop==0.20.0 watchfiles==0.24.0 websockets==13.0.1 +wrapt==1.16.0 +zipp==3.20.2 diff --git a/util/config.py b/util/config.py index e88ba8d..97f7296 100644 --- a/util/config.py +++ b/util/config.py @@ -23,3 +23,9 @@ conn_param = "host=%s port=%s dbname=%s user=%s password=%s" % ( ) secret = _load_secret() +def get_otld(): + ret = os.getenv("OTLD_SUPPORT") + if ret is "1" or ret is "true": + return True + + return False