Skip to content

Commit 69a79c9

Browse files
Add scope and domain override support for observability
Co-authored-by: sergioescalera <8428450+sergioescalera@users.noreply.github.com>
1 parent f8a9e6d commit 69a79c9

File tree

4 files changed

+110
-3
lines changed

4 files changed

+110
-3
lines changed

libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/exporters/agent365_exporter.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import json
88
import logging
9+
import os
910
import threading
1011
import time
1112
from collections.abc import Callable, Sequence
@@ -86,8 +87,12 @@ def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
8687
body = json.dumps(payload, separators=(",", ":"), ensure_ascii=False)
8788

8889
# Resolve endpoint + token
89-
discovery = PowerPlatformApiDiscovery(self._cluster_category)
90-
endpoint = discovery.get_tenant_island_cluster_endpoint(tenant_id)
90+
domain_override = os.getenv("A365_OBSERVABILITY_DOMAIN_OVERRIDE")
91+
if domain_override:
92+
endpoint = domain_override
93+
else:
94+
discovery = PowerPlatformApiDiscovery(self._cluster_category)
95+
endpoint = discovery.get_tenant_island_cluster_endpoint(tenant_id)
9196
endpoint_path = (
9297
f"/maven/agent365/service/agents/{agent_id}/traces"
9398
if self._use_s2s_endpoint

libraries/microsoft-agents-a365-runtime/microsoft_agents_a365/runtime/environment_utils.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,14 @@ def get_observability_authentication_scope() -> list[str]:
2121
"""
2222
Returns the scope for authenticating to the observability service based on the current environment.
2323
24+
The scope can be overridden via the A365_OBSERVABILITY_SCOPE_OVERRIDE environment variable
25+
to enable testing against pre-production environments.
26+
2427
Returns:
2528
list[str]: The authentication scope for the current environment.
2629
"""
27-
return [PROD_OBSERVABILITY_SCOPE]
30+
override_scope = os.getenv("A365_OBSERVABILITY_SCOPE_OVERRIDE")
31+
return [override_scope] if override_scope else [PROD_OBSERVABILITY_SCOPE]
2832

2933

3034
def is_development_environment() -> bool:

tests/observability/core/test_agent365_exporter.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,87 @@ def test_exporter_is_internal(self):
384384
"Exporter class should be prefixed with underscore to indicate it's private/internal",
385385
)
386386

387+
def test_export_uses_domain_override_when_env_var_set(self):
388+
"""Test that domain override is used when A365_OBSERVABILITY_DOMAIN_OVERRIDE is set."""
389+
# Arrange
390+
override_domain = "override.example.com"
391+
import os
392+
393+
os.environ["A365_OBSERVABILITY_DOMAIN_OVERRIDE"] = override_domain
394+
395+
try:
396+
spans = [self._create_mock_span("override_test_span")]
397+
398+
# Mock the PowerPlatformApiDiscovery class (should not be called when override is set)
399+
with patch(
400+
"microsoft_agents_a365.observability.core.exporters.agent365_exporter.PowerPlatformApiDiscovery"
401+
) as mock_discovery_class:
402+
# Mock the _post_with_retries method
403+
with patch.object(
404+
self.exporter, "_post_with_retries", return_value=True
405+
) as mock_post:
406+
# Act
407+
result = self.exporter.export(spans)
408+
409+
# Assert
410+
self.assertEqual(result, SpanExportResult.SUCCESS)
411+
mock_post.assert_called_once()
412+
413+
# Verify the call arguments - should use override domain
414+
args, kwargs = mock_post.call_args
415+
url, body, headers = args
416+
417+
self.assertIn(override_domain, url)
418+
self.assertIn("/maven/agent365/agents/test-agent-456/traces", url)
419+
420+
# Verify PowerPlatformApiDiscovery was not instantiated
421+
mock_discovery_class.assert_not_called()
422+
423+
finally:
424+
# Cleanup
425+
del os.environ["A365_OBSERVABILITY_DOMAIN_OVERRIDE"]
426+
427+
def test_export_uses_default_domain_when_no_override(self):
428+
"""Test that default domain resolution is used when no override is set."""
429+
# Arrange
430+
import os
431+
432+
# Ensure override is not set
433+
if "A365_OBSERVABILITY_DOMAIN_OVERRIDE" in os.environ:
434+
del os.environ["A365_OBSERVABILITY_DOMAIN_OVERRIDE"]
435+
436+
spans = [self._create_mock_span("default_domain_span")]
437+
438+
# Mock the PowerPlatformApiDiscovery class
439+
with patch(
440+
"microsoft_agents_a365.observability.core.exporters.agent365_exporter.PowerPlatformApiDiscovery"
441+
) as mock_discovery_class:
442+
mock_discovery = Mock()
443+
mock_discovery.get_tenant_island_cluster_endpoint.return_value = "default-endpoint.com"
444+
mock_discovery_class.return_value = mock_discovery
445+
446+
# Mock the _post_with_retries method
447+
with patch.object(self.exporter, "_post_with_retries", return_value=True) as mock_post:
448+
# Act
449+
result = self.exporter.export(spans)
450+
451+
# Assert
452+
self.assertEqual(result, SpanExportResult.SUCCESS)
453+
mock_post.assert_called_once()
454+
455+
# Verify the call arguments - should use default domain
456+
args, kwargs = mock_post.call_args
457+
url, body, headers = args
458+
459+
self.assertIn("default-endpoint.com", url)
460+
self.assertIn("/maven/agent365/agents/test-agent-456/traces", url)
461+
462+
# Verify PowerPlatformApiDiscovery was called
463+
mock_discovery_class.assert_called_once_with("test")
464+
mock_discovery.get_tenant_island_cluster_endpoint.assert_called_once_with(
465+
"test-tenant-123"
466+
)
467+
387468

388469
if __name__ == "__main__":
389470
unittest.main()

tests/runtime/test_environment_utils.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,23 @@ def test_get_observability_authentication_scope():
1818
assert result == [PROD_OBSERVABILITY_SCOPE]
1919

2020

21+
def test_get_observability_authentication_scope_with_override(monkeypatch):
22+
"""Test get_observability_authentication_scope returns override when env var is set."""
23+
override_scope = "https://override.example.com/.default"
24+
monkeypatch.setenv("A365_OBSERVABILITY_SCOPE_OVERRIDE", override_scope)
25+
26+
result = get_observability_authentication_scope()
27+
assert result == [override_scope]
28+
29+
30+
def test_get_observability_authentication_scope_without_override(monkeypatch):
31+
"""Test get_observability_authentication_scope returns default when env var is not set."""
32+
monkeypatch.delenv("A365_OBSERVABILITY_SCOPE_OVERRIDE", raising=False)
33+
34+
result = get_observability_authentication_scope()
35+
assert result == [PROD_OBSERVABILITY_SCOPE]
36+
37+
2138
@pytest.mark.parametrize(
2239
"env_value,expected",
2340
[

0 commit comments

Comments
 (0)