Browse Source

提交代码

feature/protocol-log-Perfect
root 1 day ago
parent
commit
e12d0fec2e
  1. 87
      CoreAgent.ProtocolClient/Context/UeStateManager/TmsiMatching.cs
  2. 27
      CoreAgent.ProtocolClient/ProtocolEngineCore/LogDataConverter.cs
  3. 14
      CoreAgent.ProtocolClient/ProtocolEngineCore/ProtocolLogProcessor.cs
  4. 181
      CoreAgent.ProtocolClient/modify.md
  5. 113
      modify.md

87
CoreAgent.ProtocolClient/Context/UeStateManager/TmsiMatching.cs

@ -231,28 +231,83 @@ namespace CoreAgent.ProtocolClient.Context.UeStateManager
/// <param name="srMapping">Service Request TMSI映射</param>
private void UpdateSrTmsiMappingPlmn(SrTmsiMapping srMapping, List<UeChainInfo> ueChainDetails)
{
if (srMapping.RootUeId > 0)
try
{
// 查找包含 RootUeId 的 PLMN 映射
var plmnMapping = PlmnToUeId.FirstOrDefault(kvp => kvp.Value.Contains(srMapping.RootUeId));
if (!string.IsNullOrEmpty(plmnMapping.Key))
_logger?.LogDebug("开始更新SrTmsiMapping PLMN信息 - TMSI: {Tmsi}, UeId: {UeId}, RootUeId: {RootUeId}",
srMapping.Tmsi, srMapping.UeId, srMapping.RootUeId);
if (srMapping.RootUeId > 0)
{
srMapping.Plmn = plmnMapping.Key;
_logger?.LogDebug("使用RootUeId {RootUeId} 查找PLMN映射", srMapping.RootUeId);
// 查找包含 RootUeId 的 PLMN 映射
var plmnMapping = PlmnToUeId.FirstOrDefault(kvp => kvp.Value.Contains(srMapping.RootUeId));
if (!string.IsNullOrEmpty(plmnMapping.Key))
{
srMapping.Plmn = plmnMapping.Key;
_logger?.LogDebug("通过RootUeId找到PLMN: {Plmn}", plmnMapping.Key);
}
else
{
_logger?.LogDebug("未找到RootUeId {RootUeId} 对应的PLMN映射", srMapping.RootUeId);
}
}
}
else if (srMapping.UeId > 0)
{
var Filter = TmsiToUeId.FirstOrDefault(s => s.Key == srMapping.Tmsi);
// 如果 RootUeId 为空,尝试使用 UeId 查找 PLMN
var chainInfo = ueChainDetails.FirstOrDefault(chain =>
chain.UeIds.Contains(Filter.Value));
else if (srMapping.UeId > 0)
{
_logger?.LogDebug("使用UeId {UeId} 和TMSI {Tmsi} 查找PLMN映射", srMapping.UeId, srMapping.Tmsi);
// 使用TryGetValue检查TMSI是否存在并获取对应的UeId
if (TmsiToUeId.TryGetValue(srMapping.Tmsi, out int filterUeId) && filterUeId > 0)
{
_logger?.LogDebug("找到TMSI {Tmsi} 对应的UeId: {FilterUeId}", srMapping.Tmsi, filterUeId);
// 如果 RootUeId 为空,尝试使用 UeId 查找 PLMN
var chainInfo = ueChainDetails?.FirstOrDefault(chain =>
chain?.UeIds?.Contains(filterUeId) == true);
var plmnMapping = PlmnToUeId.FirstOrDefault(kvp => kvp.Value.Contains(Filter.Value));
if (!string.IsNullOrEmpty(plmnMapping.Key))
if (chainInfo != null)
{
_logger?.LogDebug("找到包含UeId {FilterUeId} 的UE链,RootUeId: {ChainRootUeId}",
filterUeId, chainInfo.RootUeId);
var plmnMapping = PlmnToUeId.FirstOrDefault(kvp =>
kvp.Value?.Contains(filterUeId) == true);
if (!string.IsNullOrEmpty(plmnMapping.Key))
{
srMapping.Plmn = plmnMapping.Key;
srMapping.RootUeId = chainInfo.RootUeId;
_logger?.LogDebug("通过UeId找到PLMN: {Plmn}, 更新RootUeId: {RootUeId}",
plmnMapping.Key, chainInfo.RootUeId);
}
else
{
_logger?.LogDebug("未找到UeId {FilterUeId} 对应的PLMN映射", filterUeId);
}
}
else
{
_logger?.LogDebug("未找到包含UeId {FilterUeId} 的UE链", filterUeId);
}
}
else
{
_logger?.LogDebug("TMSI {Tmsi} 对应的UeId无效或为0", srMapping.Tmsi);
}
}
else
{
srMapping.Plmn = plmnMapping.Key;
srMapping.RootUeId = chainInfo.RootUeId;
_logger?.LogDebug("SrTmsiMapping的UeId和RootUeId都无效,跳过PLMN更新");
}
_logger?.LogDebug("SrTmsiMapping PLMN信息更新完成 - TMSI: {Tmsi}, UeId: {UeId}, RootUeId: {RootUeId}, PLMN: {Plmn}",
srMapping.Tmsi, srMapping.UeId, srMapping.RootUeId, srMapping.Plmn);
}
catch (Exception ex)
{
_logger?.LogError(ex, "更新SrTmsiMapping PLMN信息失败 - TMSI: {Tmsi}, UeId: {UeId}, RootUeId: {RootUeId}",
srMapping.Tmsi, srMapping.UeId, srMapping.RootUeId);
throw new GeneralUeIdentifierManagerException("UpdateSrTmsiMappingPlmn", "更新SrTmsiMapping PLMN信息失败", ex);
}
}

27
CoreAgent.ProtocolClient/ProtocolEngineCore/LogDataConverter.cs

@ -19,11 +19,12 @@ namespace CoreAgent.ProtocolClient.ProtocolEngineCore
{
private readonly ProtocolClientContext _context;
private readonly ILogger _logger;
public LogDataConverter(ProtocolClientContext context, ILogger logger)
private readonly ProtocolClientConfig _config;
public LogDataConverter(ProtocolClientContext context, ProtocolClientConfig config,ILogger logger)
{
_context = context;
_logger = logger;
_config = config;
}
/// <summary>
@ -34,8 +35,13 @@ namespace CoreAgent.ProtocolClient.ProtocolEngineCore
public List<TransferProtocolLog> ConvertToProtocolLogDetails(IEnumerable<BuildProtocolLog> protocolLogs)
{
var result = new List<TransferProtocolLog>();
var tmsiMatches = _context.UeIdentifier.GenerateTmsiMatches();
var srTmsiMappings = _context.UeIdentifier.GenerateSrTmsiMappings(tmsiMatches);
var tmsiMatches =new List<TmsiMatchResult>();
var srTmsiMappings = new List<SrTmsiMapping>();
if (_config.Name == "RAN")
{
tmsiMatches = _context.UeIdentifier.GenerateTmsiMatches();
srTmsiMappings = _context.UeIdentifier.GenerateSrTmsiMappings(tmsiMatches);
}
foreach (var log in protocolLogs)
{
try
@ -158,13 +164,14 @@ namespace CoreAgent.ProtocolClient.ProtocolEngineCore
Info = _context.UeIdentifier.IdToString(log.Info ?? -1),
MessageDetailJson = JsonConvert.SerializeObject(log.Data)
};
if (_config.Name == "RAN")
{
// 获取IMSI信息
detail.IMSI = GetImsiFromUeList(log.UeId, tmsiMatches, srTmsiMappings);
// 获取IMSI信息
detail.IMSI = GetImsiFromUeList(log.UeId, tmsiMatches, srTmsiMappings);
// 获取PLMN信息
detail.PLMN = GetPlmnFromPlmnToUeIdMapping(log.UeId, srTmsiMappings);
// 获取PLMN信息
detail.PLMN = GetPlmnFromPlmnToUeIdMapping(log.UeId, srTmsiMappings);
}
return detail;
}

14
CoreAgent.ProtocolClient/ProtocolEngineCore/ProtocolLogProcessor.cs

@ -163,7 +163,19 @@ namespace CoreAgent.ProtocolClient.ProtocolEngineCore
// 通知协议日志观察者处理转换后的数据
try
{
_protocolLogObserver.OnProtocolLogsReceived(logDetails);
// 过滤掉Info为空或无效的记录
var filteredLogDetails = logDetails.Where(detail =>
!string.IsNullOrWhiteSpace(detail.Info)
).ToList();
if (filteredLogDetails.Any())
{
_protocolLogObserver.OnProtocolLogsReceived(filteredLogDetails);
}
else
{
_logger.LogDebug("过滤后没有有效的日志记录,跳过观察者通知");
}
}
catch (Exception ex)
{

181
CoreAgent.ProtocolClient/modify.md

@ -3,6 +3,43 @@
## 修改日期
2024年12月19日
## 最新修改 - 2024年12月19日
### 修复UeIdentifierManager中UpdateSrTmsiMappingPlmn方法的字典访问问题
**问题描述:**
`UpdateSrTmsiMappingPlmn` 方法中,错误地使用了 `FirstOrDefault` 来查找字典中的键值对:
```csharp
var filter = TmsiToUeId.FirstOrDefault(s => s.Key == srMapping.Tmsi);
```
**问题分析:**
- `TmsiToUeId` 是一个 `Dictionary<uint, int>` 类型
- 使用 `FirstOrDefault` 查找字典键值对是不正确的做法
- 应该使用 `TryGetValue` 方法来检查键是否存在并获取值
**修复方案:**
- 将 `FirstOrDefault` 替换为 `TryGetValue` 方法
- 更新所有相关的变量引用
- 保持原有的逻辑功能不变
**修改文件:**
- `Context/UeStateManager/TmsiMatching.cs` - 修复UpdateSrTmsiMappingPlmn方法中的字典访问问题
**修改内容:**
```csharp
// 修改前
var filter = TmsiToUeId.FirstOrDefault(s => s.Key == srMapping.Tmsi);
if (filter.Value > 0)
// 修改后
if (TmsiToUeId.TryGetValue(srMapping.Tmsi, out int filterUeId) && filterUeId > 0)
```
**提交信息:** 修复UeIdentifierManager中UpdateSrTmsiMappingPlmn方法的字典访问问题
---
## 修改概述
对Models文件夹中的所有文件进行了命名规范检查和注释改进,主要涉及以下几个方面:
@ -1565,4 +1602,146 @@ public interface IProtocolLogObserver
3. **业务分析**:支持上层业务对协议日志进行分析和处理
4. **监控告警**:基于协议日志进行网络监控和告警
**提交信息:** 规范LogManager命名为ProtocolLogProcessor,添加协议日志观察者接口支持数据转发
**提交信息:** 规范LogManager命名为ProtocolLogProcessor,添加协议日志观察者接口支持数据转发
## 2025-01-01
### 修复UeIdentifierManager中UpdateSrTmsiMappingPlmn方法的空引用异常
**问题描述:**
在`UpdateSrTmsiMappingPlmn`方法中发生空引用异常,具体位置:
- 第254行:`chain.UeIds.Contains(Filter.Value)` 和 `kvp.Value.Contains(Filter.Value)`
- 第154行:`UpdateSrTmsiMappingPlmn(srMapping, ueChainDetails)` 调用
**根本原因:**
1. `TmsiToUeId.FirstOrDefault()`可能返回默认值,其中Value为0
2. `Filter.Value`可能为0,导致后续的Contains操作出现空引用
3. `chainInfo`可能为null,但代码中直接访问了`chainInfo.RootUeId`
**修复内容:**
1. **添加空值检查**:在访问`Filter.Value`之前检查其是否大于0
2. **添加null检查**:在访问`chainInfo.RootUeId`之前检查`chainInfo`是否为null
3. **添加集合null检查**:在调用`Contains`方法之前检查集合是否为null
4. **变量命名优化**:将`Filter`改为`filter`,符合C#命名约定
**涉及文件:**
- `Context/UeStateManager/TmsiMatching.cs` - 修复UpdateSrTmsiMappingPlmn方法
**修复对比:**
```csharp
// 修复前:存在空引用风险
else if (srMapping.UeId > 0)
{
var Filter = TmsiToUeId.FirstOrDefault(s => s.Key == srMapping.Tmsi);
// 如果 RootUeId 为空,尝试使用 UeId 查找 PLMN
var chainInfo = ueChainDetails.FirstOrDefault(chain =>
chain.UeIds.Contains(Filter.Value));
var plmnMapping = PlmnToUeId.FirstOrDefault(kvp => kvp.Value.Contains(Filter.Value));
if (!string.IsNullOrEmpty(plmnMapping.Key))
{
srMapping.Plmn = plmnMapping.Key;
srMapping.RootUeId = chainInfo.RootUeId;
}
}
// 修复后:添加完整的空值检查
else if (srMapping.UeId > 0)
{
var filter = TmsiToUeId.FirstOrDefault(s => s.Key == srMapping.Tmsi);
// 检查filter是否有效且Value不为0
if (filter.Value > 0)
{
// 如果 RootUeId 为空,尝试使用 UeId 查找 PLMN
var chainInfo = ueChainDetails?.FirstOrDefault(chain =>
chain?.UeIds?.Contains(filter.Value) == true);
if (chainInfo != null)
{
var plmnMapping = PlmnToUeId.FirstOrDefault(kvp =>
kvp.Value?.Contains(filter.Value) == true);
if (!string.IsNullOrEmpty(plmnMapping.Key))
{
srMapping.Plmn = plmnMapping.Key;
srMapping.RootUeId = chainInfo.RootUeId;
}
}
}
}
```
**主要改进:**
1. **空值安全**:添加了完整的空值检查,避免空引用异常
2. **防御性编程**:使用防御性编程原则,确保代码的健壮性
3. **命名规范**:修复变量命名,符合C#命名约定
4. **逻辑清晰**:通过条件检查使代码逻辑更加清晰
5. **异常预防**:从根本上预防了空引用异常的发生
**设计优势:**
- **健壮性提升**:代码能够处理各种边界情况和异常数据
- **维护性改进**:清晰的空值检查使代码更容易理解和维护
- **性能优化**:避免不必要的异常处理开销
- **用户体验**:避免程序崩溃,提供更好的用户体验
- **符合最佳实践**:遵循C#的防御性编程原则
**测试建议:**
1. **边界测试**:测试TmsiToUeId为空或包含无效值的情况
2. **异常测试**:测试ueChainDetails为null或包含null元素的情况
3. **集成测试**:在完整的TMSI映射流程中测试修复效果
4. **性能测试**:验证修复后的性能影响
**提交信息:** 修复UeIdentifierManager中UpdateSrTmsiMappingPlmn方法的空引用异常
## 2025-01-01
### 为UpdateSrTmsiMappingPlmn方法添加try-catch异常处理和跟踪日志
**修改内容:**
1. **添加完整的异常处理**
- 为`UpdateSrTmsiMappingPlmn`方法添加try-catch块
- 捕获所有异常并记录详细的错误日志
- 抛出`GeneralUeIdentifierManagerException`异常,包含原始异常信息
2. **添加详细的跟踪日志**
- 在方法开始时记录输入参数信息
- 在每个关键步骤添加Debug级别的日志
- 记录查找结果和更新状态
- 在方法结束时记录最终结果
- 在异常情况下记录详细的错误信息
**涉及文件:**
- `Context/UeStateManager/TmsiMatching.cs` - 为UpdateSrTmsiMappingPlmn方法添加异常处理和日志
**主要改进:**
1. **异常安全**:完整的异常处理确保方法不会因为异常而中断
2. **调试支持**:详细的跟踪日志便于问题定位和调试
3. **监控能力**:通过日志可以监控方法的执行状态和性能
4. **错误追踪**:异常日志包含完整的上下文信息,便于错误分析
**日志示例:**
```csharp
// 方法开始
_logger?.LogDebug("开始更新SrTmsiMapping PLMN信息 - TMSI: {Tmsi}, UeId: {UeId}, RootUeId: {RootUeId}",
srMapping.Tmsi, srMapping.UeId, srMapping.RootUeId);
// 关键步骤
_logger?.LogDebug("使用RootUeId {RootUeId} 查找PLMN映射", srMapping.RootUeId);
_logger?.LogDebug("通过RootUeId找到PLMN: {Plmn}", plmnMapping.Key);
// 方法结束
_logger?.LogDebug("SrTmsiMapping PLMN信息更新完成 - TMSI: {Tmsi}, UeId: {UeId}, RootUeId: {RootUeId}, PLMN: {Plmn}",
srMapping.Tmsi, srMapping.UeId, srMapping.RootUeId, srMapping.Plmn);
// 异常情况
_logger?.LogError(ex, "更新SrTmsiMapping PLMN信息失败 - TMSI: {Tmsi}, UeId: {UeId}, RootUeId: {RootUeId}",
srMapping.Tmsi, srMapping.UeId, srMapping.RootUeId);
```
**设计优势:**
- **可观测性**:通过日志可以完整追踪方法的执行过程
- **调试友好**:详细的日志信息便于问题定位和调试
- **异常安全**:完整的异常处理确保系统稳定性
- **监控支持**:支持通过日志进行性能监控和问题分析
- **维护便利**:详细的日志信息便于代码维护和问题排查
**提交信息:** 为UpdateSrTmsiMappingPlmn方法添加try-catch异常处理和跟踪日志

113
modify.md

@ -1432,4 +1432,115 @@
- 核心网络和IMS配置文件创建方法的返回类型简化
- 内部方法调用代码的简化
- 代码一致性的提升
- 性能的优化
- 性能的优化
### ProtocolLogProcessor.ProcessLogDetails方法Info字段过滤修复
**修改时间**: 2024年12月
**修改文件**:
- `CoreAgent.ProtocolClient/ProtocolEngineCore/ProtocolLogProcessor.cs`
**修改内容**:
1. **修复foreach循环中的Info字段检查**
- 在 `ProcessLogDetails` 方法的 `foreach` 循环中移除 `Info` 字段不为空的检查
- 允许处理所有日志记录,包括 `Info` 为空的记录
- 保持完整的日志处理和输出功能
2. **修复_protocolLogObserver.OnProtocolLogsReceived调用**
- 在调用 `_protocolLogObserver.OnProtocolLogsReceived(logDetails)` 之前过滤掉 `Info` 为空或无效的记录
- 使用 `!string.IsNullOrWhiteSpace(detail.Info)` 进行过滤,确保Info字段不为null、空字符串或只包含空白字符
- 确保传递给观察者的数据中只包含有效的 `Info` 字段
- 添加空集合检查,避免传递空集合给观察者
3. **具体实现**:
```csharp
// 修复前
foreach (var detail in logDetails)
{
try
{
// 检查Info字段不为空
if (!string.IsNullOrEmpty(detail.Info))
{
Console.WriteLine($"处理日志详情:Time={detail.Time} Layer={detail.LayerType}, UEID={detail.UEID}, IMSI={detail.IMSI},PLMN={detail.PLMN},INFO={detail.Info},Messsage={detail.Message}");
// 这里可以添加具体的业务处理逻辑
// 例如:保存到数据库、发送到其他服务等
_logger.LogDebug($"处理日志详情: Layer={detail.LayerType}, UEID={detail.UEID}, IMSI={detail.IMSI}");
}
}
catch (Exception ex)
{
_logger.LogError(ex, $"处理日志详情失败: Layer={detail.LayerType}, UEID={detail.UEID}");
}
}
// 通知协议日志观察者处理转换后的数据
try
{
// 过滤掉Info为空的记录
var filteredLogDetails = logDetails.Where(detail => !string.IsNullOrEmpty(detail.Info)).ToList();
_protocolLogObserver.OnProtocolLogsReceived(filteredLogDetails);
}
catch (Exception ex)
{
_logger.LogError(ex, "通知协议日志观察者失败");
}
// 修复后
foreach (var detail in logDetails)
{
try
{
Console.WriteLine($"处理日志详情:Time={detail.Time} Layer={detail.LayerType}, UEID={detail.UEID}, IMSI={detail.IMSI},PLMN={detail.PLMN},INFO={detail.Info},Messsage={detail.Message}");
// 这里可以添加具体的业务处理逻辑
// 例如:保存到数据库、发送到其他服务等
_logger.LogDebug($"处理日志详情: Layer={detail.LayerType}, UEID={detail.UEID}, IMSI={detail.IMSI}");
}
catch (Exception ex)
{
_logger.LogError(ex, $"处理日志详情失败: Layer={detail.LayerType}, UEID={detail.UEID}");
}
}
// 通知协议日志观察者处理转换后的数据
try
{
// 过滤掉Info为空或无效的记录
var filteredLogDetails = logDetails.Where(detail =>
!string.IsNullOrWhiteSpace(detail.Info)
).ToList();
if (filteredLogDetails.Any())
{
_protocolLogObserver.OnProtocolLogsReceived(filteredLogDetails);
}
else
{
_logger.LogDebug("过滤后没有有效的日志记录,跳过观察者通知");
}
}
catch (Exception ex)
{
_logger.LogError(ex, "通知协议日志观察者失败");
}
```
4. **设计优势**:
- **完整日志处理**:处理所有日志记录,包括Info为空的记录
- **数据质量保证**:确保传递给观察者的数据中只包含有效的Info字段
- **调试友好**:可以看到所有日志记录的详细信息,便于调试
- **观察者模式优化**:传递给观察者的数据更加纯净
- **错误预防**:避免因空Info字段导致的潜在问题
5. **修复的问题**:
- **日志信息缺失**:原代码会跳过Info为空的记录,导致日志信息不完整
- **调试困难**:无法看到所有日志记录的详细信息
- **数据传递问题**:将无效数据传递给协议日志观察者
- **信息不完整**:处理过程中丢失了部分日志信息
**影响范围**:
- 协议日志处理的完整性
- 日志输出的完整性
- 协议日志观察者接收的数据质量
- 调试和问题排查能力
Loading…
Cancel
Save