Skip to content

Commit afa7010

Browse files
committed
feat: improve OpenTelemetry config and warnings
1 parent b71ef1e commit afa7010

File tree

4 files changed

+127
-4
lines changed

4 files changed

+127
-4
lines changed

examples/configs/tracing/README.md

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Tracing helps you understand what happens inside your guardrails:
1818
The fastest way to see tracing in action:
1919

2020
```bash
21+
# Install tracing support with SDK (needed for examples)
2122
pip install nemoguardrails[tracing] opentelemetry-sdk
2223

2324
cd examples/configs/tracing/
@@ -109,11 +110,35 @@ This means you must configure OpenTelemetry in your application code.
109110

110111
### Installation
111112

113+
#### For Tracing Support (API only)
114+
115+
```bash
116+
# minimum requirement for NeMo Guardrails tracing features
117+
pip install nemoguardrails[tracing]
118+
```
119+
120+
This installs only the OpenTelemetry API, which is sufficient if your application already configures OpenTelemetry.
121+
122+
#### For Running Examples and Development
123+
124+
```bash
125+
# includes OpenTelemetry SDK for configuring exporters
126+
pip install nemoguardrails[tracing] opentelemetry-sdk
127+
```
128+
129+
#### For Production Deployments
130+
112131
```bash
132+
# install tracing support
113133
pip install nemoguardrails[tracing]
114134

115-
# install OpenTelemetry SDK and exporters (required for your application)
116-
pip install opentelemetry-sdk
135+
# install SDK and your preferred exporter
136+
# for TLP
137+
pip install opentelemetry-sdk opentelemetry-exporter-otlp
138+
# OR for Jaeger
139+
pip install opentelemetry-sdk opentelemetry-exporter-jaeger
140+
# OR for Zipkin
141+
pip install opentelemetry-sdk opentelemetry-exporter-zipkin
117142
```
118143

119144
### Configuration Examples

examples/configs/tracing/working_example.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,18 @@ def create_guardrails_config():
8888
}
8989
],
9090
"tracing": {"enabled": True, "adapters": [{"name": "OpenTelemetry"}]},
91+
# Note: The following old-style configuration is deprecated and will be ignored:
92+
# "tracing": {
93+
# "enabled": True,
94+
# "adapters": [{
95+
# "name": "OpenTelemetry",
96+
# "service_name": "my-service", # DEPRECATED - configure in Resource
97+
# "exporter": "console", # DEPRECATED - configure SDK
98+
# "resource_attributes": { # DEPRECATED - configure in Resource
99+
# "env": "production"
100+
# }
101+
# }]
102+
# }
91103
},
92104
)
93105

nemoguardrails/tracing/adapters/opentelemetry.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,15 @@
5353

5454
from __future__ import annotations
5555

56+
import warnings
5657
from importlib.metadata import version
57-
from typing import TYPE_CHECKING, Optional
58+
from typing import TYPE_CHECKING
5859

5960
if TYPE_CHECKING:
6061
from nemoguardrails.tracing import InteractionLog
6162
try:
6263
from opentelemetry import trace
64+
from opentelemetry.trace import NoOpTracerProvider
6365

6466
except ImportError:
6567
raise ImportError(
@@ -97,6 +99,37 @@ def __init__(
9799
Applications must configure the OpenTelemetry SDK before using this adapter.
98100
The adapter will use the globally configured tracer provider.
99101
"""
102+
# check for deprecated parameters and warn users
103+
deprecated_params = [
104+
"exporter",
105+
"exporter_cls",
106+
"resource_attributes",
107+
"span_processor",
108+
]
109+
used_deprecated = [param for param in deprecated_params if param in kwargs]
110+
111+
if used_deprecated:
112+
warnings.warn(
113+
f"OpenTelemetry configuration parameters {used_deprecated} in YAML/config are deprecated "
114+
"and will be ignored. Please configure OpenTelemetry in your application code. "
115+
"See the migration guide at: "
116+
"https://github.com/NVIDIA/NeMo-Guardrails/blob/main/examples/configs/tracing/README.md#migration-guide",
117+
DeprecationWarning,
118+
stacklevel=2,
119+
)
120+
121+
# validate that OpenTelemetry is properly configured
122+
provider = trace.get_tracer_provider()
123+
if provider is None or isinstance(provider, NoOpTracerProvider):
124+
warnings.warn(
125+
"No OpenTelemetry TracerProvider configured. Traces will not be exported. "
126+
"Please configure OpenTelemetry in your application code before using NeMo Guardrails. "
127+
"See setup guide at: "
128+
"https://github.com/NVIDIA/NeMo-Guardrails/blob/main/examples/configs/tracing/README.md#opentelemetry-setup",
129+
UserWarning,
130+
stacklevel=2,
131+
)
132+
100133
self.tracer = trace.get_tracer(
101134
service_name,
102135
instrumenting_library_version=version("nemoguardrails"),

tests/test_tracing_adapters_opentelemetry.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@
1515

1616
import asyncio
1717
import unittest
18+
import warnings
1819
from importlib.metadata import version
1920
from unittest.mock import MagicMock, patch
2021

2122
# TODO: check to see if we can add it as a dependency
2223
# but now we try to import opentelemetry and set a flag if it's not available
2324
try:
24-
from opentelemetry import trace
2525
from opentelemetry.sdk.trace import TracerProvider
26+
from opentelemetry.trace import NoOpTracerProvider
2627

2728
from nemoguardrails.tracing.adapters.opentelemetry import OpenTelemetryAdapter
2829

@@ -288,3 +289,55 @@ def test_backward_compatibility_with_old_config(self):
288289
# Should still create the adapter successfully
289290
self.assertIsInstance(adapter, OpenTelemetryAdapter)
290291
self.assertEqual(adapter.tracer, self.mock_tracer)
292+
293+
def test_deprecation_warning_for_old_parameters(self):
294+
"""Test that deprecation warnings are raised for old configuration parameters."""
295+
296+
with warnings.catch_warnings(record=True) as w:
297+
warnings.simplefilter("always")
298+
299+
# adapter with deprecated parameters
300+
_adapter = OpenTelemetryAdapter(
301+
service_name="test_service",
302+
exporter="console",
303+
resource_attributes={"test": "value"},
304+
span_processor=MagicMock(),
305+
)
306+
307+
# deprecation warning is issued
308+
self.assertEqual(len(w), 1)
309+
self.assertTrue(issubclass(w[0].category, DeprecationWarning))
310+
self.assertIn("deprecated", str(w[0].message))
311+
self.assertIn("exporter", str(w[0].message))
312+
self.assertIn("resource_attributes", str(w[0].message))
313+
self.assertIn("span_processor", str(w[0].message))
314+
315+
def test_no_op_tracer_provider_warning(self):
316+
"""Test that a warning is issued when NoOpTracerProvider is detected."""
317+
318+
with patch("opentelemetry.trace.get_tracer_provider") as mock_get_provider:
319+
mock_get_provider.return_value = NoOpTracerProvider()
320+
321+
with warnings.catch_warnings(record=True) as w:
322+
warnings.simplefilter("always")
323+
324+
_adapter = OpenTelemetryAdapter()
325+
326+
self.assertEqual(len(w), 1)
327+
self.assertTrue(issubclass(w[0].category, UserWarning))
328+
self.assertIn(
329+
"No OpenTelemetry TracerProvider configured", str(w[0].message)
330+
)
331+
self.assertIn("Traces will not be exported", str(w[0].message))
332+
333+
def test_no_warnings_with_proper_configuration(self):
334+
"""Test that no warnings are issued when properly configured."""
335+
336+
with warnings.catch_warnings(record=True) as w:
337+
warnings.simplefilter("always")
338+
339+
# adapter without deprecated parameters
340+
_adapter = OpenTelemetryAdapter(service_name="test_service")
341+
342+
# no warnings is issued
343+
self.assertEqual(len(w), 0)

0 commit comments

Comments
 (0)