Skip to content
Open
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
22 changes: 18 additions & 4 deletions shared/otel-core/Tests/Unit/src/sdk/OTelLogger.Tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import { setContextSpanContext } from "../../../../src/otel/api/trace/utils";
import { createLogger } from "../../../../src/otel/sdk/OTelLogger";
import { createResolvedPromise } from "@nevware21/ts-async";
import { IOTelApi, IOTelConfig } from "../../../../src";
import { IOTelResource, OTelRawResourceAttribute } from "../../../../src/interfaces/otel/resources/IOTelResource";
import { IOTelAttributes } from "../../../../src/interfaces/otel/IOTelAttributes";
import { IOTelLoggerProviderConfig } from "../../../../src/interfaces/otel/logs/IOTelLoggerProviderConfig";

// W3C trace flags constant for sampled traces
const eW3CTraceFlags_Sampled = 1;
Expand Down Expand Up @@ -38,9 +41,7 @@ export class OTelLoggerTests extends AITestClass {

private setup() {
const logProcessor = this._createMockProcessor();
const provider = createLoggerProvider({
processors: [logProcessor]
});
const provider = createLoggerProvider(this._cfg([logProcessor]));
const logger = provider.getLogger("test name", "test version", {
schemaUrl: "test schema url"
}) as LoggerWithScope;
Expand All @@ -53,7 +54,7 @@ export class OTelLoggerTests extends AITestClass {
name: "Logger: factory returns logger instance",
test: () => {
const logProcessor = this._createMockProcessor();
const provider = createLoggerProvider({ processors: [logProcessor] });
const provider = createLoggerProvider(this._cfg([logProcessor]));
const sharedState = provider._sharedState;
const scope: IOTelInstrumentationScope = {
name: "test name",
Expand Down Expand Up @@ -151,4 +152,17 @@ export class OTelLoggerTests extends AITestClass {
shutdown: () => createResolvedPromise(undefined)
};
}

private _cfg(processors: IOTelLogRecordProcessor[]): IOTelLoggerProviderConfig {
const resource: IOTelResource = {
attributes: {} as IOTelAttributes,
merge: () => resource,
getRawAttributes: () => [] as OTelRawResourceAttribute[]
};
return {
resource: resource,
errorHandlers: {},
processors: processors
};
}
}
86 changes: 58 additions & 28 deletions shared/otel-core/Tests/Unit/src/sdk/OTelLoggerProvider.Tests.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import { IOTelSdkLogRecord } from "../../../../src/interfaces/otel/logs/IOTelSdk
import { createLoggerProvider } from "../../../../src/otel/sdk/OTelLoggerProvider";
import { createMultiLogRecordProcessor } from "../../../../src/otel/sdk/OTelMultiLogRecordProcessor";
import { loadDefaultConfig } from "../../../../src/otel/sdk/config";
import { IOTelLoggerProviderConfig } from "../../../../src/interfaces/otel/logs/IOTelLoggerProviderConfig";
import { IOTelResource, OTelRawResourceAttribute } from "../../../../src/interfaces/otel/resources/IOTelResource";
import { IOTelAttributes } from "../../../../src/interfaces/otel/IOTelAttributes";

class TestProcessor implements IOTelLogRecordProcessor {
public logRecords: IOTelSdkLogRecord[] = [];
Expand Down Expand Up @@ -46,6 +49,23 @@ const setup = (processors?: IOTelLogRecordProcessor[]) => {
return { multiProcessor, forceFlushTimeoutMillis };
};

function _testResource(): IOTelResource {
const resource: IOTelResource = {
attributes: {} as IOTelAttributes,
merge: () => resource,
getRawAttributes: () => [] as OTelRawResourceAttribute[]
};
return resource;
}

function _cfg(processors: IOTelLogRecordProcessor[]): IOTelLoggerProviderConfig {
return {
resource: _testResource(),
errorHandlers: {},
processors: processors
};
}

export class OTelMultiLogRecordProcessorTests extends AITestClass {
public testInitialize() {
super.testInitialize();
Expand All @@ -70,7 +90,7 @@ export class OTelMultiLogRecordProcessorTests extends AITestClass {
name: "MultiLogRecordProcessor: onEmit - should no-op when no processors registered",
test: () => {
const { multiProcessor } = setup();
const provider = createLoggerProvider({ processors: [multiProcessor] });
const provider = createLoggerProvider(_cfg([multiProcessor]));
const logger = provider.getLogger("default");
logger.emit({ body: "message" });
Assert.ok(true, "Emit should not throw when no processors registered");
Expand All @@ -82,7 +102,7 @@ export class OTelMultiLogRecordProcessorTests extends AITestClass {
test: () => {
const processor = new TestProcessor();
const { multiProcessor } = setup([processor]);
const provider = createLoggerProvider({ processors: [multiProcessor] });
const provider = createLoggerProvider(_cfg([multiProcessor]));
const logger = provider.getLogger("default");
Assert.equal(processor.logRecords.length, 0, "Processor should start with no records");
logger.emit({ body: "one" });
Expand All @@ -96,7 +116,7 @@ export class OTelMultiLogRecordProcessorTests extends AITestClass {
const processor1 = new TestProcessor();
const processor2 = new TestProcessor();
const { multiProcessor } = setup([processor1, processor2]);
const provider = createLoggerProvider({ processors: [multiProcessor] });
const provider = createLoggerProvider(_cfg([multiProcessor]));
const logger = provider.getLogger("default");

Assert.equal(processor1.logRecords.length, 0, "Processor1 should start empty");
Expand Down Expand Up @@ -208,7 +228,7 @@ export class OTelMultiLogRecordProcessorTests extends AITestClass {
const processor1 = new TestProcessor();
const processor2 = new TestProcessor();
const { multiProcessor } = setup([processor1, processor2]);
const provider = createLoggerProvider({ processors: [multiProcessor] });
const provider = createLoggerProvider(_cfg([multiProcessor]));
const logger = provider.getLogger("default");

logger.emit({ body: "one" });
Expand Down
7 changes: 3 additions & 4 deletions shared/otel-core/planning/IMPLEMENTATION_PLAN.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# OTel Web SDK Implementation Plan

**Last Updated**: February 2026
**Last Updated**: March 2026
**Reference**: [CONTEXT.md](../CONTEXT.md)

---
Expand Down Expand Up @@ -77,7 +77,7 @@

## Implementation Phases

### Phase 0: CONTEXT.md Compliance Fixes (Prerequisite)
### Phase 0: CONTEXT.md Compliance Fixes (Prerequisite) ✅ Complete

Fix existing implementations to comply with CONTEXT.md before building new features.

Expand All @@ -90,10 +90,9 @@ Fix existing implementations to comply with CONTEXT.md before building new featu
| `createResource` | Add shutdown/cleanup method |
| All | Add `IUnloadHook` management with `.rm()` calls during shutdown |
| All | Add comprehensive TypeDoc documentation |

**Deliverable**: All existing implementations pass CONTEXT.md validation checklist.

### Phase 1: SDK Foundation (Critical)
### Phase 1: SDK Foundation (Critical) ✅ Complete

| Component | Location | Description |
|-----------|----------|-------------|
Expand Down
4 changes: 3 additions & 1 deletion shared/otel-core/src/ext/ValueSanitizer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { arrForEach, arrIncludes, arrIndexOf, getLength, isNullOrUndefined, isString, objCreate, objForEachKey } from "@nevware21/ts-utils";
import {
arrForEach, arrIncludes, arrIndexOf, getLength, isNullOrUndefined, isString, objCreate, objForEachKey
} from "@nevware21/ts-utils";
import { STR_EMPTY } from "../constants/InternalConstants";
import { FieldValueSanitizerType } from "../enums/ext/Enums";
import {
Expand Down
4 changes: 4 additions & 0 deletions shared/otel-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ export { IOTelSpanOptions } from "./interfaces/otel/trace/IOTelSpanOptions";
export { createOTelTraceState, isOTelTraceState } from "./otel/api/trace/traceState";
export { createSpan } from "./otel/api/trace/span";
export { createTraceProvider } from "./otel/api/trace/traceProvider";
export { createTracerProvider } from "./otel/api/trace/tracerProvider";
export { ITracerProviderConfig } from "./interfaces/otel/trace/ITracerProviderConfig";
export { isSpanContext, wrapDistributedTrace, createOTelSpanContext } from "./otel/api/trace/spanContext";
export {
createNonRecordingSpan, deleteContextSpan, getContextSpan, setContextSpan, setContextSpanContext, getContextActiveSpanContext, isSpanContextValid,
Expand Down Expand Up @@ -200,6 +202,7 @@ export { IOTelWebSdkConfig } from "./interfaces/otel/config/IOTelWebSdkConfig";
export { createContextManager } from "./otel/api/context/contextManager";
export { createContext } from "./otel/api/context/context";
export { IOTelContextManager } from "./interfaces/otel/context/IOTelContextManager";
export { IContextManagerConfig } from "./interfaces/otel/context/IContextManagerConfig";
export { IOTelContext } from "./interfaces/otel/context/IOTelContext";

// OpenTelemetry Resources
Expand Down Expand Up @@ -253,6 +256,7 @@ export { IOTelSdkLogRecord } from "./interfaces/otel/logs/IOTelSdkLogRecord";
export { IOTelLoggerProvider } from "./interfaces/otel/logs/IOTelLoggerProvider";
export { IOTelLoggerOptions } from "./interfaces/otel/logs/IOTelLoggerOptions";
export { IOTelLoggerProviderSharedState } from "./interfaces/otel/logs/IOTelLoggerProviderSharedState";
export { IOTelLoggerProviderConfig } from "./interfaces/otel/logs/IOTelLoggerProviderConfig";
export { IOTelLogRecordLimits } from "./interfaces/otel/logs/IOTelLogRecordLimits";

// SDK Logs
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import { IOTelErrorHandlers } from "../config/IOTelErrorHandlers";
import { IOTelContext } from "./IOTelContext";

/**
* Configuration interface for creating a context manager.
*
* @since 4.0.0
*/
export interface IContextManagerConfig {
/**
* The parent / root context to use if there is no active context.
*/
parentContext?: IOTelContext;

/**
* Error handlers for internal diagnostics.
*
* @see {@link IOTelErrorHandlers}
*/
errorHandlers?: IOTelErrorHandlers;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should have this defined here, the "implementation" should inherit/use the core (Sdk/Distro) config to use the errorHandlers -- which should be provided during initialization...

Otherwise, we could end up passing the same error handlers multiple times

}
Original file line number Diff line number Diff line change
@@ -1,21 +1,45 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import { IOTelErrorHandlers } from "../config/IOTelErrorHandlers";
import { IOTelResource } from "../resources/IOTelResource";
import { IOTelLogRecordLimits } from "./IOTelLogRecordLimits";
import { IOTelLogRecordProcessor } from "./IOTelLogRecordProcessor";

/**
* Configuration interface for the OpenTelemetry LoggerProvider.
* Provides all configuration options required for LoggerProvider initialization.
*
* @remarks
* - The `resource` and `errorHandlers` properties are required
* - Config is used directly — never copied with spread operator
* - Supports dynamic configuration via `onConfigChange` callbacks
*
* @since 3.4.0
*/
export interface IOTelLoggerProviderConfig {
/** Resource associated with trace telemetry */
resource?: IOTelResource;
/**
* Resource information for telemetry source identification.
* Provides attributes that describe the entity producing telemetry.
*/
resource: IOTelResource;

/**
* Error handlers for internal diagnostics.
* Provides hooks to customize how different types of errors and
* diagnostic messages are handled.
*
* @see {@link IOTelErrorHandlers}
*/
errorHandlers: IOTelErrorHandlers;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment as context


/**
* How long the forceFlush can run before it is cancelled.
* The default value is 30000ms
*/
forceFlushTimeoutMillis?: number;

/** Log Record Limits*/
/** Log Record Limits */
logRecordLimits?: IOTelLogRecordLimits;

/** Log Record Processors */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ export interface IOTelLoggerProviderSharedState {
/**
* Resource describing the entity producing telemetry.
*/
readonly resource: IOTelResource;
resource: IOTelResource;
/**
* Timeout applied when forcing processors to flush.
*/
readonly forceFlushTimeoutMillis: number;
forceFlushTimeoutMillis: number;
/**
* Limits applied to log records created by the provider.
*/
readonly logRecordLimits: Required<IOTelLogRecordLimits>;
logRecordLimits: Required<IOTelLogRecordLimits>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,12 @@ export interface IOTelResource {
merge(other: IOTelResource | null): IOTelResource;

getRawAttributes(): OTelRawResourceAttribute[];

/**
* Releases internal resources and clears cached attribute containers.
* After shutdown, the resource should not be used.
*
* @since 4.0.0
*/
shutdown?(): void;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import { ITraceHost } from "../../ai/ITraceProvider";
import { IOTelErrorHandlers } from "../config/IOTelErrorHandlers";

/**
* Configuration interface for creating a TracerProvider.
*
* @remarks
* The TracerProvider manages Tracer instances and delegates span creation
* to the configured trace host.
*
* @since 4.0.0
*/
export interface ITracerProviderConfig {
/**
* The trace host that provides span creation and context management.
*
* @see {@link ITraceHost}
*/
host: ITraceHost;

/**
* Error handlers for internal diagnostics.
*
* @see {@link IOTelErrorHandlers}
*/
errorHandlers?: IOTelErrorHandlers;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment as context -- and this being defined for context, logs and trace reinforces my original comment

}
4 changes: 2 additions & 2 deletions shared/otel-core/src/otel/api/OTelApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import { IOTelApiCtx } from "../../interfaces/otel/IOTelApiCtx";
import { ITraceApi } from "../../interfaces/otel/trace/IOTelTraceApi";
import { setProtoTypeName } from "../../utils/HelperFuncs";
import { _createTraceApi } from "./trace/traceApi";
import { _createTracerProvider } from "./trace/tracerProvider";
import { createTracerProvider } from "./trace/tracerProvider";

/*#__NO_SIDE_EFFECTS__*/
export function createOTelApi(otelApiCtx: IOTelApiCtx): IOTelApi {
let _traceApi: ILazyValue<ITraceApi>;

let otelApi = setProtoTypeName(objDefineProps<IOTelApi>(_createTracerProvider(otelApiCtx.host) as IOTelApi, {
let otelApi = setProtoTypeName(objDefineProps<IOTelApi>(createTracerProvider({ host: otelApiCtx.host }) as IOTelApi, {
cfg: { g: () => otelApiCtx.host.config },
trace: { g: () => _traceApi.v },
host: { g: () => otelApiCtx.host }
Expand Down
Loading
Loading