Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@

## [0.1.0-alpha.4] - 2026-04-22

### Breaking Changes
- Remove `PerRequestSpanProcessor` and its `PerRequestSpanProcessorOptions` from the public API. The `perRequestExport` option on `A365Options` and the `ENABLE_A365_OBSERVABILITY_PER_REQUEST_EXPORT` environment variable have also been removed. ([#40](https://github.com/microsoft/opentelemetry-distro-javascript/pull/40))
- **Migration:** If you were relying on per-request export behaviour, use the now-public `Agent365Exporter` together with any OTel-compatible `SpanProcessor`. See the [migration guide](./MIGRATION_A365.md#custom-span-export) for an example.

### Features Added
- Export `Agent365Exporter`, `Agent365ExporterOptions`, and `TokenResolver` as public API to enable custom span processor configurations. ([#40](https://github.com/microsoft/opentelemetry-distro-javascript/pull/40))
- Add console exporter ([#35](https://github.com/microsoft/opentelemetry-distro-javascript/pull/35))
- Conditional azure monitor and readme updates ([#33](https://github.com/microsoft/opentelemetry-distro-javascript/pull/33))
- Add a365 hosting middleware ([#29](https://github.com/microsoft/opentelemetry-distro-javascript/pull/29))
Expand Down
39 changes: 30 additions & 9 deletions MIGRATION_A365.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ All public APIs are re-exported from the root `@microsoft/opentelemetry` package
| `@microsoft/agents-a365-observability` | `@microsoft/opentelemetry` | Notes |
|---|---|---|
| `SpanProcessor` (from `processors/`) | `A365SpanProcessor` | Renamed to avoid collision with OTel `SpanProcessor` |
| `PerRequestSpanProcessor` | `PerRequestSpanProcessor` | Same name |

## Initialization

Expand All @@ -102,7 +101,6 @@ import { Builder } from "@microsoft/agents-a365-observability";
const manager = new Builder({
tokenResolver: async (agentId, tenantId) => getToken(agentId, tenantId),
clusterCategory: "prod",
perRequestExport: true,
}).build();
```

Expand All @@ -118,7 +116,6 @@ useMicrosoftOpenTelemetry({
enabled: true,
tokenResolver: async (agentId, tenantId) => getToken(agentId, tenantId),
clusterCategory: "prod",
perRequestExport: true,
},
// Optional: also send to Azure Monitor
azureMonitor: {
Expand All @@ -136,15 +133,39 @@ Environment variable names are **unchanged** from Agent365-nodejs:
| Environment Variable | Description |
|---|---|
| `ENABLE_A365_OBSERVABILITY_EXPORTER` | Enable/disable A365 exporter (`true`, `1`, `yes`, `on`) |
| `ENABLE_A365_OBSERVABILITY_PER_REQUEST_EXPORT` | Enable/disable per-request export mode |
| `A365_OBSERVABILITY_SCOPES_OVERRIDE` | Space-separated list of OAuth scopes |
| `A365_OBSERVABILITY_DOMAIN_OVERRIDE` | Override service domain |
| `CLUSTER_CATEGORY` | Cluster category (`prod`, `dev`, `test`, etc.) |
| `A365_PER_REQUEST_MAX_TRACES` | Max buffered traces (default: `1000`) |
| `A365_PER_REQUEST_MAX_SPANS_PER_TRACE` | Max spans per trace (default: `5000`) |
| `A365_PER_REQUEST_MAX_CONCURRENT_EXPORTS` | Max concurrent exports (default: `20`) |
| `A365_PER_REQUEST_FLUSH_GRACE_MS` | Grace period after root span ends (default: `250`) |
| `A365_PER_REQUEST_MAX_TRACE_AGE_MS` | Max trace age before forced flush (default: `1800000`) |

## Custom Span Export

If your previous setup used `perRequestExport: true` (buffering spans per trace and exporting when a trace completes), use a custom `SpanProcessor` with the exported `Agent365Exporter`.
`BatchSpanProcessor` and `SimpleSpanProcessor` are supported, but they change export timing compared to the removed per-request behavior.
To keep equivalent timing semantics, implement a custom span processor.

```typescript
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { useMicrosoftOpenTelemetry, Agent365Exporter } from "@microsoft/opentelemetry";

const exporter = new Agent365Exporter({
clusterCategory: "prod",
tokenResolver: async (agentId, tenantId) => getToken(agentId, tenantId),
});

// Disable built-in A365 exporter to avoid double exporting when using custom processors.
process.env.ENABLE_A365_OBSERVABILITY_EXPORTER = "false";

// Supply any OTel-compatible SpanProcessor wrapping Agent365Exporter.
// Note: BatchSpanProcessor does not export on root-span completion.
useMicrosoftOpenTelemetry({
a365: { enabled: false },
spanProcessors: [new BatchSpanProcessor(exporter)],
});
```

> **Note:** `Agent365Exporter` is a standard `SpanExporter`. You can wrap it with any
> `SpanProcessor` from `@opentelemetry/sdk-trace-base` (e.g. `BatchSpanProcessor`,
> `SimpleSpanProcessor`) or a custom implementation.
Comment thread
hectorhdzg marked this conversation as resolved.

## Scopes

Expand Down
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ See the [OpenTelemetry OTLP Exporter specification](https://opentelemetry.io/doc
| `clusterCategory` | `ClusterCategory` | `"prod"` | Cluster category for endpoint resolution (`local`, `dev`, `test`, `preprod`, `firstrelease`, `prod`, `gov`, `high`, `dod`, `mooncake`, `ex`, `rx`) |
| `domainOverride` | `string` | — | Override the A365 observability service domain |
| `authScopes` | `string[]` | `["https://api.powerplatform.com/.default"]` | OAuth scopes for A365 service authentication |
| `perRequestExport` | `boolean` | `false` | Buffer spans per trace and export on root completion instead of batch export |
| `baggage` | `A365BaggageOptions` | see below | Baggage propagation and span enrichment options |
| `hosting` | `A365HostingOptions` | see below | Hosting middleware options (requires `@microsoft/agents-hosting`) |

Expand All @@ -137,7 +136,6 @@ A365 options can also be set via environment variables (highest precedence):
| Environment Variable | Description |
|---|---|
| `ENABLE_A365_OBSERVABILITY_EXPORTER` | `"true"` / `"false"` — override `enabled` |
| `ENABLE_A365_OBSERVABILITY_PER_REQUEST_EXPORT` | `"true"` / `"false"` — override `perRequestExport` |
| `A365_OBSERVABILITY_SCOPES_OVERRIDE` | Space-separated list of OAuth scopes |
| `A365_OBSERVABILITY_DOMAIN_OVERRIDE` | Override service domain |
| `CLUSTER_CATEGORY` | Override cluster category |
Expand Down
13 changes: 0 additions & 13 deletions src/a365/configuration/A365Configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ function parseEnvBoolean(envValue: string | undefined): boolean | undefined {
*/
export const A365_ENV_VARS = {
EXPORTER_ENABLED: "ENABLE_A365_OBSERVABILITY_EXPORTER",
PER_REQUEST_EXPORT: "ENABLE_A365_OBSERVABILITY_PER_REQUEST_EXPORT",
AUTH_SCOPES: "A365_OBSERVABILITY_SCOPES_OVERRIDE",
DOMAIN: "A365_OBSERVABILITY_DOMAIN_OVERRIDE",
CLUSTER_CATEGORY: "CLUSTER_CATEGORY",
Expand Down Expand Up @@ -82,9 +81,6 @@ export class A365Configuration {
/** OAuth scopes for A365 service authentication. */
public readonly authScopes: string[];

/** Whether to use per-request export mode. */
public readonly perRequestExport: boolean;

/** Baggage options. */
public readonly baggage: Required<A365BaggageOptions>;

Expand All @@ -97,13 +93,11 @@ export class A365Configuration {
let clusterCategory: ClusterCategory = "prod";
let domainOverride: string | undefined = options?.domainOverride;
let authScopes: string[] = options?.authScopes ?? [DEFAULT_AUTH_SCOPE];
let perRequestExport = false;

// 2. Apply programmatic options
if (options) {
enabled = options.enabled ?? enabled;
clusterCategory = options.clusterCategory ?? clusterCategory;
perRequestExport = options.perRequestExport ?? perRequestExport;
}

// 3. Apply environment variable overrides (highest precedence)
Expand All @@ -112,11 +106,6 @@ export class A365Configuration {
enabled = envEnabled;
}

const envPerRequest = parseEnvBoolean(process.env[A365_ENV_VARS.PER_REQUEST_EXPORT]);
if (envPerRequest !== undefined) {
perRequestExport = envPerRequest;
}

const envScopes = process.env[A365_ENV_VARS.AUTH_SCOPES]?.trim();
if (envScopes) {
authScopes = envScopes.split(/\s+/).filter(Boolean);
Expand All @@ -142,7 +131,6 @@ export class A365Configuration {
this.clusterCategory = clusterCategory;
this.domainOverride = domainOverride;
this.authScopes = authScopes;
this.perRequestExport = perRequestExport;

this.baggage = {
propagationEnabled: options?.baggage?.propagationEnabled ?? true,
Expand All @@ -165,7 +153,6 @@ export class A365Configuration {
const hasNonTrivialOptions =
options.tokenResolver !== undefined ||
options.domainOverride !== undefined ||
options.perRequestExport !== undefined ||
options.hosting?.enabled === true;

if (hasNonTrivialOptions) {
Expand Down
7 changes: 0 additions & 7 deletions src/a365/configuration/A365ConfigurationOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,6 @@ export interface A365Options {
/** OAuth scopes for A365 service authentication. */
authScopes?: string[];

/**
* Use per-request export mode (buffer spans per trace, export on root completion).
* When false, uses standard batch export.
* @default false
*/
perRequestExport?: boolean;

/** Baggage propagation and span enrichment options. */
baggage?: A365BaggageOptions;

Expand Down
2 changes: 0 additions & 2 deletions src/a365/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ export class OpenTelemetryConstants {
public static readonly ENABLE_OBSERVABILITY = "ENABLE_OBSERVABILITY";
public static readonly ENABLE_A365_OBSERVABILITY_EXPORTER = "ENABLE_A365_OBSERVABILITY_EXPORTER";
public static readonly ENABLE_A365_OBSERVABILITY = "ENABLE_A365_OBSERVABILITY";
public static readonly ENABLE_A365_OBSERVABILITY_PER_REQUEST_EXPORT =
"ENABLE_A365_OBSERVABILITY_PER_REQUEST_EXPORT";

// ── GenAI core attributes ────────────────────────────────────────
public static readonly GEN_AI_OPERATION_NAME_KEY = "gen_ai.operation.name";
Expand Down
4 changes: 2 additions & 2 deletions src/a365/context/tokenContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
* Per-request export token context propagation.
*
* Uses OpenTelemetry Context (backed by AsyncLocalStorage) to carry and
* refresh a per-request bearer token that the PerRequestSpanProcessor
* restores at export time.
* refresh a per-request bearer token that the export pipeline
* reads at export time.
*
* Adapted from microsoft/Agent365-nodejs agents-a365-observability/src/tracing/context/token-context.ts
*/
Expand Down
2 changes: 0 additions & 2 deletions src/a365/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,9 @@ export { BaggageBuilder, BaggageScope } from "./middleware/index.js";
// ── Processors ──────────────────────────────────────────────────────────────
export {
A365SpanProcessor,
PerRequestSpanProcessor,
GENERIC_ATTRIBUTES,
INVOKE_AGENT_ATTRIBUTES,
} from "./processors/index.js";
export type { PerRequestSpanProcessorOptions } from "./processors/index.js";

// ── Token context ───────────────────────────────────────────────────────────
export { runWithExportToken, updateExportToken, getExportToken } from "./context/tokenContext.js";
Expand Down
Loading
Loading