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("toolkit_version", str(call_tool_request.tool.version))
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)

View file

@ -1,14 +1,17 @@
import logging
import os
import urllib.parse
from typing import Optional
from typing import Literal, Optional
from fastapi import FastAPI
from opentelemetry import _logs, trace
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.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.aiohttp_client import AioHttpClientInstrumentor
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.sdk._logs import LoggerProvider, LoggingHandler
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
EXCLUDED_URLS = "/worker/health"
EXCLUDED_SPANS = ["send", "receive"]
EXCLUDED_SPANS: list[Literal["send", "receive"]] = ["send", "receive"]
class ShutdownError(Exception):
@ -59,6 +62,9 @@ class OTELHandler:
FastAPIInstrumentor().instrument_app(
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:
self._tracer_provider = TracerProvider(resource=self.resource)

View file

@ -1,6 +1,6 @@
[project]
name = "arcade-serve"
version = "3.2.0"
version = "3.2.1"
description = "Arcade Serve - Serving infrastructure for Arcade tools and workers"
readme = "README.md"
license = {text = "MIT"}
@ -27,6 +27,10 @@ dependencies = [
"opentelemetry-instrumentation-fastapi==0.49b2",
"opentelemetry-exporter-otlp-proto-http==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]