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
227 changes: 227 additions & 0 deletions shared/otel-core/planning/IMPLEMENTATION_PLAN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
# OTel Web SDK Implementation Plan

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

---

## Current Status

| Category | Status | Notes |
|----------|--------|-------|
| Tracing Core (TracerProvider, Tracer, Span) | ⚠️ Needs Updates | Closure pattern used but missing CONTEXT.md compliance |
| Logging Core (LoggerProvider, Logger, LogRecord) | ⚠️ Needs Updates | Closure pattern used but missing CONTEXT.md compliance |
| Context Management (ContextManager, Context) | ⚠️ Needs Updates | Closure pattern used but missing config pattern |
| Resource/Attributes | ⚠️ Needs Updates | Closure pattern used but missing shutdown cleanup |
| Error Handling (`handleErrors.ts`) | ✅ Complete | Helper functions |
| SDK Entry Point | ❌ Not Started | `OTelSdk` class has commented-out code |
| Samplers | ❌ Interface only | No implementations |
| SpanProcessor | ❌ Interface commented out | No implementations |
| Metrics | ❌ Interfaces only | No implementations |
| Propagation | ❌ Interface commented out | No implementations |

---

## CONTEXT.md Compliance Issues in "Complete" Components

### TracerProvider (`_createTracerProvider`)

| Issue | CONTEXT.md Requirement | Current State |
|-------|------------------------|---------------|
| Naming | Public factories use `create*` | Uses `_create*` (internal naming) |
| Config | Takes config object with deps | Takes `host: ITraceHost` |
| Error handlers | Get from `config.errorHandlers` | No error handling |
| Dynamic config | Use `onConfigChange`, save `IUnloadHook` | Not implemented |
| TypeDoc | Required on all public APIs | Missing |

### LoggerProvider (`createLoggerProvider`)

| Issue | CONTEXT.md Requirement | Current State |
|-------|------------------------|---------------|
| Required deps | Validate upfront, no defaults | Uses `config = {}` default |
| Error handlers | Get from `config.errorHandlers` | Creates empty `handlers = {}` |
| Dynamic config | Use `onConfigChange`, save `IUnloadHook` | Reads config at creation only |
| Shutdown | Call `_configUnload.rm()` | No config listener cleanup |

### ContextManager (`createContextManager`)

| Issue | CONTEXT.md Requirement | Current State |
|-------|------------------------|---------------|
| Config | Takes config object with deps | Takes `parentContext?` only |
| Error handlers | Get from `config.errorHandlers` | No error handling |
| TypeDoc | Required on factory functions | Minimal documentation |

### Resource (`createResource`)

| Issue | CONTEXT.md Requirement | Current State |
|-------|------------------------|---------------|
| Error handlers | Uses config handlers | ✅ Uses `resourceCtx.cfg.errorHandlers` |
| Shutdown | Cleanup method | No explicit shutdown/cleanup |

### Span (`createSpan`)

| Issue | CONTEXT.md Requirement | Current State |
|-------|------------------------|---------------|
| Error handlers | Uses config handlers | ✅ Uses handlers from config |
| Closure pattern | Private state in closures | ✅ Correct |
| Dynamic config | Use `onConfigChange` if caching config | Reads `otelCfg` at creation |

### Common Missing Patterns

1. **No `onConfigChange` usage** - None of the implementations use `onConfigChange` for dynamic config
2. **No `IUnloadHook` management** - No saving of hooks or calling `.rm()` during shutdown
3. **Inconsistent factory signatures** - Some take config objects, some take host/context directly
4. **Missing dependency validation** - Required deps not validated upfront per CONTEXT.md

---

## Implementation Phases

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

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

| Component | Changes Required |
|-----------|-----------------|
| `_createTracerProvider` | Rename to `createTracerProvider`, add config object, add error handlers, add `onConfigChange` |
| `createLoggerProvider` | Remove default config, validate required deps, get handlers from config, add `onConfigChange` |
| `createContextManager` | Add config object with error handlers |
| `createSpan` | Add `onConfigChange` if caching config values |
| `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)

| Component | Location | Description |
|-----------|----------|-------------|
| `createOTelWebSdk()` | `src/otel/sdk/` | Main SDK entry point factory |
| `IOTelWebSdkConfig` | `src/interfaces/otel/config/` | SDK configuration interface |
| Deprecate `OTelSdk` class | `src/otel/sdk/OTelSdk.ts` | Remove DynamicProto usage |

**Deliverable**: Functional SDK that can be instantiated with trace+log providers.

### Phase 2: Trace Processing Pipeline

| Component | Location | Description |
|-----------|----------|-------------|
| `IOTelSpanProcessor` | `src/interfaces/otel/trace/` | Uncomment/finalize interface |
| `ISpanExporter` | `src/interfaces/otel/trace/` | Export contract |
| `createSimpleSpanProcessor()` | `src/otel/sdk/` | Immediate export on span end |
| `createBatchSpanProcessor()` | `src/otel/sdk/` | Batched export with configurable delays |

**Deliverable**: Spans can be processed and exported.

### Phase 3: Samplers

| Component | Location | Description |
|-----------|----------|-------------|
| `createAlwaysOnSampler()` | `src/otel/sdk/` | Sample all spans |
| `createAlwaysOffSampler()` | `src/otel/sdk/` | Sample no spans |
| `createTraceIdRatioBasedSampler()` | `src/otel/sdk/` | Ratio-based sampling |
| `createParentBasedSampler()` | `src/otel/sdk/` | Inherit parent decision |

**Deliverable**: Configurable sampling strategies.

### Phase 4: Propagation

| Component | Location | Description |
|-----------|----------|-------------|
| `IOTelPropagationApi` | `src/interfaces/otel/propagation/` | Uncomment/finalize interface |
| `createW3CTraceContextPropagator()` | `src/otel/api/propagation/` | W3C TraceContext |
| `createW3CBaggagePropagator()` | `src/otel/api/propagation/` | W3C Baggage |
| `createCompositePropagator()` | `src/otel/api/propagation/` | Combine propagators |

**Deliverable**: Distributed tracing context propagation.

### Phase 5: Basic Metrics

| Component | Location | Description |
|-----------|----------|-------------|
| `createMeterProvider()` | `src/otel/sdk/` | MeterProvider factory |
| `createMeter()` | `src/otel/sdk/` | Meter factory |
| `createCounter()` | `src/otel/sdk/` | Monotonic counter |
| `createHistogram()` | `src/otel/sdk/` | Value distribution |
| `createGauge()` | `src/otel/sdk/` | Current value |

**Deliverable**: Basic metrics (counter, histogram, gauge).

### Phase 6: Cleanup & Polish

| Component | Location | Description |
|-----------|----------|-------------|
| `createIdGenerator()` | `src/otel/api/trace/` | Factory wrapper for ID utilities |
| `createBaggage()` | `src/otel/api/baggage/` | Baggage creation |
| Config isolation | `src/otel/sdk/config.ts` | Fix `process.env` access |
| Pattern migration | Various | Remove remaining DynamicProto usages |

---

## Pattern Compliance Issues

### Files Requiring Migration (DynamicProto → Closure)

| File | Priority | Action |
|------|----------|--------|
| `OTelSdk.ts` | High | Replace with `createOTelWebSdk()` factory |
| `DiagnosticLogger.ts` | Medium | Assess if migration needed for OTel use |
| `AppInsightsCore.ts` | Low | AI core - may not need migration |

### Files Using Closure Pattern (Need CONTEXT.md Compliance Updates)

| File | Factory | Compliance Issues |
|------|---------|-------------------|
| `tracerProvider.ts` | `_createTracerProvider` | Internal naming, no config object, no `onConfigChange` |
| `tracer.ts` | `_createTracer` | Internal naming, takes host not config |
| `span.ts` | `createSpan` | No `onConfigChange` for cached config |
| `contextManager.ts` | `createContextManager` | No config object, no error handlers |
| `OTelLoggerProvider.ts` | `createLoggerProvider` | Default config, no `onConfigChange`, creates own handlers |
| `OTelLogger.ts` | `createLogger` | Needs review for config compliance |
| `OTelLogRecord.ts` | `createLogRecord` | Needs review for config compliance |
| `resource.ts` | `createResource` | No shutdown method |
| `attributeContainer.ts` | `createAttributeContainer` | Needs review for config compliance |

---

## Test Coverage Gaps

| Area | Status |
|------|--------|
| Trace (Span, Tracer, TraceState) | ✅ Extensive |
| Logging (Logger, LogRecord, Processor) | ✅ Good |
| Attributes | ✅ Yes |
| Error Handling | ✅ Yes |
| SDK Entry Point | ❌ None |
| Samplers | ❌ None |
| SpanProcessor | ❌ None |
| Metrics | ❌ None |
| Propagation | ❌ None |

---

## Priority Order

0. **Phase 0** - CONTEXT.md Compliance Fixes (prerequisite for all other work)
1. **Phase 1** - SDK Entry Point (unblocks SDK usage)
2. **Phase 2** - SpanProcessor (enables trace export)
3. **Phase 3** - Samplers (enables sampling control)
4. **Phase 4** - Propagation (enables distributed tracing)
5. **Phase 5** - Metrics (completes OTel API coverage)
6. **Phase 6** - Polish and cleanup

---

## Validation Criteria

Per [CONTEXT.md](../CONTEXT.md), all implementations must:

- [ ] Use closure/factory pattern (`create*` functions)
- [ ] Return interface types only
- [ ] Inject all dependencies through config
- [ ] Use `IOTelErrorHandlers` for error handling
- [ ] Use config directly (NO spread operator)
- [ ] Use `onConfigChange` for dynamic config (save `IUnloadHook`, call `.rm()` on shutdown)
- [ ] Have comprehensive TypeDoc documentation
- [ ] Have unit tests with cleanup validation
82 changes: 82 additions & 0 deletions shared/otel-core/planning/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# OTel Web SDK - Summary

Concise summary of implementation requirements. Full details in [CONTEXT.md](../CONTEXT.md).

---

## Architectural Principles

**Interface-First Design** — Define all public APIs as TypeScript interfaces before implementation. Factory functions return interface types only, never expose implementation details.

**Inversion of Control (IoC)** — All dependencies explicitly injected through factory config. No hidden globals, no singletons, no static state. Components never reach out to get dependencies — they receive everything they need.

**Multi-Instance Architecture** — Multiple SDK instances coexist without interference. No shared global state between instances.

**No OpenTelemetry Package Imports** — Never import `@opentelemetry/*` packages directly (they have side effects that register globals). Define compatible `IOTel` interfaces instead.

**Complete Unload Support** — Every SDK instance must fully clean up on unload: remove hooks, clear timers, flush pending telemetry, release all resources.

---

## Core Rules

**No Global State** — Never use window properties, static state, or singletons.

**No OpenTelemetry Imports** — Define compatible interfaces with `IOTel` prefix instead.

**Factory Functions Only** — Use `create*` naming, return interface types, inject all dependencies through config.

**Closure Pattern** — Private state in closure variables, public methods on `_self` object, no classes.

**Config Used Directly** — Never copy config with spread operator. Use `onConfigChange` for local caching, save the `IUnloadHook`, call `.rm()` on shutdown.

**Error Handling** — Get handlers from `config.errorHandlers`, use helpers from `handleErrors.ts`.

**No No-Op Code in SDK** — The main SDK must not contain no-op/fallback implementations. A separate standalone no-op package provides API-compatible stubs for unsupported browsers. The SDK Loader decides whether to load the full SDK or the no-op package based on browser capability detection.

---

## Naming

| Type | Convention | Example |
|------|------------|---------|
| Public interfaces | `I` prefix | `IUnloadResult` |
| OTel interfaces | `IOTel` prefix | `IOTelTraceProvider` |
| Internal interfaces | `_I` prefix + `@internal` | `_ISpanProcessor` |
| Const enums | `e` prefix | `eSpanKind` |
| Public enums | use `createEnumStyle` | `SpanKind` |
| Factory functions | `create*` | `createTraceProvider` |

---

## Anti-Patterns

```typescript
window.__OTEL_SDK__; // ❌ Global access
let _config = { ...config }; // ❌ Spread operator
import { trace } from '@opentelemetry/api'; // ❌ OTel imports
export function create(config = {}) { } // ❌ Default deps
onConfigChange(config, () => {}); // ❌ Not saving hook
```

---

## Testing

- Call `core.unload(false)` in cleanup
- Test dynamic config changes
- Use framework helpers (`this.hookFetch()`, `this.useFakeTimers()`)
- Return `IPromise` for async tests

---

## Performance Targets (p95)

| Operation | Target |
|-----------|--------|
| SDK Init | < 5ms |
| Span Creation | < 0.1ms |
| Attribute Addition | < 0.05ms |
| Span Completion | < 0.2ms |

No continuous timers — start only when work exists, stop when complete.
Loading