Carry trace context across the Redis Stream by hand
Redis Streams have no headers, so the gateway injects traceparent into the message fields inside a PRODUCER span. The notifier and audit consumers extract it and open one CONSUMER span per message, so their DB and XACK spans join the transaction's trace. The idle XREADGROUP poll runs under suppress_instrumentation (it was a ~2s root trace every loop), and audit strips the trace fields so its stored payload is unchanged. The gateway also stops tracing its frontend's /api/health/ polling. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
14938ab36b
commit
d1c158c835
@@ -24,7 +24,7 @@ app = FastAPI(title=SERVICE_NAME)
|
||||
|
||||
FastAPIInstrumentor.instrument_app(
|
||||
app,
|
||||
excluded_urls="/healthz",
|
||||
excluded_urls="/healthz,/api/health/",
|
||||
exclude_spans=["receive", "send"],
|
||||
)
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ import time
|
||||
import uuid
|
||||
|
||||
import httpx
|
||||
from opentelemetry import propagate, trace
|
||||
from opentelemetry.trace import SpanKind
|
||||
|
||||
from common.http import downstream_client
|
||||
from common.redis_client import redis_client
|
||||
@@ -11,6 +13,7 @@ from common.settings import settings
|
||||
from . import chaos
|
||||
|
||||
logger = logging.getLogger("gateway")
|
||||
tracer = trace.get_tracer(__name__)
|
||||
|
||||
REJECT_THRESHOLD = 0.8
|
||||
|
||||
@@ -129,17 +132,29 @@ async def create_transaction(payload: dict, request_id: str) -> dict:
|
||||
settled = settle_resp.json()
|
||||
|
||||
try:
|
||||
await redis_client.xadd(
|
||||
settings.stream_name,
|
||||
{
|
||||
"transaction_id": tx_id,
|
||||
"account_id": created["account_id"],
|
||||
"kind": created["kind"],
|
||||
"amount_minor": str(created["amount_minor"]),
|
||||
"status": settled["status"],
|
||||
"risk_score": str(risk_score),
|
||||
fields = {
|
||||
"transaction_id": tx_id,
|
||||
"account_id": created["account_id"],
|
||||
"kind": created["kind"],
|
||||
"amount_minor": str(created["amount_minor"]),
|
||||
"status": settled["status"],
|
||||
"risk_score": str(risk_score),
|
||||
}
|
||||
with tracer.start_as_current_span(
|
||||
f"send {settings.stream_name}",
|
||||
kind=SpanKind.PRODUCER,
|
||||
attributes={
|
||||
"messaging.system": "redis",
|
||||
"messaging.destination.name": settings.stream_name,
|
||||
"messaging.operation.type": "send",
|
||||
},
|
||||
)
|
||||
) as span:
|
||||
# Redis Streams carry no trace context, so it travels in the
|
||||
# message itself. Injected inside the PRODUCER span, so the
|
||||
# consumers' parent is this span.
|
||||
propagate.inject(fields)
|
||||
msg_id = await redis_client.xadd(settings.stream_name, fields)
|
||||
span.set_attribute("messaging.message.id", msg_id)
|
||||
except Exception:
|
||||
logger.exception(f"failed to publish transaction {tx_id} to the stream")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user