Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { PowerPlatformApiDiscovery, ClusterCategory } from '@microsoft/agents-a3
import { partitionByIdentity, parseIdentityKey, hexTraceId, hexSpanId, kindName, statusName } from './utils';
import logger, { formatError } from '../../utils/logging';
import { Agent365ExporterOptions } from './Agent365ExporterOptions';
import { useCustomDomainForObservability, resolveAgent365Endpoint } from '../util';
import { useCustomDomainForObservability, resolveAgent365Endpoint, getAgent365ObservabilityDomainOverride } from '../util';

const DEFAULT_HTTP_TIMEOUT_SECONDS = 30000; // 30 seconds in ms
const DEFAULT_MAX_RETRIES = 3;
Expand Down Expand Up @@ -144,25 +144,26 @@ export class Agent365Exporter implements SpanExporter {

const payload = this.buildExportRequest(spans);
const body = JSON.stringify(payload);

const usingCustomServiceEndpoint = useCustomDomainForObservability();

// Select endpoint path based on S2S flag
const endpointPath =
const endpointRelativePath =
this.options.useS2SEndpoint
? `/maven/agent365/service/agents/${agentId}/traces`
: `/maven/agent365/agents/${agentId}/traces`;

let url: string;
if (usingCustomServiceEndpoint) {
const domainOverride = getAgent365ObservabilityDomainOverride();
if (domainOverride) {
url = `${domainOverride}${endpointRelativePath}?api-version=1`;
Comment thread
fpfp100 marked this conversation as resolved.
} else if (usingCustomServiceEndpoint) {
const base = resolveAgent365Endpoint(this.options.clusterCategory as ClusterCategory);
url = `${base}${endpointPath}?api-version=1`;
url = `${base}${endpointRelativePath}?api-version=1`;
logger.info(`[Agent365Exporter] Using custom domain endpoint: ${url}`);
} else {
// Default behavior: discover PPAPI gateway endpoint per-tenant
const discovery = new PowerPlatformApiDiscovery(this.options.clusterCategory as ClusterCategory);
const endpoint = discovery.getTenantIslandClusterEndpoint(tenantId);
url = `https://${endpoint}${endpointPath}?api-version=1`;
url = `https://${endpoint}${endpointRelativePath}?api-version=1`;
logger.info(`[Agent365Exporter] Resolved endpoint: ${url}`);
}

Expand Down
28 changes: 16 additions & 12 deletions packages/agents-a365-observability/src/tracing/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,27 @@ export const useCustomDomainForObservability = (): boolean => {

/**
* Resolve the Agent365 service endpoint base URI for a given cluster category.
*
* By default this returns the production Agent365 endpoint. Internal development
* and test clusters can override this by setting the
* `A365_OBSERVABILITY_DOMAIN_OVERRIDE` environment variable. When set to a
* non-empty value, that value is used as the base URI regardless of cluster category.
*/
export function resolveAgent365Endpoint(clusterCategory: ClusterCategory): string {
const override = process.env.A365_OBSERVABILITY_DOMAIN_OVERRIDE;

if (override && override.trim().length > 0) {
// Normalize to avoid double slashes when concatenating paths
return override.trim().replace(/\/+$/, '');
}

switch (clusterCategory) {
Comment thread
fpfp100 marked this conversation as resolved.
Outdated
case 'prod':
default:
return 'https://agent365.svc.cloud.microsoft';
}
}

/**
* Get Agent365 Observability domain override.
* Internal development and test clusters can override this by setting the
* `A365_OBSERVABILITY_DOMAIN_OVERRIDE` environment variable. When set to a
* non-empty value, that value is used as the base URI regardless of cluster category. Otherwise, null is returned.
*/
export function getAgent365ObservabilityDomainOverride(): string | null{
Comment thread
fpfp100 marked this conversation as resolved.
Outdated
const override = process.env.A365_OBSERVABILITY_DOMAIN_OVERRIDE;

if (override && override.trim().length > 0) {
// Normalize to avoid double slashes when concatenating paths
return override.trim().replace(/\/+$/, '');
}
Comment thread
fpfp100 marked this conversation as resolved.
Outdated
return null;
}
18 changes: 14 additions & 4 deletions tests/observability/core/agent365-exporter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ describe('Agent365Exporter', () => {

it.each([
{
description: 'set to non-empty value',
description: 'set to non-empty value and A365_OBSERVABILITY_USE_CUSTOM_DOMAIN is true',
override: 'https://custom-observability.internal',
expectedBaseUrl: 'https://custom-observability.internal'
},
Expand All @@ -152,10 +152,16 @@ describe('Agent365Exporter', () => {
description: 'unset (undefined)',
override: undefined,
expectedBaseUrl: 'https://agent365.svc.cloud.microsoft'
}
])('uses correct domain when A365_OBSERVABILITY_DOMAIN_OVERRIDE is $description', async ({ override, expectedBaseUrl }) => {
},
{
description: 'set to non-empty value and A365_OBSERVABILITY_USE_CUSTOM_DOMAIN is false',
override: 'https://custom-observability.internal',
expectedBaseUrl: 'https://custom-observability.internal',
notUseCustomDomain: true
},
])('uses correct domain when A365_OBSERVABILITY_DOMAIN_OVERRIDE is $description', async ({ override, expectedBaseUrl, notUseCustomDomain }) => {
mockFetchSequence([200]);
process.env.A365_OBSERVABILITY_USE_CUSTOM_DOMAIN = 'true';
process.env.A365_OBSERVABILITY_USE_CUSTOM_DOMAIN = notUseCustomDomain ? 'false' : 'true';

if (override !== undefined) {
process.env.A365_OBSERVABILITY_DOMAIN_OVERRIDE = override as string;
Expand Down Expand Up @@ -184,7 +190,11 @@ describe('Agent365Exporter', () => {
const headersArg = fetchCalls[0][1].headers as Record<string, string>;

expect(urlArg).toBe(`${expectedBaseUrl}/maven/agent365/agents/${agentId}/traces?api-version=1`);
if(!notUseCustomDomain) {
expect(headersArg['x-ms-tenant-id']).toBe(tenantId);
} else {
expect(headersArg['x-ms-tenant-id']).toBeUndefined();
Comment thread
fpfp100 marked this conversation as resolved.
Outdated
Comment thread
fpfp100 marked this conversation as resolved.
Outdated
}
expect(headersArg['authorization']).toBe(`Bearer ${token}`);
});

Expand Down