# @loongsuite/cms_context

## 简介

CMS Context 提供基于 Node.js AsyncLocalStorage 和 async_hooks 的上下文管理器实现，用于在异步调用链中传递和保持上下文信息。这是实现分布式追踪上下文传播的关键组件。

## 特性

- 🔄 **异步上下文**: 基于 AsyncLocalStorage 的异步上下文管理
- 🎯 **自动传播**: 自动在异步调用中传播上下文
- ⚡ **高性能**: 利用 Node.js 原生 async_hooks 机制
- 🔧 **灵活配置**: 支持多种上下文管理器实现
- 🛡️ **类型安全**: 完整的 TypeScript 类型支持

## 安装

```bash
# 使用 anpm (推荐)
anpm add @loongsuite/cms_context

# 或使用 npm
npm install @loongsuite/cms_context
```

## 核心概念

### 上下文管理器

上下文管理器负责在异步调用中维护和传递上下文信息。CMS Context 提供了两种实现：

1. **AsyncLocalStorageContextManager**: 基于 AsyncLocalStorage（推荐）
2. **AsyncHooksContextManager**: 基于 async_hooks

### AsyncLocalStorageContextManager

这是默认的上下文管理器实现，基于 Node.js 12.17+ 的 AsyncLocalStorage API。

```typescript
import { AsyncLocalStorageContextManager } from '@loongsuite/cms_context';

const contextManager = new AsyncLocalStorageContextManager();

// 获取当前上下文
const currentContext = contextManager.active();

// 在指定上下文中执行代码
const newContext = { traceId: 'abc123', spanId: 'def456' };
contextManager.with(newContext, () => {
  // 在此作用域内，上下文为新上下文
  const activeContext = contextManager.active();
  console.log(activeContext); // { traceId: 'abc123', spanId: 'def456' }
  
  // 异步操作也会保持上下文
  setTimeout(() => {
    const asyncContext = contextManager.active();
    console.log(asyncContext); // 仍然是 { traceId: 'abc123', spanId: 'def456' }
  }, 100);
});
```

### AsyncHooksContextManager

基于 async_hooks 的上下文管理器，兼容更老的 Node.js 版本。

```typescript
import { AsyncHooksContextManager } from '@loongsuite/cms_context';

const contextManager = new AsyncHooksContextManager();

// 使用方式与 AsyncLocalStorageContextManager 相同
const context = { userId: 'user123', requestId: 'req456' };
contextManager.with(context, () => {
  // 业务逻辑
  processAsyncOperation();
});
```

## 主要导出

```typescript
import {
  AsyncLocalStorageContextManager,
  AsyncHooksContextManager,
  IContextManager,
  Context
} from '@loongsuite/cms_context';
```

## 使用示例

### 基本用法

```typescript
import { AsyncLocalStorageContextManager } from '@loongsuite/cms_context';

const contextManager = new AsyncLocalStorageContextManager();

// 设置初始上下文
const initialContext = {
  traceId: 'trace-123',
  spanId: 'span-456',
  userId: 'user-789'
};

contextManager.with(initialContext, () => {
  console.log('Initial context:', contextManager.active());
  
  // 嵌套的异步操作
  setTimeout(() => {
    console.log('Async context:', contextManager.active());
    
    // 更深层的异步操作
    Promise.resolve().then(() => {
      console.log('Promise context:', contextManager.active());
    });
  }, 100);
});
```

### 与追踪器集成

```typescript
import { AsyncLocalStorageContextManager } from '@loongsuite/cms_context';
import { TracerManager } from '@loongsuite/cms_trace';

const contextManager = new AsyncLocalStorageContextManager();
const tracerManager = new TracerManager({
  contextManager,
  // ... 其他配置
});

const tracer = tracerManager.getTracer('my-service');

// 创建 span 并设置到上下文
const span = tracer.startSpan('operation');
const contextWithSpan = tracerManager.setSpan(contextManager.active(), span);

contextManager.with(contextWithSpan, () => {
  // 在此上下文中，可以获取到当前的 span
  const currentSpan = tracerManager.getActiveSpan(contextManager.active());
  console.log('Current span:', currentSpan);
  
  // 异步操作会保持 span 上下文
  setTimeout(() => {
    const asyncSpan = tracerManager.getActiveSpan(contextManager.active());
    console.log('Async span:', asyncSpan); // 仍然是同一个 span
  }, 100);
});
```

### 上下文绑定

```typescript
import { AsyncLocalStorageContextManager } from '@loongsuite/cms_context';

const contextManager = new AsyncLocalStorageContextManager();

// 绑定上下文到函数
const context = { requestId: 'req-123' };
const boundFunction = contextManager.bind(context, (message: string) => {
  const activeContext = contextManager.active();
  console.log(`[${activeContext.requestId}] ${message}`);
});

// 即使在不同上下文中调用，也会使用绑定的上下文
contextManager.with({ requestId: 'req-456' }, () => {
  boundFunction('Hello World'); // 输出: [req-123] Hello World
});
```

### 错误处理

```typescript
import { AsyncLocalStorageContextManager } from '@loongsuite/cms_context';

const contextManager = new AsyncLocalStorageContextManager();

const context = { operationId: 'op-123' };

try {
  contextManager.with(context, () => {
    // 可能抛出错误的操作
    throw new Error('Something went wrong');
  });
} catch (error) {
  // 错误处理时仍然可以访问上下文
  const errorContext = contextManager.active();
  console.log(`Error in context ${errorContext.operationId}:`, error.message);
}
```

## 性能考虑

### AsyncLocalStorage vs AsyncHooks

- **AsyncLocalStorage**: 性能更好，但需要 Node.js 12.17+
- **AsyncHooks**: 兼容性更好，但性能稍差

### 最佳实践

1. **优先使用 AsyncLocalStorageContextManager**
2. **避免频繁的上下文切换**
3. **合理使用上下文绑定**

```typescript
// 好的做法：批量操作
contextManager.with(context, () => {
  // 执行多个相关操作
  operation1();
  operation2();
  operation3();
});

// 避免：频繁切换上下文
contextManager.with(context1, () => operation1());
contextManager.with(context2, () => operation2());
contextManager.with(context3, () => operation3());
```

## 与 NodeSDK 集成

NodeSDK 默认使用 AsyncLocalStorageContextManager，通常无需手动配置：

```typescript
import { NodeSDK } from '@loongsuite/cms_node_sdk';

const sdk = new NodeSDK({
  serviceName: 'my-service',
  // contextManager 会自动使用 AsyncLocalStorageContextManager
});

sdk.start();

// 获取上下文管理器
const contextManager = sdk.getContextManager();
console.log(contextManager instanceof AsyncLocalStorageContextManager); // true
```

## 故障排除

### 常见问题

1. **上下文丢失**: 确保在正确的异步边界内使用上下文
2. **性能问题**: 考虑使用 AsyncLocalStorageContextManager
3. **内存泄漏**: 及时清理不需要的上下文

### 调试技巧

```typescript
import { AsyncLocalStorageContextManager } from '@loongsuite/cms_context';

const contextManager = new AsyncLocalStorageContextManager();

// 添加调试信息
const debugContext = {
  ...context,
  _debug: {
    timestamp: Date.now(),
    stack: new Error().stack
  }
};

contextManager.with(debugContext, () => {
  console.log('Context debug info:', contextManager.active()._debug);
});
```

## 依赖

- `@loongsuite/cms_core`: 核心接口和类型定义

## 许可证

MIT License