Skip to content

Commit 13e6fb5

Browse files
authored
Merge pull request #65 from ks6088ts-labs/feature/issue-64_otel
support otel
2 parents 0595310 + 224562c commit 13e6fb5

File tree

6 files changed

+251
-0
lines changed

6 files changed

+251
-0
lines changed

.env.template

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,7 @@ CSV_LOADER_DATA_DIR_PATH="./data"
5959

6060
## PDF Loader Settings
6161
PDF_LOADER_DATA_DIR_PATH="./data"
62+
63+
## OpenTelemetry Settings
64+
OTEL_SERVICE_NAME="template-langgraph"
65+
OTEL_COLLECTOR_ENDPOINT="http://localhost:4317"

docker-compose.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,19 @@ services:
3131
POSTGRES_PASSWORD: password
3232
POSTGRES_DB: db
3333
restart: always
34+
jaeger:
35+
image: jaegertracing/all-in-one:1.72.0
36+
container_name: jaeger
37+
environment:
38+
- SPAN_STORAGE_TYPE=elasticsearch
39+
- ES_SERVER_URLS=http://elasticsearch:9200
40+
- LOG_LEVEL=debug
41+
ports:
42+
- "16686:16686"
43+
- "4317:4317"
44+
- "4318:4318"
45+
- "5778:5778"
46+
- "9411:9411"
47+
depends_on:
48+
- elasticsearch
49+
restart: unless-stopped

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ dependencies = [
1919
"langgraph>=0.6.2",
2020
"langgraph-supervisor>=0.0.29",
2121
"openai>=1.98.0",
22+
"opentelemetry-api>=1.36.0",
23+
"opentelemetry-exporter-otlp>=1.36.0",
24+
"opentelemetry-sdk>=1.36.0",
2225
"psycopg2-binary>=2.9.10",
2326
"pydantic-settings>=2.9.1",
2427
"pypdf>=5.9.0",

scripts/otel_operator.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import logging
2+
import time
3+
4+
import typer
5+
from dotenv import load_dotenv
6+
7+
from template_langgraph.loggers import get_logger
8+
from template_langgraph.utilities.otel_helpers import OtelWrapper
9+
10+
# Initialize the Typer application
11+
app = typer.Typer(
12+
add_completion=False,
13+
help="OTEL operator CLI",
14+
)
15+
16+
# Set up logging
17+
logger = get_logger(__name__)
18+
19+
20+
@app.command()
21+
def run(
22+
query: str = typer.Option(
23+
"What is the weather like today?",
24+
"--query",
25+
"-q",
26+
help="Query to run against the Ollama model",
27+
),
28+
verbose: bool = typer.Option(
29+
False,
30+
"--verbose",
31+
"-v",
32+
help="Enable verbose output",
33+
),
34+
):
35+
# Set up logging
36+
if verbose:
37+
logger.setLevel(logging.DEBUG)
38+
otel_wrapper = OtelWrapper()
39+
otel_wrapper.initialize()
40+
41+
logger.info("Running...")
42+
tracer = otel_wrapper.get_tracer(name=__name__)
43+
with tracer.start_as_current_span("otel_operator_run"):
44+
logger.info(f"Query: {query}")
45+
# Simulate some work
46+
response = {"content": "It's sunny!"}
47+
time.sleep(1) # Simulate processing time
48+
logger.info(f"Response: {response['content']}")
49+
50+
51+
if __name__ == "__main__":
52+
load_dotenv(
53+
override=True,
54+
verbose=True,
55+
)
56+
app()
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from functools import lru_cache
2+
3+
from opentelemetry import trace
4+
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
5+
from opentelemetry.sdk.resources import Resource
6+
from opentelemetry.sdk.trace import TracerProvider
7+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
8+
from pydantic_settings import BaseSettings, SettingsConfigDict
9+
10+
11+
class Settings(BaseSettings):
12+
otel_service_name: str = "<OTEL_SERVICE_NAME>"
13+
otel_collector_endpoint: str = "<OTEL_COLLECTOR_ENDPOINT>"
14+
15+
model_config = SettingsConfigDict(
16+
env_file=".env",
17+
env_ignore_empty=True,
18+
extra="ignore",
19+
)
20+
21+
22+
@lru_cache
23+
def get_otel_settings() -> Settings:
24+
"""Get OpenTelemetry settings."""
25+
return Settings()
26+
27+
28+
class OtelWrapper:
29+
def __init__(
30+
self,
31+
settings: Settings = None,
32+
):
33+
if settings is None:
34+
settings = get_otel_settings()
35+
self.settings = settings
36+
37+
def initialize(self):
38+
provider = TracerProvider(
39+
resource=Resource(
40+
attributes={
41+
"service.name": self.settings.otel_service_name,
42+
}
43+
)
44+
)
45+
otlp_exporter = OTLPSpanExporter(
46+
endpoint=self.settings.otel_collector_endpoint,
47+
)
48+
provider.add_span_processor(
49+
span_processor=BatchSpanProcessor(otlp_exporter),
50+
)
51+
trace.set_tracer_provider(provider)
52+
53+
def get_tracer(self, name: str):
54+
return trace.get_tracer(name)

uv.lock

Lines changed: 118 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)