Instrumentation for outbound requests (#726)

<!-- CURSOR_SUMMARY -->
> [!NOTE]
> Adds OpenTelemetry instrumentation for outbound HTTP (httpx, aiohttp,
requests), guards span environment attribute, and updates
package/version deps.
> 
> - **Telemetry**:
> - Instrument outbound HTTP clients with OpenTelemetry: `httpx`,
`aiohttp-client`, and `requests`.
>   - Tighten excluded span types using `Literal`.
> - **Core**:
> - Guard setting `environment` span attribute in `CallToolComponent`
only if present on `worker`.
> - **Packaging**:
>   - Bump `arcade-serve` to `3.2.1`.
> - Add new dependencies for HTTP client instrumentation and `aiohttp`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
9ab57f3c33d6033ff9ec4c6a40445a85328b169a. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
This commit is contained in:
Sterling Dreyer 2025-12-12 15:30:11 -08:00 committed by GitHub
parent 4d54b28926
commit 069ce70fb2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 15 additions and 4 deletions

View file

@ -73,7 +73,8 @@ class CallToolComponent(WorkerComponent):
current_span.set_attribute("tool_name", str(call_tool_request.tool.name)) current_span.set_attribute("tool_name", str(call_tool_request.tool.name))
current_span.set_attribute("toolkit_version", str(call_tool_request.tool.version)) current_span.set_attribute("toolkit_version", str(call_tool_request.tool.version))
current_span.set_attribute("toolkit_name", str(call_tool_request.tool.toolkit)) current_span.set_attribute("toolkit_name", str(call_tool_request.tool.toolkit))
current_span.set_attribute("environment", self.worker.environment) if hasattr(self.worker, "environment"):
current_span.set_attribute("environment", self.worker.environment)
return await self.worker.call_tool(call_tool_request) return await self.worker.call_tool(call_tool_request)

View file

@ -1,14 +1,17 @@
import logging import logging
import os import os
import urllib.parse import urllib.parse
from typing import Optional from typing import Literal, Optional
from fastapi import FastAPI from fastapi import FastAPI
from opentelemetry import _logs, trace from opentelemetry import _logs, trace
from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter
from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.aiohttp_client import AioHttpClientInstrumentor
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor
from opentelemetry.metrics import Meter, get_meter_provider, set_meter_provider from opentelemetry.metrics import Meter, get_meter_provider, set_meter_provider
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
@ -21,7 +24,7 @@ from opentelemetry.semconv._incubating.attributes import deployment_attributes
from opentelemetry.semconv.attributes import service_attributes from opentelemetry.semconv.attributes import service_attributes
EXCLUDED_URLS = "/worker/health" EXCLUDED_URLS = "/worker/health"
EXCLUDED_SPANS = ["send", "receive"] EXCLUDED_SPANS: list[Literal["send", "receive"]] = ["send", "receive"]
class ShutdownError(Exception): class ShutdownError(Exception):
@ -59,6 +62,9 @@ class OTELHandler:
FastAPIInstrumentor().instrument_app( FastAPIInstrumentor().instrument_app(
app, excluded_urls=EXCLUDED_URLS, exclude_spans=EXCLUDED_SPANS app, excluded_urls=EXCLUDED_URLS, exclude_spans=EXCLUDED_SPANS
) )
HTTPXClientInstrumentor()._instrument(tracer_provider=self._tracer_provider)
AioHttpClientInstrumentor()._instrument(tracer_provider=self._tracer_provider)
RequestsInstrumentor()._instrument(tracer_provider=self._tracer_provider)
def _init_tracer(self) -> None: def _init_tracer(self) -> None:
self._tracer_provider = TracerProvider(resource=self.resource) self._tracer_provider = TracerProvider(resource=self.resource)

View file

@ -1,6 +1,6 @@
[project] [project]
name = "arcade-serve" name = "arcade-serve"
version = "3.2.0" version = "3.2.1"
description = "Arcade Serve - Serving infrastructure for Arcade tools and workers" description = "Arcade Serve - Serving infrastructure for Arcade tools and workers"
readme = "README.md" readme = "README.md"
license = {text = "MIT"} license = {text = "MIT"}
@ -27,6 +27,10 @@ dependencies = [
"opentelemetry-instrumentation-fastapi==0.49b2", "opentelemetry-instrumentation-fastapi==0.49b2",
"opentelemetry-exporter-otlp-proto-http==1.28.2", "opentelemetry-exporter-otlp-proto-http==1.28.2",
"opentelemetry-exporter-otlp-proto-common==1.28.2", "opentelemetry-exporter-otlp-proto-common==1.28.2",
"opentelemetry-instrumentation-httpx==0.49b2",
"opentelemetry-instrumentation-aiohttp-client==0.49b2",
"opentelemetry-instrumentation-requests==0.49b2",
"aiohttp>=3.0.0,<4.0.0",
] ]
[project.optional-dependencies] [project.optional-dependencies]