Skip to content

Commit ada9183

Browse files
updated logging config
1 parent 18dfa2f commit ada9183

3 files changed

Lines changed: 58 additions & 6 deletions

File tree

content-gen/infra/main.bicep

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,8 @@ module containerInstance 'modules/container-instance.bicep' = {
869869
{ name: 'AZURE_AI_PROJECT_ENDPOINT', value: aiFoundryAiProjectEndpoint }
870870
{ name: 'AZURE_AI_MODEL_DEPLOYMENT_NAME', value: gptModelName }
871871
{ name: 'AZURE_AI_IMAGE_MODEL_DEPLOYMENT', value: imageModelConfig[imageModelChoice].name }
872+
// Application Insights
873+
{ name: 'APPLICATIONINSIGHTS_CONNECTION_STRING', value: enableMonitoring ? applicationInsights!.outputs.connectionString : '' }
872874
]
873875
}
874876
}

content-gen/src/backend/app.py

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from quart import Quart, request, jsonify, Response
1818
from quart_cors import cors
19+
from opentelemetry import trace
1920

2021
from settings import app_settings
2122
from models import CreativeBrief, Product
@@ -24,7 +25,9 @@
2425
from services.blob_service import get_blob_service
2526
from services.title_service import get_title_service
2627
from api.admin import admin_bp
28+
from azure.core.settings import settings as azure_settings
2729
from azure.monitor.opentelemetry import configure_azure_monitor
30+
from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware
2831

2932
# In-memory task storage for generation tasks
3033
# In production, this should be replaced with Redis or similar
@@ -35,27 +38,73 @@
3538
level=logging.INFO,
3639
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
3740
)
38-
logging.getLogger("azure.core.pipeline.policies.http_logging_policy").setLevel(logging.WARNING)
3941
logger = logging.getLogger(__name__)
4042

43+
# Create Quart app
44+
app = Quart(__name__)
45+
app = cors(app, allow_origin="*")
46+
4147
# Check if the Application Insights connection string is set in the environment variables
4248
appinsights_connection_string = os.getenv("APPLICATIONINSIGHTS_CONNECTION_STRING")
4349
if appinsights_connection_string:
4450
# Configure Application Insights if the connection string is found
45-
configure_azure_monitor(connection_string=appinsights_connection_string)
51+
# logging_level=WARNING sends only WARNING/ERROR/CRITICAL to App Insights
52+
# (INFO traces like "Loaded product", "Uploaded image", workflow steps stay in container logs only)
53+
configure_azure_monitor(
54+
connection_string=appinsights_connection_string,
55+
enable_live_metrics=False,
56+
logging_level=logging.WARNING,
57+
)
58+
# Disable Azure SDK native span creation (ContainerProxy.*, BlobClient.* InProc spans)
59+
azure_settings.tracing_implementation = None
60+
# Apply ASGI middleware for request tracing (Quart is not auto-instrumented by configure_azure_monitor)
61+
app.asgi_app = OpenTelemetryMiddleware(
62+
app.asgi_app,
63+
exclude_spans=["receive", "send"],
64+
excluded_urls="api/generate/status",
65+
)
4666
logger.info("Application Insights configured with the provided connection string")
4767
else:
4868
# Log a warning if the connection string is not found
4969
logger.warning("No Application Insights connection string found. Skipping configuration")
5070

51-
# Create Quart app
52-
app = Quart(__name__)
53-
app = cors(app, allow_origin="*")
54-
5571
# Register blueprints
5672
app.register_blueprint(admin_bp)
5773

5874

75+
@app.before_request
76+
async def set_conversation_context():
77+
"""Attach conversation_id and user_id to the current OTel span for App Insights."""
78+
conversation_id = ""
79+
user_id = ""
80+
81+
# 1. Extract from JSON body (POST requests)
82+
if request.content_type and "json" in request.content_type:
83+
try:
84+
data = await request.get_json()
85+
if data and isinstance(data, dict):
86+
conversation_id = data.get("conversation_id", "")
87+
user_id = data.get("user_id", "")
88+
except Exception:
89+
pass
90+
91+
# 2. Extract from URL path parameters (e.g. /api/conversations/<conversation_id>)
92+
if not conversation_id and request.view_args:
93+
conversation_id = request.view_args.get("conversation_id", "")
94+
95+
# 3. Extract from query parameters (e.g. ?conversation_id=xxx)
96+
if not conversation_id:
97+
conversation_id = request.args.get("conversation_id", "")
98+
99+
if not user_id:
100+
user_id = request.args.get("user_id", "") or request.headers.get("X-Ms-Client-Principal-Id", "anonymous")
101+
102+
span = trace.get_current_span()
103+
if span.is_recording():
104+
span.set_attribute("conversation_id", conversation_id)
105+
span.set_attribute("user_id", user_id)
106+
107+
59108
# ==================== Authentication Helper ====================
60109

61110
def get_authenticated_user():

content-gen/src/backend/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ httpx>=0.27.0
2828

2929
# Monitoring / Telemetry
3030
azure-monitor-opentelemetry>=1.6.0
31+
opentelemetry-instrumentation-asgi>=0.48b0
3132

3233
# Data Validation
3334
pydantic>=2.8.0

0 commit comments

Comments
 (0)