本文档旨在帮助理解 STS_Root_Cause_Analysis_Error.ipynb
Jupyter Notebook 的工作流程和各部分功能。
STS_Root_Cause_Analysis_Error.ipynb 工作流程解析
该 notebook 是一个基于阿里云日志服务(SLS)的根因分析工具,用于诊断系统错误的根本原因。它通过分析分布式追踪数据中的错误 span,结合模式匹配和日志分析技术,识别出导致系统错误的核心服务。
1. 环境配置阶段
ini
import os
import sys
from dotenv import load_dotenv
from aliyun.log import LogClient
from alibabacloud_sts20150401.client import Client as StsClient
from alibabacloud_sts20150401 import models as sts_models
from alibabacloud_tea_openapi import models as open_api_models
from Tea.exceptions import TeaException
load_dotenv()
sys.path.append('..')
# 分析时间区间
FAULT_START_TIME = "2025-08-28 15:08:03"
FAULT_END_TIME = "2025-08-28 15:13:03"
# SLS配置参数 (日志数据存储的位置)
PROJECT_NAME = "proj-xtrace-a46b97cfdc1332238f714864c014a1b-cn-qingdao"
LOGSTORE_NAME = "logstore-tracing"
REGION = "cn-qingdao"
# 获取环境变量配置
MAIN_ACCOUNT_ACCESS_KEY_ID = os.getenv('ALIBABA_CLOUD_ACCESS_KEY_ID')
MAIN_ACCOUNT_ACCESS_KEY_SECRET = os.getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET')
STS_ROLE_ARN = os.getenv('STS_RAM_ROLE_ARN','acs:ram::1672753017899339:role/tianchi-user-a')
STS_SESSION_NAME = "my-sls-access" # 自定义会话名称
这部分代码主要完成以下任务:
- 导入必要的库和模块
- 加载环境变量配置
- 设置故障分析时间范围
- 配置SLS参数(项目名、日志库名、区域)
- 从环境变量中读取认证信息
其中关键点是使用了STS(Security Token Service)机制获取临时凭证,这是一种安全最佳实践。
2.错误 Span 筛选阶段
在错误 Span 筛选阶段,代码首先定义了一些辅助函数,然后使用这些函数来完成主要任务。
2.1 辅助方法
get_sts_credentials() 函数
这个函数负责通过阿里云STS(Security Token Service)服务获取临时安全凭证。它使用主账号的AccessKey ID和Secret,以及预定义的角色ARN来获取临时的安全凭证,这种方式比直接使用主账号凭证更加安全。
python
def get_sts_credentials():
# 检查必要的参数是否都已配置
if not all([MAIN_ACCOUNT_ACCESS_KEY_ID, MAIN_ACCOUNT_ACCESS_KEY_SECRET, STS_ROLE_ARN]):
print("❌ 角色ARN缺失! 请在.env文件中配置 ALIBABA_CLOUD_ACCESS_KEY_ID, ALIBABA_CLOUD_ACCESS_KEY_SECRET")
return None
# 配置STS客户端
config = open_api_models.Config(
access_key_id=MAIN_ACCOUNT_ACCESS_KEY_ID,
access_key_secret=MAIN_ACCOUNT_ACCESS_KEY_SECRET,
endpoint=f'sts.{REGION}.aliyuncs.com'
)
sts_client = StsClient(config)
# 创建AssumeRole请求
assume_role_request = sts_models.AssumeRoleRequest(
role_arn=STS_ROLE_ARN,
role_session_name=STS_SESSION_NAME,
duration_seconds=3600
)
# 尝试获取临时凭证
try:
response = sts_client.assume_role(assume_role_request)
print("✅ 成功获取访问权限!")
return response.body.credentials
except TeaException as e:
print(f"❌ 获取STS临时凭证失败: {e.message}")
print(f" 错误码: {e.code}")
print(" 请检查:1. 主账号AK是否正确;2. 目标角色ARN是否正确;3. 目标角色的信任策略是否已配置为信任您的主账号。")
return None
except Exception as e:
print(f"❌ 发生未知错误在获取STS凭证时: {e}")
return None
函数执行过程:
- 首先检查必要的环境变量是否都已正确配置
- 使用主账号的AccessKey ID和Secret创建STS客户端配置
- 构造AssumeRole请求,指定角色ARN、会话名称和凭证有效期(3600秒)
- 调用STS服务的assume_role方法获取临时凭证
- 处理可能的异常情况,如凭证错误、角色配置问题等
- 返回获取到的临时凭证对象
create_sls_client_with_sts() 函数
这个函数使用前面获取的STS临时凭证来创建SLS(日志服务)客户端。通过这个客户端,我们可以安全地访问阿里云日志服务中的数据。
ini
def create_sls_client_with_sts():
# 获取STS临时凭证
sts_credentials = get_sts_credentials()
# 如果凭证获取失败,直接返回None
if not sts_credentials:
return None
# 构造SLS服务端点
sls_endpoint = f"{REGION}.log.aliyuncs.com"
# 使用临时凭证创建SLS客户端
# aliyun-log-python-sdk 使用 securityToken 参数
log_client = LogClient(
endpoint=sls_endpoint,
accessKeyId=sts_credentials.access_key_id,
accessKey=sts_credentials.access_key_secret,
securityToken=sts_credentials.security_token
)
print("✅ SLS客户端已使用临时凭证创建。")
return log_client
函数执行过程:
- 调用get_sts_credentials()函数获取临时安全凭证
- 如果凭证获取失败,则直接返回None
- 根据区域信息构造SLS服务端点
- 使用获取到的临时凭证(包括AccessKey ID、AccessKey Secret和Security Token)创建LogClient实例
- 返回创建好的SLS客户端
2.2 前置知识
Trace
在微服务架构中,一个用户请求可能会经过多个服务的处理,每个服务可能部署在不同的服务器上。这种架构虽然提高了系统的可扩展性和灵活性,但也增加了问题排查的复杂性。当系统出现错误或性能问题时,很难快速定位问题发生在哪个服务的哪个环节。
分布式追踪技术就是为了解决这个问题而诞生的。它通过在请求流经的每个服务中添加追踪信息,形成一个完整的调用链路,帮助开发者和运维人员快速定位问题。
Trace(追踪)代表了一个完整的请求在分布式系统中的执行路径。它是一系列相关的 Span 的集合,这些 Span 共同构成了一个请求从开始到结束的完整调用链路。
核心特征
- Trace ID:Trace 的全局唯一标识符,用于关联同一个请求的所有 Span
- 起点和终点:代表了整个请求的开始和结束时间
- 完整调用链:包含了请求经过的所有服务和组件
Trace 示例
假设用户在电商网站上进行购物下单操作,这个操作可能涉及以下服务调用:
makefile
用户请求: 下单操作
↓
前端服务 (frontend)
↓
结算服务 (checkout)
↓
货币服务 (currency) 购物车服务 (cart) 配送服务 (shipping) 支付服务 (payment)
↓
忠诚度服务 (loyalty)
这个完整的下单操作就构成了一个 Trace,它包含了从用户发起请求到收到响应的整个过程。
在当前任务中,Trace 还包含以下字段信息等:
- traceId:追踪的全局唯一标识符
- serviceName:服务名称
- spanName:跨度名称
Span
在分布式追踪中,Span 是构成 Trace 的基本单元。每个 Span 代表了请求在某个服务中的具体操作。
定义
Span 代表了分布式系统中具有开始时间和执行时长的逻辑运行单元。在当前任务中,Span 包含以下核心字段信息:
-
spanId:Span 的唯一标识符
-
parentSpanId:父级 Span 的 ID(用于构建调用树,表示 Span 间的父子关系)
-
traceId:整个调用链路的唯一标识符(与 Trace 关联)
-
serviceName:产生该 Span 的服务名称
-
spanName:Span 的名称,描述这个 Span 做了什么(例如:"PlaceOrder"、"Charge")
-
statusCode:状态码,用于表示 Span 的执行状态:
- 0/1:正常状态
- 2/3:错误/异常状态
-
statusMessage:错误文本或异常摘要,当 statusCode 为错误状态时提供详细的错误信息
-
resources:JSON 格式的扩展标签,包含额外的上下文信息,如:
- k8s.pod.ip:Kubernetes Pod 的 IP 地址
- k8s.node.name:Kubernetes 节点名称
- service.version:服务版本
Span 在 Trace 中的关系
在同一个 Trace 中,Span 之间通过 Parent-Child 关系构建出调用树:
less
Trace (购物下单)
├── Span (frontend: /cart/checkout)
│ ├── Span (checkout: PlaceOrder)
│ │ ├── Span (currency: GetCurrency)
│ │ ├── Span (cart: GetCart)
│ │ ├── Span (shipping: GetQuote)
│ │ └── Span (payment: Charge)
│ │ └── Span (loyalty: ApplyLoyalty)
│ └── Span (frontend: RenderResponse)
在上面的示例中:
frontend: /cart/checkout
是根 Span(Root Span)checkout: PlaceOrder
是frontend: /cart/checkout
的子 Spancurrency: GetCurrency
、cart: GetCart
等是checkout: PlaceOrder
的子 Span
在当前的根因分析任务中,Span 扮演着关键角色:
- 问题定位的基本单元:每个 Span 代表一个具体的操作,通过分析这些操作的状态可以定位问题发生的具体位置。
- 错误识别:通过检查 Span 的状态码(statusCode),我们可以识别出哪些操作执行失败了。在当前任务中,statusCode > 1 的 Span 被认为是错误 Span。
- 调用链分析:通过 traceId 和 parentSpanId,我们可以构建完整的调用链路,分析错误是如何在服务间传播的。
- 根本原因分析:通过分析大量的错误 Span,我们可以识别出最常见的错误模式,进而找出导致问题的根本原因服务。
在当前任务中,我们通过以下步骤使用 Span 进行根因分析:
- 筛选错误 Span:找出指定时间范围内所有 statusCode > 1 的 Span
- 模式分析:分析这些错误 Span 的特征,找出最常见的错误模式
- 根因识别:通过比较不同服务的错误频率,识别出最可能的根本原因服务
2.3 FindRootCauseSpans 类
FindRootCauseSpans 类是用于在分布式追踪数据中查找根因 Span 的核心组件。它通过分析错误 Span 的调用关系,识别出真正的根因 Span。
类构造函数
python
def __init__(self, client, project_name: str, logstore_name: str, region: str, start_time: str, end_time: str):
构造函数接收以下参数:
client
:已配置好凭证的阿里云日志服务客户端实例project_name
:日志项目名称logstore_name
:日志库名称region
:区域信息start_time
:查询开始时间end_time
:查询结束时间
find_root_cause_spans() 方法
find_root_cause_spans() 方法是 FindRootCauseSpans 类的核心方法,用于查找指定时间范围内的根因 Span。
-
构建查询条件:
iniall_spans_query = "statusCode>1"
构造查询语句,筛选出所有状态码大于1(表示错误)的 Span。
-
执行日志查询:
inirequest = GetLogsRequest( project=self.project_name, logstore=self.logstore_name, query=all_spans_query, fromTime=self.start_time, toTime=self.end_time, line=ERROR_TRACES ) response = self.client.get_logs(request) all_spans = [log_item.get_contents() for log_item in response.get_logs()] if response else []
使用阿里云日志服务 SDK 执行查询,获取指定时间范围内的所有错误 Span。
-
按 Trace 分组:
initrace_groups = {} for span in all_spans: trace_id = span.get('traceId') if trace_id: if trace_id not in trace_groups: trace_groups[trace_id] = [] trace_groups[trace_id].append(span)
将查询到的 Span 按照 traceId 进行分组,便于后续按 Trace 进行分析。
-
逐个处理 Trace:
iniall_root_cause_span_ids = [] for trace_id, trace_logs in trace_groups.items(): root_cause_span_ids = self.process_one_trace_log(trace_logs) all_root_cause_span_ids.extend(root_cause_span_ids)
对每个 Trace 调用 process_one_trace_log() 方法处理,找出该 Trace 中的根因 Span。
-
返回结果: 返回所有 Trace 中识别出的根因 Span ID 列表。
方法输出值
根据 notebook 中的执行结果,[find_root_cause_spans()] 方法的输出值为一个包含 Span ID 的列表:
markdown
找到 154 个根因span:
1. a83bb1e960e12216
2. 76af7f3f7141bbf1
3. 2d27a4a14d01d2da
4. 7c3d14ba208aba2d
5. 149605380d997ed7
6. 8f63518d7e900af7
7. 4a88854bf7632fdf
8. b7640166a3e0bf26
9. cbeb0704d0ea7c37
10. 529e883159d97406
... 还有 144 个
这表明方法成功识别出了 154 个根因 Span,其中每个 Span ID 都是字符串形式的唯一标识符。
process_one_trace_log() 方法
process_one_trace_log() 方法用于处理单个 Trace 中的 Span,找出该 Trace 中的根因 Span。
在该方法中,根因 Span 被定义为:
- 状态码大于1(表示错误)的 Span
- 该 Span 的所有子 Span 中没有状态码大于1的 Span
换句话说,根因 Span 是错误调用链中最后一个出错的 Span,它没有导致其他 Span 出错。
方法实现流程
-
建立映射关系:
- 建立 Span 状态映射(span_status)
- 建立父子 Span 关系映射(parent_spans 和 child_spans)
-
筛选错误 Span: 找出所有状态码大于1的 Span。
-
识别根因 Span: 对每个错误 Span,检查其所有子 Span 是否都为正常状态,如果是,则该 Span 为根因 Span。
-
返回结果: 返回该 Trace 中所有根因 Span 的 ID 列表。
2.4 主要执行流程
python
print("--- 开始执行根因SPAN查找任务 ---")
# 1. 创建带有STS凭证的SLS客户端
log_client_instance = create_sls_client_with_sts()
# 2. 如果客户端创建成功,则开始执行查找根因span任务
if log_client_instance:
# 3. 创建根因span查找器,传入客户端实例
root_cause_finder = FindRootCauseSpans(
client=log_client_instance,
project_name=PROJECT_NAME,
logstore_name=LOGSTORE_NAME,
region=REGION,
start_time=FAULT_START_TIME,
end_time=FAULT_END_TIME
)
print("\n开始查找根因spans...")
try:
root_cause_span_ids = root_cause_finder.find_root_cause_spans()
print(f"\n找到 {len(root_cause_span_ids)} 个根因span:")
for i, span_id in enumerate(root_cause_span_ids[:10]):
print(f"{i+1}. {span_id}")
if len(root_cause_span_ids) > 10:
print(f"... 还有 {len(root_cause_span_ids) - 10} 个")
except TeaException as e:
print(f"\n❌ 查询日志时发生错误: {e.message}")
print(f" 错误码: {e.code}")
print(" 请检查:1. 临时凭证是否已过期;2. 扮演的角色是否拥有对目标Project和Logstore的读权限。")
except Exception as e:
print(f"\n❌ 查询日志时发生未知错误: {e}")
else:
print("\n❌ 因无法创建SLS客户端,任务终止。")
主要执行流程包括以下步骤:
- 调用 create_sls_client_with_sts()函数创建SLS客户端实例
- 检查客户端是否创建成功
- 如果成功,实例化FindRootCauseSpans类,传入客户端和相关配置参数
- 调用 find_root_cause_spans()方法执行查找操作
- 获取并展示错误span ID列表供后续分析使用
- 处理可能发生的异常情况
3. 生成 SpanId 查询条件
在成功识别出根因 Span 后,下一步是将这些 Span ID 转换为可用于后续查询的条件语句。这一步的目的是构建一个 SPL(Search Processing Language)查询条件,以便在后续分析中精确地筛选出这些根因 Span。
3.1 核心代码实现
python
# 生成spanId查询条件
if root_cause_span_ids:
span_conditions = " or ".join([f"spanId='{span_id}'" for span_id in root_cause_span_ids])
print("生成的spanId查询列表:")
print(span_conditions[:500] + "..." if len(span_conditions) > 500 else span_conditions)
# 保存到变量供后续使用
SPAN_CONDITIONS = span_conditions
print(f"\n✅ 查询条件已保存,包含 {len(root_cause_span_ids)} 个spanId")
else:
print("❌ 未找到根因span,无法生成查询条件")
SPAN_CONDITIONS = ""
3.2 代码执行流程
-
检查根因 Span 列表:
- 首先检查
root_cause_span_ids
是否存在且非空 - 如果列表为空,输出错误信息并设置
SPAN_CONDITIONS
为空字符串
- 首先检查
-
构建查询条件:
- 使用列表推导式和
join()
方法将所有 Span ID 转换为查询条件 - 格式为:
spanId='id1' or spanId='id2' or spanId='id3' ...
- 这种格式可以直接用于 SPL 查询语句中
- 使用列表推导式和
-
输出和保存:
- 打印生成的查询条件(如果过长则截取前500个字符)
- 将完整的查询条件保存到全局变量
SPAN_CONDITIONS
中 - 输出成功信息,包含 Span ID 的总数
3.3 输入与输出
输入
-
root_cause_span_ids
:由 find_root_cause_spans()方法返回的根因 Span ID 列表 -
示例(部分):
css['a83bb1e960e12216', '76af7f3f7141bbf1', '2d27a4a14d01d2da', ...]
输出
-
SPAN_CONDITIONS
:格式化的 SPL 查询条件字符串 -
控制台输出示例:
ini生成的spanId查询列表: spanId='a83bb1e960e12216' or spanId='76af7f3f7141bbf1' or spanId='2d27a4a14d01d2da' or spanId='7c3d14ba208aba2d' or spanId='149605380d997ed7' or spanId='8f63518d7e900af7' or spanId='4a88854bf7632fdf' or spanId='b7640166a3e0bf26' or spanId='cbeb0704d0ea7c37' or spanId='529e883159d97406' or spanId='00d547ce56971266' or spanId='d6323829b16e4487' or spanId='615c90a7fb37a021' or spanId='e3f22206b6ecab24' or spanId='99ee8078b1864cbd' or spanId='3b82e6f4458fba65' or spanId='4aac6cf2bb6b9c16' or spanId=... ✅ 查询条件已保存,包含 154 个spanId
生成的 SPAN_CONDITIONS
查询条件将在后续的分析阶段中发挥关键作用:
- 错误特征分析:用于筛选出根因 Span 进行模式匹配分析
- 差异模式分析:作为异常组的标识条件
- 日志模式分析:用于精确匹配根因 Span 的日志内容
通过这种方式,我们可以确保后续的所有分析都基于之前识别出的根因 Span,从而提高分析的准确性和针对性。
4. 寻找报错特征
在识别出根因 Span 后,下一步是分析这些 Span 的特征,找出导致错误的共同模式。这一部分使用了阿里云 SLS 提供的智能分析函数,包括 get_patterns
和 diff_patterns
,来自动分析数据的组合,找出其中出现频率最高的模式。
4.1 使用 SPL 模式匹配分析错误特征
这部分代码使用 get_patterns
函数分析根因 Span 的错误特征。
less
from aliyun.log import GetLogsRequest
from datetime import datetime
if SPAN_CONDITIONS:
# 使用之前创建的SLS客户端
client = log_client_instance
# 构建错误特征分析查询,关键是调用阿里云SLS提供的智能分析函数get_patterns,自动分析数据的组合,找出其中出现频率最高的模式
pattern_analysis_query = f"""
* | set session enable_remote_functions=true ;
set session velox_support_row_constructor_enabled=true;
with t0 as (
select spanName, serviceName,
JSON_EXTRACT_SCALAR(resources, '$["k8s.pod.ip"]') AS pod_ip,
JSON_EXTRACT_SCALAR(resources, '$["k8s.node.name"]') AS node_name,
JSON_EXTRACT_SCALAR(resources, '$["service.version"]') AS service_version,
if((statusCode = 2 or statusCode = 3), 'true', 'false') as anomaly_label,
cast(if((statusCode = 2 or statusCode = 3), 1, 0) as double) as error_count
from log
where {SPAN_CONDITIONS}
),
t1 as (
select array_agg(spanName) as spanName,
array_agg(serviceName) as serviceName,
array_agg(pod_ip) as pod_ip,
array_agg(node_name) as node_name,
array_agg(service_version) as service_version,
array_agg(anomaly_label) as anomaly_label,
array_agg(error_count) as error_count
from t0
),
t2 as (
select row(spanName, serviceName) as table_row
from t1
),
t3 as (
select get_patterns(table_row, ARRAY['spanName', 'serviceName']) as ret
from t2
)
select * from t3
"""
SPL 语句详细解析
- 启用远程函数和 Velox 支持
ini
* | set session enable_remote_functions=true ;
set session velox_support_row_constructor_enabled=true;
说明:
set session enable_remote_functions=true;
:启用远程函数调用能力,允许在查询中使用如get_patterns()
等智能分析函数set session velox_support_row_constructor_enabled=true;
:启用 Velox 引擎对ROW()
构造器的支持,用于后续构建结构化数据行
- CTE
t0
: 提取原始字段并标记异常
csharp
with t0 as (
select spanName, serviceName,
JSON_EXTRACT_SCALAR(resources, '$["k8s.pod.ip"]') AS pod_ip,
JSON_EXTRACT_SCALAR(resources, '$["k8s.node.name"]') AS node_name,
JSON_EXTRACT_SCALAR(resources, '$["service.version"]') AS service_version,
if((statusCode = 2 or statusCode = 3), 'true', 'false') as anomaly_label,
cast(if((statusCode = 2 or statusCode = 3), 1, 0) as double) as error_count
from log
where {SPAN_CONDITIONS}
)
说明:
-
从
log
表中筛选出满足SPAN_CONDITIONS
条件的记录 -
提取多个关键属性:
spanName
: Span 名称serviceName
: 所属服务名pod_ip
,node_name
,service_version
: 来自resources
字段中的 Kubernetes 元信息
-
构造两个新字段:
anomaly_label
: 判断是否为异常(statusCode=2
或3
代表错误)error_count
: 错误计数器,用于后续聚合
- CTE
t1
: 聚合字段为数组
scss
t1 as (
select array_agg(spanName) as spanName,
array_agg(serviceName) as serviceName,
array_agg(pod_ip) as pod_ip,
array_agg(node_name) as node_name,
array_agg(service_version) as service_version,
array_agg(anomaly_label) as anomaly_label,
array_agg(error_count) as error_count
from t0
)
说明:
- 使用
array_agg()
将每个字段的所有值聚合成一个数组 - 目的是为了后续将这些字段组合成结构化的
ROW
类型,供get_patterns()
函数处理
- CTE
t2
: 构造结构化行数据
vbnet
t2 as (
select row(spanName, serviceName) as table_row
from t1
)
说明:
- 使用
row()
构造一个包含多个字段的结构体(类似结构体或元组),这里只包含了spanName
和serviceName
- 之所以这么做是为了适配
get_patterns()
函数所需的输入格式:它期望接收一个结构化数据行(ROW
)以及对应的列名数组
- CTE
t3
: 应用get_patterns()
函数进行模式分析
csharp
t3 as (
select get_patterns(table_row, ARRAY['spanName', 'serviceName']) as ret
from t2
)
说明:
- 调用阿里云 SLS 提供的智能分析函数
get_patterns()
- 第一个参数:结构化行数据
table_row
- 第二个参数:列名数组,告诉函数哪些字段参与模式识别
- 返回值是一个复杂的嵌套数组结构,表示发现的模式及其频次
- 最终输出结果
csharp
select * from t3
说明:
- 输出最终的模式识别结果
代码执行流程
-
检查查询条件:
- 首先检查
SPAN_CONDITIONS
是否存在且非空
- 首先检查
-
构建分析查询:
-
使用之前创建的 SLS 客户端
-
构建 SPL 查询语句,使用
get_patterns
函数分析数据模式 -
查询中提取了多个维度的信息:
spanName
:Span 名称serviceName
:服务名称pod_ip
:Kubernetes Pod 的 IP 地址node_name
:Kubernetes 节点名称service_version
:服务版本anomaly_label
:异常标签('true' 表示错误,'false' 表示正常)error_count
:错误计数
-
-
执行查询:
- 使用
GetLogsRequest
构造查询请求 - 调用客户端的
get_logs
方法执行查询
- 使用
输入与输出
输入
-
SPAN_CONDITIONS
:之前生成的根因 Span 查询条件 -
示例:
inispanId='a83bb1e960e12216' or spanId='76af7f3f7141bbf1' or spanId='2d27a4a14d01d2da' ...
输出
-
get_patterns_result
:模式分析结果 -
控制台输出示例:
csharp错误特征分析结果 (1 条记录): ret: [["serviceName=payment","serviceName=frontend-proxy"],[152,2],null,null]
输出结果的含义:
- 第一个数组
["serviceName=payment","serviceName=frontend-proxy"]
:识别出的模式 - 第二个数组
[152,2]
:对应模式的出现次数 - 其他字段为 null
从结果可以看出,
payment
服务出现了 152 次错误,是主要的错误来源。 - 第一个数组
在根因分析中的作用
- 识别主要错误服务:通过分析不同服务的错误频率,找出最可能的根因服务
- 模式发现:自动发现错误 Span 中的共同模式,减少人工分析工作量
- 数据聚合:将分散的 Span 数据聚合成可分析的模式
通过 get_patterns
函数,我们可以快速识别出错误最频繁出现的服务,为后续的根因定位提供重要线索。
总结:整个流程的作用
步骤 | 功能 | 说明 |
---|---|---|
CTE t0 | 数据提取与清洗 | 从日志中提取关键字段并标记错误 |
CTE t1 | 数据聚合 | 将字段转为数组,便于构造结构体 |
CTE t2 | 构造结构体 | 为 get_patterns 构造输入数据格式 |
CTE t3 | 模式识别 | 使用 get_patterns 分析高频错误模式 |
最终输出 | 展示结果 | 展示识别出的模式及频率 |
补充
在数据分析和根因分析的上下文中,模式是指在数据中反复出现的、具有统计意义的特征组合或规律。
模式可以理解为:
- 重复出现的特征组合:某些属性值的特定组合在数据集中频繁出现
- 统计上的显著性:这些组合的出现频率明显高于随机分布
- 业务意义的相关性:这些组合往往与特定的业务场景或问题相关
在当前场景中,模式指的是错误Span中常见的属性组合,例如:
css
["serviceName=payment", "spanName=ProcessPayment"]
["serviceName=user-service", "spanName=ValidateUser"]
这些模式告诉我们:
- 哪些服务最容易出错
- 哪些接口调用最容易失败
- 错误发生的特定上下文环境
模式的价值
-
快速定位问题热点:
makefile模式: ["serviceName=payment"] 频次: 152次 含义: payment服务是主要的错误来源
-
发现隐藏的关联关系:
less模式: ["serviceName=payment", "pod_ip=10.0.1.23"] 频次: 89次 含义: 特定Pod上的payment服务问题严重
-
减少人工分析工作量:
- 自动识别出人眼难以察觉的复杂组合
- 量化每个模式的重要性
假设有以下错误Span数据:
serviceName | spanName | pod_ip | 错误次数 |
---|---|---|---|
payment | ProcessPayment | 10.0.1.23 | 50 |
payment | ProcessPayment | 10.0.1.24 | 30 |
user-service | ValidateUser | 10.0.2.15 | 25 |
payment | ProcessPayment | 10.0.1.23 | 40 |
order-service | CreateOrder | 10.0.3.8 | 15 |
通过模式分析,可能会发现:
-
单一模式:
css["serviceName=payment"] → 出现120次
-
组合模式:
css["serviceName=payment", "spanName=ProcessPayment"] → 出现120次 ["serviceName=payment", "pod_ip=10.0.1.23"] → 出现90次
get_patterns函数的工作原理
css
get_patterns(table_row, ARRAY['spanName', 'serviceName'])
这个函数会:
- 枚举所有可能的属性组合
- 统计每种组合的出现频次
- 按照频次降序排列
- 返回最显著的模式
输出结果解读
csharp
ret: [["serviceName=payment","serviceName=frontend-proxy"],[152,2],null,null]
-
模式列表 :
["serviceName=payment","serviceName=frontend-proxy"]
-
对应频次 :
[152,2]
-
含义:
serviceName=payment
模式出现了152次(主要问题)serviceName=frontend-proxy
模式出现了2次(次要问题)
4.2 使用 diff_patterns 分析异常特征差异
这部分代码使用 diff_patterns
函数分析根因 Span 与非根因错误 Span 的差异特征。
核心代码实现
less
# 使用diff_patterns函数分析根因span与非根因错误span的差异特征
# - 异常组('true'): 根因span (在SPAN_CONDITIONS列表中的span)
# - 对照组('false'): 非根因的错误span (statusCode>0但不在根因列表中的span)
# 🎯 分析目的:
# - 找出根因span与其他错误span在服务名等维度上的差异模式
# - 识别根因span的独特特征,帮助验证根因识别的准确性
if SPAN_CONDITIONS:
diff_pattern_query = f"""
statusCode>0 | set session enable_remote_functions=true ;
set session velox_support_row_constructor_enabled=true;
with t0 as (
select spanName, serviceName,
JSON_EXTRACT_SCALAR(resources, '$["k8s.pod.ip"]') AS pod_ip,
JSON_EXTRACT_SCALAR(resources, '$["k8s.node.name"]') AS node_name,
JSON_EXTRACT_SCALAR(resources, '$["service.version"]') AS service_version,
if(({SPAN_CONDITIONS}), 'true', 'false') as anomaly_label,
cast(if((statusCode = 2 or statusCode = 3), 1, 0) as double) as error_count
from log
),
t1 as (
select array_agg(spanName) as spanName,
array_agg(serviceName) as serviceName,
array_agg(pod_ip) as pod_ip,
array_agg(node_name) as node_name,
array_agg(service_version) as service_version,
array_agg(anomaly_label) as anomaly_label,
array_agg(error_count) as error_count
from t0
),
t2 as (
select row(serviceName, anomaly_label) as table_row
from t1
),
t3 as (
select diff_patterns(table_row, ARRAY['serviceName', 'anomaly_label'], 'anomaly_label', 'true', 'false') as ret
from t2
)
select * from t3
"""
SPL 语句详细解析
- 查询条件和会话设置
ini
statusCode>0 | set session enable_remote_functions=true ;
set session velox_support_row_constructor_enabled=true;
说明:
statusCode>0
:筛选所有错误Span(包括根因Span和非根因错误Span)- 启用远程函数和Velox支持,为后续的
diff_patterns
函数调用做准备
- CTE
t0
: 数据提取和分组标记
javascript
with t0 as (
select spanName, serviceName,
JSON_EXTRACT_SCALAR(resources, '$["k8s.pod.ip"]') AS pod_ip,
JSON_EXTRACT_SCALAR(resources, '$["k8s.node.name"]') AS node_name,
JSON_EXTRACT_SCALAR(resources, '$["service.version"]') AS service_version,
if(({SPAN_CONDITIONS}), 'true', 'false') as anomaly_label,
cast(if((statusCode = 2 or statusCode = 3), 1, 0) as double) as error_count
from log
)
说明:
-
提取关键字段信息
-
关键区别 :使用
if(({SPAN_CONDITIONS}), 'true', 'false') as anomaly_label
来标记分组'true'
:根因Span(在SPAN_CONDITIONS列表中的Span)'false'
:非根因错误Span(statusCode>0但不在根因列表中的Span)
-
这样就创建了两个对比组:异常组和对照组
- CTE
t1
: 数据聚合
scss
t1 as (
select array_agg(spanName) as spanName,
array_agg(serviceName) as serviceName,
array_agg(pod_ip) as pod_ip,
array_agg(node_name) as node_name,
array_agg(service_version) as service_version,
array_agg(anomaly_label) as anomaly_label,
array_agg(error_count) as error_count
from t0
)
说明:
- 将所有字段聚合成数组,为构造结构化行数据做准备
- CTE
t2
: 构造结构化行数据
vbnet
t2 as (
select row(serviceName, anomaly_label) as table_row
from t1
)
说明:
- 构造包含
serviceName
和anomaly_label
的结构化行数据 anomaly_label
作为分组标识字段,用于后续的差异分析
- CTE
t3
: 应用diff_patterns函数进行差异分析
csharp
t3 as (
select diff_patterns(table_row, ARRAY['serviceName', 'anomaly_label'], 'anomaly_label', 'true', 'false') as ret
from t2
)
说明:
-
调用
diff_patterns
函数进行差异模式分析 -
参数解释:
table_row
:结构化行数据ARRAY['serviceName', 'anomaly_label']
:参与分析的字段'anomaly_label'
:分组字段名'true'
:异常组标识'false'
:对照组标识
- 最终输出
csharp
select * from t3
代码执行流程
-
检查查询条件:
- 首先检查
SPAN_CONDITIONS
是否存在且非空
- 首先检查
-
构建差异分析查询:
-
查询条件为
statusCode>0
,包含所有错误 Span(不仅限于根因 Span) -
使用
if(({SPAN_CONDITIONS}), 'true', 'false') as anomaly_label
来标记 Span:'true'
:根因 Span(在 SPAN_CONDITIONS 列表中的 Span)'false'
:非根因的错误 Span(statusCode>0 但不在根因列表中的 Span)
-
使用
diff_patterns
函数比较异常组和对照组的差异
-
-
执行查询:
- 使用
GetLogsRequest
构造查询请求 - 调用客户端的
get_logs
方法执行查询
- 使用
diff_patterns 函数详解
diff_patterns(table_row, field_names, group_field, positive_group, negative_group)
参数说明:
table_row
:结构化行数据field_names
:参与分析的字段名数组group_field
:用于分组的字段名positive_group
:正数组(异常组)的标识值negative_group
:负数组(对照组)的标识值
工作原理:
- 根据
group_field
将数据分为两组 - 分别统计每组中各字段组合的出现频次
- 计算两组之间的差异度量
- 识别在正数组中显著高于负数组的模式
输入与输出
输入
SPAN_CONDITIONS
:之前生成的根因 Span 查询条件- 查询范围:所有
statusCode>0
的 Span(包括根因和非根因错误 Span)
输出
-
diff_patterns_result
:差异模式分析结果 -
控制台输出示例:
less\n差异模式分析结果 (1 条记录): ret: [[""serviceName"='payment'",""serviceName"='frontend-proxy'"],[152,2],[0,304],[0.987012987012987,0.012987012987012988],[0.0,0.019740259740259743],[0.987012987012987,-0.006753246753246754],[1.0,1.0],[0.0,0.0],null]
输出结果的含义(数组各项):
0.[""serviceName"='payment'",""serviceName"='frontend-proxy'"]
:识别出的模式[152,2]
:异常组中各模式的出现次数[0,304]
:对照组中各模式的出现次数[0.987012987012987,0.012987012987012988]
:异常组中各模式的占比[0.0,0.019740259740259743]
:对照组中各模式的占比[0.987012987012987,-0.006753246753246754]
:各模式在异常组和对照组中的差异值[1.0,1.0]
:各模式的显著性[0.0,0.0]
:各模式的效应大小null
:其他信息
结果解读:
payment
服务在异常组中出现152次,占比98.7%payment
服务在对照组中出现0次,占比0%- 这表明
payment
服务是根因Span的显著特征,几乎只在根因Span中出现
在根因分析中的作用
- 差异识别:通过比较根因 Span 和非根因错误 Span,识别出根因 Span 的独特特征
- 验证准确性 :帮助验证之前通过
get_patterns
识别出的根因服务的准确性 - 增强置信度:通过对比分析增强对根因识别结果的置信度
- 量化分析:提供量化的差异度量,而非简单的频次统计
对比
特性 | get_patterns | diff_patterns |
---|---|---|
分析对象 | 单一数据集 | 两个对比数据集 |
分析目标 | 找出高频模式 | 找出组间差异模式 |
输出结果 | 模式+频次 | 模式+多维度差异指标 |
应用场景 | 模式发现 | 差异分析、根因验证 |
5. 交叉验证日志特征
在通过 Span 分析识别出可能的根因服务后,下一步是通过日志内容分析来进一步验证根因。这部分使用了阿里云 SLS 提供的日志模式识别功能,通过两阶段查询分析日志模式。
5.1 核心代码实现
日志模式分析采用两阶段查询方法:
第一阶段:获取 model_id
ini
# 第一阶段:获取model_id
log_pattern_query_stage1 = """
statusCode > 0 | set session enable_remote_functions=true;
set session velox_support_row_constructor_enabled=true;
with t0 as (
select statusCode, statusMessage, serviceName,
CONCAT('[', serviceName, '] ', statusMessage) as combined_message
from log
),
t1 as (
select array_agg(combined_message) as contents
from t0
),
t2 as (
-- 调用get_log_patterns函数进行模式学习
select
contents,
get_log_patterns(contents, ARRAY[' ', '\n', '\t', '\r', '\f', '\v', ':', ',', '[', ']'], null, null, '{"threshold": 3, "tolerance": 0.3, "maxDigitRatio": 0.3}') as ret
from t1
)
select
ret.model_id as model_id,
ret.error_msg as error_msg
from t2
"""
print("第一阶段:获取model_id...")
stage1_request = GetLogsRequest(
project=PROJECT_NAME,
logstore=LOGSTORE_NAME,
query=log_pattern_query_stage1,
fromTime=start_timestamp,
toTime=end_timestamp,
line=10
)
try:
stage1_response = client.get_logs(stage1_request)
if stage1_response and stage1_response.get_count() > 0:
# 获取model_id
first_log = stage1_response.get_logs()[0]
stage1_contents = first_log.get_contents()
model_id = stage1_contents.get("model_id", "")
error_msg = stage1_contents.get("error_msg", "")
print(f"✅ 成功获取model_id: {model_id}")
if error_msg:
print(f"错误信息: {error_msg}")
- 会话设置和查询条件
ini
statusCode > 0 | set session enable_remote_functions=true;
set session velox_support_row_constructor_enabled=true;
说明:
statusCode > 0
:筛选所有错误状态的日志记录- 启用远程函数和Velox支持,为
get_log_patterns
函数调用做准备
- CTE
t0
: 数据提取和消息组合
csharp
with t0 as (
select statusCode, statusMessage, serviceName,
CONCAT('[', serviceName, '] ', statusMessage) as combined_message
from log
)
说明:
- 提取关键字段:
statusCode
、statusMessage
、serviceName
- 使用
CONCAT
函数将服务名和服务消息组合成统一格式:[serviceName] statusMessage
- 这种格式有助于模式识别函数更好地理解日志结构
- CTE
t1
: 数据聚合
vbnet
t1 as (
select array_agg(combined_message) as contents
from t0
)
说明:
- 使用
array_agg
将所有组合后的消息聚合成一个数组 - 为后续的模式学习函数提供输入数据格式
- CTE
t2
: 调用模式学习函数
csharp
t2 as (
select
contents,
get_log_patterns(contents, ARRAY[' ', '\n', '\t', '\r', '\f', '\v', ':', ',', '[', ']'], null, null, '{"threshold": 3, "tolerance": 0.3, "maxDigitRatio": 0.3}') as ret
from t1
)
说明:
-
调用
get_log_patterns
函数进行日志模式学习 -
参数详解:
-
contents
:日志内容数组 -
ARRAY[' ', '\n', '\t', '\r', '\f', '\v', ':', ',', '[', ']'
:分隔符数组,用于切分日志内容 -
第三个参数:保留字段(null)
-
第四个参数:保留字段(null)
-
'{"threshold": 3, "tolerance": 0.3, "maxDigitRatio": 0.3}'
:配置参数threshold
: 最小出现次数阈值(3次以上才被认为是模式)tolerance
: 容忍度(0.3表示允许30%的变异)maxDigitRatio
: 最大数字比例(控制数字变量的提取)
-
- 最终输出
csharp
select
ret.model_id as model_id,
ret.error_msg as error_msg
from t2
说明:
- 输出模型ID和可能的错误信息
第二阶段:使用 model_id 进行模式匹配
ini
# 第二阶段:使用model_id进行模式匹配
if model_id:
log_pattern_query_stage2 = f"""
statusCode > 0 | set session presto_velox_mix_run_not_check_linked_agg_enabled=true;
set session presto_velox_mix_run_support_complex_type_enabled=true;
set session velox_sanity_limit_enabled=false;
set session enable_remote_functions=true;
with t0 as (
select CONCAT('[', serviceName, '] ', statusMessage) as combined_message
from log
)
select
ret.is_matched as is_matched,
ret.pattern_id as pattern_id,
ret.pattern as pattern,
ret.regexp as pattern_regexp,
ret.variables as variables,
combined_message as content
from (
-- 调用match_log_patterns函数,使用第一阶段生成的model_id进行匹配
select match_log_patterns('{model_id}', combined_message) as ret, combined_message
from t0
)
where ret.is_matched = true
limit 50000
"""
print("第二阶段:使用model_id进行模式匹配...")
stage2_request = GetLogsRequest(
project=PROJECT_NAME,
logstore=LOGSTORE_NAME,
query=log_pattern_query_stage2,
fromTime=start_timestamp,
toTime=end_timestamp,
line=50000
)
stage2_response = client.get_logs(stage2_request)
- 会话设置
ini
statusCode > 0 | set session presto_velox_mix_run_not_check_linked_agg_enabled=true;
set session presto_velox_mix_run_support_complex_type_enabled=true;
set session velox_sanity_limit_enabled=false;
set session enable_remote_functions=true;
说明:
- 启用多种会话参数以支持复杂的模式匹配操作
velox_sanity_limit_enabled=false
:关闭限制,允许处理大量数据
- CTE
t0
: 数据准备
csharp
with t0 as (
select CONCAT('[', serviceName, '] ', statusMessage) as combined_message
from log
)
说明:
- 与第一阶段相同,准备统一格式的日志消息
- 模式匹配和筛选
csharp
select
ret.is_matched as is_matched,
ret.pattern_id as pattern_id,
ret.pattern as pattern,
ret.regexp as pattern_regexp,
ret.variables as variables,
combined_message as content
from (
select match_log_patterns('{model_id}', combined_message) as ret, combined_message
from t0
)
where ret.is_matched = true
limit 50000
说明:
- 调用
match_log_patterns
函数,使用第一阶段生成的model_id
进行模式匹配 - 筛选匹配成功的记录(
is_matched = true
) - 输出匹配结果的详细信息
5.2 代码执行流程
-
第一阶段 - 模型训练:
- 查询所有
statusCode > 0
的日志记录 - 将日志内容组合成特定格式:
[serviceName] statusMessage
- 调用
get_log_patterns
函数进行模式学习,生成日志模式模型 - 获取模型 ID(model_id)用于后续匹配
- 查询所有
-
第二阶段 - 模式匹配:
- 使用第一阶段生成的 model_id
- 调用
match_log_patterns
函数对日志进行模式匹配 - 筛选出成功匹配的日志记录
- 对匹配结果进行统计分析
-
结果统计和分析:
- 统计每个模式的出现次数
- 提取服务名进行错误次数统计
- 按出现频率排序展示结果
get_log_patterns 函数详解
get_log_patterns(contents, delimiters, reserved, reserved, config)
参数说明:
-
contents
: 日志内容数组 -
delimiters
: 分隔符数组 -
config
: 配置参数JSON字符串threshold
: 最小出现次数阈值tolerance
: 模式容忍度maxDigitRatio
: 最大数字比例
工作原理:
- 使用分隔符切分日志内容
- 识别重复出现的文本模式
- 提取变量部分(如数字、变化的文本)
- 生成模式模板和正则表达式
- 返回模型ID用于后续匹配
match_log_patterns 函数详解
match_log_patterns(model_id, log_content)
参数说明:
model_id
: 模式模型IDlog_content
: 待匹配的日志内容
返回值:
is_matched
: 是否匹配成功pattern_id
: 匹配的模式IDpattern
: 模式模板regexp
: 对应的正则表达式variables
: 提取的变量值
5.3 输入与输出
输入
- 时间范围:
start_timestamp
到end_timestamp
- 查询条件:所有
statusCode > 0
的日志记录 - 日志内容:
serviceName
和statusMessage
字段
输出
-
控制台输出示例:
makefile执行日志模式分析查询... 第一阶段:获取model_id... ✅ 成功获取model_id: cc657176-9f58-46fd-9bdc-f78572b3c842 错误信息: null 第二阶段:使用model_id进行模式匹配... \n✅ 日志模式匹配结果 (15554 条记录): 🔍 主要日志模式 (按出现频率排序): 注意: 只显示服务名的模式通常表示该服务的statusMessage为空 有具体错误信息的模式更可能指向真正的根因 出现次数: 15250 模式: [<*>] -------------------------------------------------------------------------------- 出现次数: 152 模式: [checkout] Payment request failed. Invalid token. app.loyalty.level=gold -------------------------------------------------------------------------------- 出现次数: 152 模式: [checkout] rpc error: code = Internal desc = failed to charge card: could not charge the card: rpc error: code = Unknown desc = Payment request failed. Invalid token. app.loyalty.level=gold -------------------------------------------------------------------------------- 📊 涉及的服务排序 (按错误频率): currency: 13418 次错误 checkout: 918 次错误 frontend: 608 次错误 frontend-proxy: 306 次错误 load-generator: 152 次错误 payment: 152 次错误
结果解读
-
高频模式分析:
[*]
:出现15250次,通常是statusMessage
为空的日志[checkout] Payment request failed...
:出现152次,包含具体错误信息[checkout] rpc error...
:出现152次,详细的RPC错误堆栈
-
服务错误频率:
currency
: 13418次错误(最高)checkout
: 918次错误payment
: 152次错误
5.4 在根因分析中的作用
- 内容验证:通过对日志内容的模式分析,验证之前通过 Span 分析得出的根因结论
- 细节补充:提供具体的错误信息,帮助更深入地理解问题原因
- 多维度确认:从日志内容角度提供额外证据,增强根因分析的可靠性
- 异常检测:识别异常的日志模式,发现潜在的系统问题
交叉验证的价值
通过将Span分析结果与日志模式分析结果进行对比:
分析维度 | Span分析结果 | 日志分析结果 | 交叉验证结论 |
---|---|---|---|
主要问题服务 | payment (152次) | checkout (918次) | 需要进一步分析 |
错误模式 | 服务调用失败 | 具体错误信息 | 互为补充 |
置信度 | 中等 | 高 | 增强整体置信度 |
6. 结果总结
在完成所有分析步骤后,最后一步是汇总分析结果并给出最终的诊断结论。这部分代码综合了前面所有分析步骤的结果,通过解析 get_patterns
和 diff_patterns
的输出,确定最终的根因服务。
6.1 核心代码实现
swift
# 从运行时收集的证据中动态提取TARGET_SERVICE
def parse_service_from_evidence():
"""从get_patterns和diff_patterns的实际运行结果中提取目标服务"""
target_service = "unknown"
confidence_score = 0
# 1. 解析get_patterns结果
# 格式: [["serviceName=product-catalog","serviceName=frontend-proxy","serviceName=frontend"],[276,11,2],null,null]
if 'get_patterns_result' in globals() and get_patterns_result:
try:
# Parse the string format from SLS: [["serviceName=cart","serviceName=frontend-proxy"],[139,1],null,null]
if isinstance(get_patterns_result, str):
# Replace 'null' with 'None' for Python parsing
data_str = get_patterns_result.replace('null', 'None')
result = eval(data_str) # Safely parse the array
else:
result = get_patterns_result
if len(result) >= 2 and isinstance(result[0], list) and isinstance(result[1], list):
service_patterns = result[0] # 服务名模式
service_counts = result[1] # 对应的计数
# 提取服务名并找出计数最高的
max_count = 0
for i, pattern in enumerate(service_patterns):
if i < len(service_counts):
count = service_counts[i]
# 从 "serviceName=product-catalog" 中提取服务名
if "serviceName=" in pattern:
service = pattern.split("serviceName=")[1].strip('"'')
if count > max_count:
max_count = count
target_service = service
confidence_score = max_count
print(f" ✅ get_patterns发现主要错误服务: {service} (错误数: {count})")
except Exception as e:
print(f" ⚠️ get_patterns结果解析失败: {e}")
# 2. 解析diff_patterns结果进行验证
# 格式: [[""serviceName"='product-catalog'"],[276],[2684],[0.955...]...]
if 'diff_patterns_result' in globals() and diff_patterns_result:
try:
# Parse the string format from SLS: [[""serviceName"='cart'"],[139],[0]...]
if isinstance(diff_patterns_result, str):
# Replace 'null' with 'None' for Python parsing
data_str = diff_patterns_result.replace('null', 'None')
result = eval(data_str) # Safely parse the array
else:
result = diff_patterns_result
if len(result) >= 1 and isinstance(result[0], list):
anomaly_patterns = result[0] # 异常模式
# 从异常模式中提取服务名
for pattern in anomaly_patterns:
if "serviceName" in pattern and "=" in pattern:
# 从 ""serviceName"='product-catalog'" 中提取服务名
service = pattern.split("='")[1].strip("'"")
print(f" ✅ diff_patterns确认异常服务: {service}")
# 如果get_patterns也找到了同样的服务,增加置信度
if service == target_service:
print(f" ✅ 多重证据确认: {service} 是主要根因服务")
return target_service, True # 高置信度
elif target_service == "unknown":
# 如果get_patterns没找到,使用diff_patterns的结果
target_service = service
return target_service, True
except Exception as e:
print(f" ⚠️ diff_patterns结果解析失败: {e}")
# 3. 返回结果
if target_service != "unknown":
return target_service, True
else:
print(f" ❌ 无法从运行时证据中提取明确的目标服务")
return "unknown", False
# 解析运行时证据
if error_span_evidence:
TARGET_SERVICE, pattern_evidence = parse_service_from_evidence()
if pattern_evidence:
print(f" ✅ 根据运行时证据确定目标服务: {TARGET_SERVICE}")
else:
print(f" ❌ 无足够的模式分析证据")
else:
print(f" ❌ 无足够的模式分析证据")
TARGET_SERVICE = "unknown"
pattern_evidence = False
print(f"\n🏆 根因候选:")
# 基于evidence的评估
evidence = error_span_evidence and pattern_evidence
if evidence:
root_cause_candidate = f"{TARGET_SERVICE}"
confidence = "高"
print(f" 🎯 {root_cause_candidate}")
print(f" 📈 置信度:{confidence}")
print(f" ✅ 证据:TRUE(已检测到异常)")
print(f" 📝 支持证据:")
print(f" - 发现 {len(root_cause_span_ids)} 个错误span")
print(f" - diff_patterns分析表明 {TARGET_SERVICE} 服务异常")
print(f" - 日志模式分析验证了服务问题")
print(f" - 错误特征分析确认了根因位置")
elif error_span_evidence:
root_cause_candidate = "unknown"
confidence = "中"
print(f" 🎯 {root_cause_candidate}")
print(f" 📈 置信度:{confidence}")
print(f" ❌ 证据:FALSE(模式不够明确)")
print(f" 📝 支持证据:")
print(f" - 发现 {len(root_cause_span_ids)} 个错误span")
print(f" - 但模式分析结果不够明确")
else:
root_cause_candidate = "unknown"
confidence = "低"
print(f" 🎯 {root_cause_candidate}")
print(f" 📈 置信度:{confidence}")
print(f" ❌ 证据:FALSE(证据不足)")
print(f" 📝 支持证据:")
print(f" - 错误span或模式分析数据有限")
print(f"\n💡 建议:")
if evidence:
print(f" - 检查 {TARGET_SERVICE} 服务的健康状态")
print(f" - 查看 {TARGET_SERVICE} 的错误日志和异常")
print(f" - 验证 {TARGET_SERVICE} 的部署和配置")
print(f" - 考虑重启或修复 {TARGET_SERVICE} 服务")
elif confidence == "中":
print(f" - 进一步分析错误span的分布模式")
print(f" - 检查多个可疑服务的状态")
print(f" - 扩大时间范围进行分析")
else:
print(f" - 调整分析参数(时间范围等)")
print(f" - 核查数据是否完整")
print(f" - 考虑其他分析方法")
print(f"\n" + "="*60)
print(f"🎯 最终答复:{root_cause_candidate}")
print(f"📈 置信度:{confidence}")
print(f"🔍 证据:{'TRUE' if evidence else 'FALSE'}")
print(f"" + "="*60)
6.2 证据生成的完整流程
第一步:根因Span识别生成基础证据
证据来源 :第3章异常检测结果 证据内容:
root_cause_span_ids
:识别出的具体异常Span ID列表error_span_evidence
:布尔值,表示是否发现异常Span
证据生成过程:
- 执行异常检测算法,识别出异常的Span
- 统计异常Span数量:
len(root_cause_span_ids)
- 设置证据标志:
error_span_evidence = True
证据示例:
css
发现 154 个错误span
第二步:get_patterns分析生成高频模式证据
证据来源 :第4.1章get_patterns分析结果 证据内容:
get_patterns_result
:高频模式分析结果
证据生成过程:
- 对根因Span进行模式分析
- 识别出高频出现的服务名组合
- 统计各模式的出现频次
证据示例:
csharp
[["serviceName=payment","serviceName=frontend-proxy"],[152,2],null,null]
解析结果:
makefile
get_patterns发现主要错误服务: payment (错误数: 152)
第三步:diff_patterns分析生成差异特征证据
证据来源 :第4.2章diff_patterns分析结果 证据内容:
diff_patterns_result
:差异模式分析结果
证据生成过程:
- 对比根因Span和非根因错误Span
- 识别在根因组中显著高于对照组的模式
- 计算差异度量指标
证据示例:
css
[[""serviceName"='payment'"],[152],[0],[0.987...],[0.0],[0.987...],[1.0],[0.0],null]
解析结果:
makefile
diff_patterns确认异常服务: payment
✅ 多重证据确认: payment 是主要根因服务
第四步:日志模式分析生成内容验证证据
证据来源 :第5章日志模式分析结果 证据内容:
- 具体的错误日志模式和频率统计
证据生成过程:
- 对所有错误日志进行模式学习
- 识别重复出现的日志模式
- 统计各服务的错误频率
证据示例:
ini
出现次数: 152
模式: [checkout] Payment request failed. Invalid token. app.loyalty.level=gold
...
📊 涉及的服务排序 (按错误频率):
payment: 152 次错误
6.3 证据整合与评估流程
证据整合顺序
证据评估标准
证据类型 | 评估标准 | 权重 |
---|---|---|
根因Span证据 | 是否发现异常Span | 30% |
高频模式证据 | 是否识别出主要错误服务 | 30% |
差异特征证据 | 是否通过差异验证 | 25% |
内容验证证据 | 日志模式是否支持结论 | 15% |
置信度计算逻辑
ini
# 第一层评估:基础证据检查
if not error_span_evidence:
# 无基础证据 → 低置信度
confidence = "低"
return
# 第二层评估:模式分析证据检查
if not pattern_evidence:
# 有基础证据但无模式证据 → 中置信度
confidence = "中"
return
# 第三层评估:多重证据验证
if get_patterns证据 and diff_patterns证据 and 一致性检查:
# 多重证据一致 → 高置信度
confidence = "高"
6.4 证据链完整性验证
完整证据链示例
less
证据1 - 基础证据:
- 发现 154 个错误span (来自第3章)
证据2 - 高频模式证据:
- get_patterns发现主要错误服务: payment (错误数: 152) (来自第4.1章)
证据3 - 差异特征证据:
- diff_patterns确认异常服务: payment
- 多重证据确认: payment 是主要根因服务 (来自第4.2章)
证据4 - 内容验证证据:
- 日志模式分析显示payment服务有152次错误 (来自第5章)
证据一致性检查
csharp
# 多重证据交叉验证
def validate_evidence_consistency():
evidence_chain = []
# 检查各证据的一致性
if get_patterns_service == diff_patterns_service:
evidence_chain.append("✓ 服务名一致性验证通过")
if log_pattern_frequency[get_patterns_service] > threshold:
evidence_chain.append("✓ 日志频率验证通过")
if len(root_cause_span_ids) > min_span_count:
evidence_chain.append("✓ Span数量验证通过")
return len(evidence_chain) == 4 # 所有证据都通过验证
6.5 最终诊断生成
诊断结论生成流程
-
证据收集:
- 收集所有分析阶段的输出结果
-
证据解析:
- 解析各阶段的复杂数据结构
- 提取关键信息(主要是服务名)
-
证据验证:
- 检查证据的完整性和一致性
- 进行多重交叉验证
-
置信度评估:
- 根据证据完整性评估置信度等级
-
结论输出:
- 生成最终的根因诊断报告
输出格式标准化
python
{
"root_cause": "payment",
"confidence": "高",
"evidence": True,
"supporting_evidence": [
"发现 154 个错误span",
"diff_patterns分析表明 payment 服务异常",
"日志模式分析验证了服务问题",
"错误特征分析确认了根因位置"
],
"recommendations": [
"检查 payment 服务的健康状态",
"查看 payment 的错误日志和异常",
"验证 payment 的部署和配置",
"考虑重启或修复 payment 服务"
]
}
6.6 代码执行流程
-
证据解析:
- 解析
get_patterns_result
,提取出现频率最高的服务 - 解析
diff_patterns_result
,验证根因服务的一致性 - 通过多重证据确认提高置信度
- 解析
-
置信度评估:
- 根据是否有足够的证据(错误 Span 和模式分析结果)评估置信度
- 分为高、中、低三个等级
-
诊断结论生成:
- 输出根因候选服务
- 给出置信度评估
- 提供处理建议
6.7 输入与输出
输入
get_patterns_result
:get_patterns
函数的分析结果diff_patterns_result
:diff_patterns
函数的分析结果root_cause_span_ids
:识别出的根因 Span ID 列表
输出
-
控制台输出示例:
diff🏆 根因候选: 🎯 payment 📈 置信度:高 ✅ 证据:TRUE(已检测到异常) 📝 支持证据: - 发现 154 个错误span - diff_patterns分析表明 payment 服务异常 - 日志模式分析验证了服务问题 - 错误特征分析确认了根因位置 💡 建议: - 检查 payment 服务的健康状态 - 查看 payment 的错误日志和异常 - 验证 payment 的部署和配置 - 考虑重启或修复 payment 服务 ============================================================ 🎯 最终答复:payment 📈 置信度:高 🔍 证据:TRUE ============================================================
6.8 在根因分析中的作用
- 证据整合:将分散在各分析阶段的证据进行系统性整合
- 置信度评估:通过多维度证据评估根因识别的可信度
- 结论验证:通过交叉验证确保诊断结论的准确性
- 结果呈现:以标准化格式呈现分析结果和处理建议
通过这种系统性的证据生成和整合机制,确保了根因分析结果的准确性和可靠性,为后续的问题处理提供了有力的支持。