85 lines
2.8 KiB
Python
85 lines
2.8 KiB
Python
import time
|
|
import random
|
|
from flask import Flask
|
|
from opentelemetry import trace, metrics
|
|
from opentelemetry.sdk.trace import TracerProvider
|
|
from opentelemetry.sdk.metrics import MeterProvider
|
|
from opentelemetry.sdk.resources import Resource
|
|
from opentelemetry.sdk.trace.export import BatchSpanProcessor
|
|
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
|
|
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
|
|
import logging
|
|
from opentelemetry.trace import get_current_span
|
|
|
|
# === Resource ===
|
|
resource = Resource(attributes={"service.name": "my-flask-app"})
|
|
|
|
# === Tracing ===
|
|
trace.set_tracer_provider(TracerProvider(resource=resource))
|
|
tracer = trace.get_tracer(__name__)
|
|
# trace_exporter = OTLPSpanExporter(
|
|
# endpoint="http://otel-collector.meta.svc:4318/v1/traces"
|
|
# )
|
|
trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(trace_exporter))
|
|
|
|
# === Metrics ===
|
|
reader = PeriodicExportingMetricReader(
|
|
OTLPMetricExporter(endpoint="http://otel-collector.meta.svc:4318/v1/metrics")
|
|
)
|
|
provider = MeterProvider(resource=resource, metric_readers=[reader])
|
|
metrics.set_meter_provider(provider)
|
|
|
|
|
|
# === Logging with trace ID ===
|
|
class TraceIdFilter(logging.Filter):
|
|
def filter(self, record):
|
|
span = trace.get_current_span()
|
|
ctx = span.get_span_context()
|
|
record.trace_id = format(ctx.trace_id, "032x") if ctx.trace_id != 0 else None
|
|
record.span_id = format(ctx.span_id, "016x") if ctx.span_id != 0 else None
|
|
return True
|
|
|
|
|
|
formatter = logging.Formatter(
|
|
'{"message": "%(message)s", "trace_id": "%(trace_id)s", "span_id": "%(span_id)s"}'
|
|
)
|
|
handler = logging.StreamHandler()
|
|
handler.setFormatter(formatter)
|
|
handler.addFilter(TraceIdFilter())
|
|
|
|
logger = logging.getLogger("myapp")
|
|
logger.setLevel(logging.INFO)
|
|
logger.addHandler(handler)
|
|
|
|
# === Flask App ===
|
|
app = Flask(__name__)
|
|
# FlaskInstrumentor().instrument_app(app) # auto-instruments metrics + traces
|
|
|
|
|
|
@app.route("/")
|
|
def root():
|
|
with tracer.start_as_current_span("handle_homepage"):
|
|
logger.info(
|
|
"handling request",
|
|
extra={"trace_id": get_current_span().get_span_context().trace_id},
|
|
)
|
|
time.sleep(random.random() * 0.05) # simulate request parsing
|
|
fetch_user()
|
|
calculate_recommendations()
|
|
return "Hello from / with traces and metrics!"
|
|
|
|
|
|
def fetch_user():
|
|
with tracer.start_as_current_span("fetch_user"):
|
|
time.sleep(random.random() * 0.1) # simulate DB query
|
|
|
|
|
|
def calculate_recommendations():
|
|
with tracer.start_as_current_span("calculate_recommendations"):
|
|
for i in range(2):
|
|
with tracer.start_as_current_span(f"score_item_{i}"):
|
|
time.sleep(random.random())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
app.run(host="0.0.0.0", port=5000)
|