-
Notifications
You must be signed in to change notification settings - Fork 54
docs(integration): add OpenSearch integration guide #164
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
maheshbabugorantla
wants to merge
3
commits into
traceloop:main
Choose a base branch
from
maheshbabugorantla:mbg/opensearch-integration-docs
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
77bae00
docs(integration): add OpenSearch integration guide
maheshbabugorantla c635229
docs(integration): add trace visualization screenshot to OpenSearch g…
maheshbabugorantla d5ce291
fix(integration): add alt text to OpenSearch trace visualization scre…
maheshbabugorantla File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,342 @@ | ||
| --- | ||
| title: "LLM Observability with OpenSearch" | ||
| sidebarTitle: "OpenSearch" | ||
| --- | ||
|
|
||
| Connect OpenLLMetry to [OpenSearch](https://opensearch.org/) to visualize LLM traces in OpenSearch Dashboards' Trace Analytics interface. This integration routes traces from your application through an OpenTelemetry Collector to [Data Prepper](https://opensearch.org/docs/latest/data-prepper/), which ingests them into OpenSearch. | ||
|
|
||
| <Note> | ||
| This integration requires an OpenTelemetry Collector and Data Prepper as intermediaries between the Traceloop OpenLLMetry SDK and OpenSearch. | ||
| Data Prepper 2.0+ supports OTLP ingestion natively. | ||
| </Note> | ||
|
|
||
| ## Quick Start | ||
|
|
||
| <Steps> | ||
| <Step title="Install OpenLLMetry"> | ||
| Install the Traceloop SDK alongside your LLM provider client: | ||
|
|
||
| ```bash | ||
| pip install traceloop-sdk openai | ||
| ``` | ||
| </Step> | ||
|
|
||
| <Step title="Configure OpenTelemetry Collector"> | ||
| Configure your OpenTelemetry Collector to receive traces from OpenLLMetry and forward them to Data Prepper. | ||
|
|
||
| Create an `otel-collector-config.yaml` file: | ||
|
|
||
| ```yaml | ||
| receivers: | ||
| otlp: | ||
| protocols: | ||
| http: | ||
| endpoint: localhost:4318 | ||
| grpc: | ||
| endpoint: localhost:4317 | ||
|
|
||
| processors: | ||
| batch: | ||
| timeout: 10s | ||
| send_batch_size: 1024 | ||
|
|
||
| memory_limiter: | ||
| check_interval: 1s | ||
| limit_mib: 512 | ||
|
|
||
| resource: | ||
| attributes: | ||
| - key: service.name | ||
| action: upsert | ||
| value: your-service-name # Match this to app_name parameter value when calling Traceloop.init() | ||
|
|
||
| exporters: | ||
| # Export to Data Prepper via OTLP | ||
| otlp/data-prepper: | ||
| endpoint: http://localhost:21890 | ||
| tls: | ||
| insecure: true # Allow insecure connection from OTEL Collector to Data Prepper (for demo purposes) | ||
|
|
||
| # Logging exporter for debugging (can ignore if not needed) | ||
| logging: | ||
| verbosity: normal | ||
| sampling_initial: 5 | ||
| sampling_thereafter: 200 | ||
|
|
||
| # Debug exporter to verify trace data | ||
| debug: | ||
| verbosity: detailed | ||
| sampling_initial: 10 | ||
| sampling_thereafter: 10 | ||
|
|
||
| extensions: | ||
| health_check: | ||
| endpoint: localhost:13133 | ||
|
|
||
| service: | ||
| extensions: [health_check] | ||
|
|
||
| pipelines: | ||
| traces: | ||
| receivers: [otlp] | ||
| processors: [memory_limiter, batch, resource] | ||
| exporters: [otlp/data-prepper, logging, debug] | ||
|
|
||
| metrics: | ||
| receivers: [otlp] | ||
| processors: [memory_limiter, batch, resource] | ||
| exporters: [logging] | ||
|
|
||
| logs: | ||
| receivers: [otlp] | ||
| processors: [memory_limiter, batch, resource] | ||
| exporters: [logging] | ||
| ``` | ||
|
|
||
| <Warning> | ||
| In production, enable TLS and use authentication between the OpenTelemetry Collector and Data Prepper. | ||
| Set `tls.insecure: false` and configure appropriate certificates. | ||
| </Warning> | ||
| </Step> | ||
|
|
||
| <Step title="Configure Data Prepper"> | ||
| Data Prepper receives traces from the OpenTelemetry Collector, processes them, and writes them to OpenSearch. | ||
|
|
||
| Create a `data-prepper-pipelines.yaml` file: | ||
|
|
||
| ```yaml | ||
| entry-pipeline: | ||
| source: | ||
| otel_trace_source: | ||
| ssl: false | ||
| sink: | ||
| - pipeline: | ||
| name: raw-trace-pipeline | ||
| - pipeline: | ||
| name: service-map-pipeline | ||
|
|
||
| raw-trace-pipeline: | ||
| source: | ||
| pipeline: | ||
| name: entry-pipeline | ||
| processor: | ||
| - otel_traces: | ||
| sink: | ||
| - opensearch: | ||
| hosts: ["https://localhost:9200"] | ||
| index_type: trace-analytics-raw | ||
| username: admin | ||
| password: admin | ||
|
|
||
| service-map-pipeline: | ||
| source: | ||
| pipeline: | ||
| name: entry-pipeline | ||
| processor: | ||
| - service_map: | ||
| sink: | ||
| - opensearch: | ||
| hosts: ["https://localhost:9200"] | ||
| index_type: trace-analytics-service-map | ||
| username: admin | ||
| password: admin | ||
| ``` | ||
|
|
||
| <Note> | ||
| Data Prepper automatically creates the `otel-v1-apm-span` and `otel-v1-apm-service-map` indices in OpenSearch. | ||
| The `entry-pipeline` listens on port 21890 by default for OTLP gRPC traffic. | ||
| </Note> | ||
| </Step> | ||
|
|
||
| <Step title="Initialize Traceloop"> | ||
| Import and initialize Traceloop before any LLM imports: | ||
|
|
||
| ```python | ||
| from os import getenv | ||
|
|
||
| from traceloop.sdk import Traceloop | ||
| from openai import OpenAI | ||
|
|
||
| # Initialize Traceloop with OTLP endpoint | ||
| Traceloop.init( | ||
| app_name="your-service-name", | ||
| api_endpoint="http://localhost:4318" | ||
| ) | ||
|
|
||
| # Traceloop must be initialized before importing the LLM client | ||
| # Traceloop instruments the OpenAI client automatically | ||
| client = OpenAI(api_key=getenv("OPENAI_API_KEY")) | ||
|
|
||
| # Make LLM calls - automatically traced | ||
| response = client.chat.completions.create( | ||
| model="gpt-4o-mini", | ||
| messages=[{"role": "user", "content": "Hello!"}] | ||
| ) | ||
| ``` | ||
|
|
||
| <Note> | ||
| The `app_name` parameter sets the service name visible in OpenSearch Dashboards' Trace Analytics. | ||
| </Note> | ||
| </Step> | ||
|
|
||
| <Step title="View Traces in OpenSearch Dashboards"> | ||
| Navigate to OpenSearch Dashboards to explore your LLM traces: | ||
|
|
||
| 1. Open OpenSearch Dashboards at `http://localhost:5601` | ||
| 2. Go to **Observability → Trace Analytics → Traces** | ||
| 3. Click on a trace to view the full span waterfall | ||
| 4. Inspect individual spans for LLM metadata | ||
|
|
||
| Each LLM call appears as a span containing: | ||
| - Model name (`gen_ai.request.model`) | ||
| - Token usage (`gen_ai.usage.input_tokens`, `gen_ai.usage.output_tokens`) | ||
| - Prompts and completions (configurable) | ||
| - Request duration and latency | ||
| </Step> | ||
| </Steps> | ||
|
|
||
| ## Environment Variables | ||
|
|
||
| Configure OpenLLMetry behavior using environment variables: | ||
|
|
||
| | Variable | Description | Default | | ||
| |----------|-------------|---------| | ||
| | `TRACELOOP_BASE_URL` | OpenTelemetry Collector endpoint | `http://localhost:4318` | | ||
| | `TRACELOOP_TRACE_CONTENT` | Capture prompts/completions | `true` | | ||
|
|
||
|
|
||
| <Warning> | ||
| Set `TRACELOOP_TRACE_CONTENT=false` in production to prevent logging sensitive prompt content. | ||
| </Warning> | ||
|
|
||
| ## Using Workflow Decorators | ||
|
|
||
| For complex applications with multiple steps, use workflow decorators to create hierarchical traces: | ||
|
|
||
| ```python | ||
| from os import getenv | ||
| from traceloop.sdk import Traceloop | ||
| from traceloop.sdk.decorators import workflow, task | ||
| from openai import OpenAI | ||
|
|
||
| Traceloop.init( | ||
| app_name="recipe-service", | ||
| api_endpoint="http://localhost:4318", | ||
| ) | ||
|
|
||
| # Traceloop must be initialized before importing the LLM client | ||
| # Traceloop instruments the OpenAI client automatically | ||
| client = OpenAI(api_key=getenv("OPENAI_API_KEY")) | ||
|
|
||
| @task(name="generate_recipe") | ||
| def generate_recipe(dish: str): | ||
| """LLM call - creates a child span""" | ||
| response = client.chat.completions.create( | ||
| model="gpt-4o-mini", | ||
| messages=[ | ||
| {"role": "system", "content": "You are a chef."}, | ||
| {"role": "user", "content": f"Recipe for {dish}"} | ||
| ] | ||
| ) | ||
| return response.choices[0].message.content | ||
|
|
||
|
|
||
| @workflow(name="recipe_workflow") | ||
| def create_recipe(dish: str, servings: int): | ||
| """Parent workflow - creates the root transaction""" | ||
| recipe = generate_recipe(dish) | ||
| return {"recipe": recipe, "servings": servings} | ||
|
|
||
| # Call the workflow | ||
| result = create_recipe("pasta carbonara", 4) | ||
| ``` | ||
|
|
||
| In OpenSearch Dashboards' Trace Analytics, you'll see: | ||
| - `recipe_workflow.workflow` as the parent trace | ||
| - `generate_recipe.task` as a child span | ||
| - `openai.chat.completions` as the LLM API span with full metadata | ||
|
|
||
| ## Example Trace Visualization | ||
|
|
||
| <Frame> | ||
| <img src="/img/integrations/opensearch-trace-details.png" alt="OpenSearch Dashboards trace detail waterfall showing parent and child LLM spans" /> | ||
| </Frame> | ||
|
|
||
| ## Captured Metadata | ||
|
|
||
| OpenLLMetry automatically captures these attributes in each LLM span: | ||
|
|
||
| **Request Attributes:** | ||
| - `gen_ai.request.model` - Model identifier | ||
| - `gen_ai.request.temperature` - Sampling temperature | ||
| - `gen_ai.system` - Provider name (OpenAI, Anthropic, etc.) | ||
|
|
||
| **Response Attributes:** | ||
| - `gen_ai.response.model` - Actual model used | ||
| - `gen_ai.response.id` - Unique response identifier | ||
| - `gen_ai.response.finish_reason` - Completion reason | ||
|
|
||
| **Token Usage:** | ||
| - `gen_ai.usage.input_tokens` - Input token count | ||
| - `gen_ai.usage.output_tokens` - Output token count | ||
| - `llm.usage.total_tokens` - Total tokens | ||
|
|
||
| **Content (if enabled):** | ||
| - `gen_ai.prompt.{N}.content` - Prompt messages | ||
| - `gen_ai.completion.{N}.content` - Generated completions | ||
|
|
||
| ## Production Considerations | ||
|
|
||
| <Tabs> | ||
| <Tab title="Content Logging"> | ||
| Disable prompt/completion logging in production: | ||
|
|
||
| ```bash | ||
| export TRACELOOP_TRACE_CONTENT=false | ||
| ``` | ||
|
|
||
| This prevents sensitive data from being stored in OpenSearch. | ||
| </Tab> | ||
|
|
||
| <Tab title="Sampling"> | ||
| Configure sampling in the OpenTelemetry Collector to reduce trace volume: | ||
|
|
||
| ```yaml | ||
| processors: | ||
| probabilistic_sampler: | ||
| sampling_percentage: 10 # Sample 10% of traces | ||
| ``` | ||
| </Tab> | ||
|
|
||
| <Tab title="Security"> | ||
| Enable TLS and authentication for Data Prepper and OpenSearch: | ||
|
|
||
| **Data Prepper TLS:** | ||
|
|
||
| ```yaml | ||
| entry-pipeline: | ||
| source: | ||
| otel_trace_source: | ||
| ssl: true | ||
| sslKeyCertChainFile: "/path/to/cert.pem" | ||
| sslKeyFile: "/path/to/key.pem" | ||
| ``` | ||
|
|
||
| **OpenSearch Authentication:** | ||
|
|
||
| ```yaml | ||
| sink: | ||
| - opensearch: | ||
| hosts: ["https://opensearch-node:9200"] | ||
| username: "${OPENSEARCH_USERNAME}" | ||
| password: "${OPENSEARCH_PASSWORD}" | ||
| ``` | ||
| </Tab> | ||
| </Tabs> | ||
|
|
||
| ## Resources | ||
|
|
||
| - [OpenSearch Documentation](https://opensearch.org/docs/latest/) | ||
| - [Data Prepper Documentation](https://opensearch.org/docs/latest/data-prepper/) | ||
| - [OpenTelemetry Collector Configuration](https://opentelemetry.io/docs/collector/configuration/) | ||
| - [Traceloop SDK Configuration](https://www.traceloop.com/docs/openllmetry/configuration) | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix contradictory initialization guidance vs example code order.
The page says to initialize Traceloop before LLM imports, but both examples import
OpenAIbeforeTraceloop.init(). This can mislead users and break auto-instrumentation expectations.Suggested doc fix
Also applies to: 227-229
🤖 Prompt for AI Agents