Monitor Claude using OpenTelemetry
Shield360 uses OpenTelemetry instrumentation to help you monitor LLM applications built using Anthropic models. This includes tracking performance, token usage, costs, and how users interact with the application.
The integration is compatible with:
- Anthropic Python SDK client
>= 0.21.0 - Anthropic Typescript SDK client
>= 0.21.0 - Anthropic Go SDK, via Shield360’s
InstrumentedClientwrapper
Get started
Section titled “Get started”Auto-instrumentation means you don’t have to set up monitoring manually for different LLMs, frameworks, or databases. By simply adding Shield360 in your application, all the necessary monitoring configurations are automatically set up.
Open your command line or terminal and run:
pip install shield360npm install shield360Perfect for existing applications - no code modifications needed:
# Configure via CLI argumentsshield360-instrument \ --service-name my-ai-app \ --environment production \ --otlp-endpoint YOUR_OTEL_ENDPOINT \ python your_app.py# Configure via environment variablesexport OTEL_SERVICE_NAME=my-ai-appexport OTEL_DEPLOYMENT_ENVIRONMENT=productionexport OTEL_EXPORTER_OTLP_ENDPOINT=YOUR_OTEL_ENDPOINT
# Run with zero code changesshield360-instrument python your_app.pyimport shield360
shield360.init(otlp_endpoint="YOUR_OTEL_ENDPOINT")Add the following two lines to your application code:
import shield360
shield360.init()Then, configure the your OTLP endpoint using environment variable:
export OTEL_EXPORTER_OTLP_ENDPOINT=YOUR_OTEL_ENDPOINTimport shield360 from "shield360"
shield360.init({ otlpEndpoint: "YOUR_OTEL_ENDPOINT" })Add the following two lines to your application code:
import shield360 from "shield360"
shield360.init()Then, configure the your OTLP endpoint using environment variable:
export OTEL_EXPORTER_OTLP_ENDPOINT=YOUR_OTEL_ENDPOINTReplace: YOUR_OTEL_ENDPOINT with the URL of your OpenTelemetry backend, such as http://127.0.0.1:4318 if you are using Shield360 and a local OTel Collector.
To send metrics and traces to other Observability tools, refer to the supported destinations.
For more advanced configurations and application use cases, visit the SDK configuration reference or TypeScript SDK reference.
The Go SDK wraps your Anthropic client with an InstrumentedClient that automatically emits traces and metrics for every API call - with zero changes to your application logic.
The integration supports:
- Messages (standard and streaming)
- Prompt caching token tracking (
cache_creation_input_tokens,cache_read_input_tokens) - Tool use
Open your terminal and run:
go get github.com/ThinkfleetAI/shield360-gogo get github.com/ThinkfleetAI/shield360-go@v1.2.3Replace v1.2.3 with the version you want to install.
Add this once at the start of your application (e.g. in main()):
import ( "context" shield360 "github.com/ThinkfleetAI/shield360-go")
if err := shield360.Init(shield360.Config{ OtlpEndpoint: "YOUR_OTEL_ENDPOINT", ApplicationName: "my-ai-app", Environment: "production",}); err != nil { log.Fatal(err)}defer shield360.Shutdown(context.Background())import ( "context" shield360 "github.com/ThinkfleetAI/shield360-go")
if err := shield360.Init(shield360.Config{ ApplicationName: "my-ai-app",}); err != nil { log.Fatal(err)}defer shield360.Shutdown(context.Background())Then set your OTLP endpoint via environment variable:
export OTEL_EXPORTER_OTLP_ENDPOINT=YOUR_OTEL_ENDPOINTReplace YOUR_OTEL_ENDPOINT with the URL of your OpenTelemetry backend, such as http://127.0.0.1:4318 for a local Shield360 deployment.
Replace your existing Anthropic client creation with the Shield360 instrumented client:
import "github.com/ThinkfleetAI/shield360-go/instrumentation/anthropic"
client := anthropic.NewClient("your-anthropic-api-key")Optional configuration:
// Custom API version or base URLclient := anthropic.NewClient("your-api-key", anthropic.WithAPIVersion("2023-06-01"), anthropic.WithBaseURL("https://api.anthropic.com/v1"),)Use the instrumented client exactly as you would a normal Anthropic client:
Message:
resp, err := client.CreateMessage(ctx, anthropic.MessageRequest{ Model: "claude-opus-4-5", MaxTokens: 256, System: "You are a helpful assistant.", Messages: []anthropic.Message{ {Role: "user", Content: "What is OpenTelemetry?"}, },})if err != nil { return err}
for _, block := range resp.Content { if block.Type == "text" { fmt.Println(block.Text) }}Streaming:
stream, err := client.CreateMessageStream(ctx, anthropic.MessageRequest{ Model: "claude-opus-4-5", MaxTokens: 1024, Messages: []anthropic.Message{ {Role: "user", Content: "Tell me a story."}, },})if err != nil { return err}defer stream.Close()
for { event, err := stream.Recv() if err == io.EOF { break } if err != nil { return err } if event.Type == "content_block_delta" && event.Delta != nil { fmt.Print(event.Delta.Text) }}Tool use:
resp, err := client.CreateMessage(ctx, anthropic.MessageRequest{ Model: "claude-opus-4-5", MaxTokens: 512, Messages: []anthropic.Message{ {Role: "user", Content: "What is the weather in San Francisco?"}, }, Tools: []anthropic.Tool{ { Name: "get_weather", Description: "Get the current weather in a location", InputSchema: map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "location": map[string]interface{}{ "type": "string", "description": "The city and state", }, }, "required": []string{"location"}, }, }, },})What gets collected
Section titled “What gets collected”Every call to the instrumented client automatically records:
| Data | Attribute |
|---|---|
| Operation name | gen_ai.operation.name |
| Model requested | gen_ai.request.model |
| Model used | gen_ai.response.model |
| Response ID | gen_ai.response.id |
| Input tokens | gen_ai.usage.input_tokens |
| Output tokens | gen_ai.usage.output_tokens |
| Cache creation tokens | gen_ai.usage.prompt_tokens_details.cache_write |
| Cache read tokens | gen_ai.usage.prompt_tokens_details.cache_read |
| Estimated cost | gen_ai.usage.cost |
| Finish reason | gen_ai.response.finish_reasons |
| Tool calls | gen_ai.tool.name, gen_ai.tool.call.id |
| Time to first token | gen_ai.server.time_to_first_token (streaming) |
| Time per output token | gen_ai.server.time_per_output_token (streaming) |
Metrics emitted:
gen_ai.client.token.usage- token usage histogram (input/output)gen_ai.client.operation.duration- total operation durationgen_ai.server.time_to_first_token- TTFT for streaminggen_ai.client.operation.time_to_first_chunk- client-side TTFTgen_ai.client.operation.time_per_output_chunk- per-chunk latencygen_ai.server.request.duration- estimated server processing time