STS_Root_Cause_Analysis_Error.ipynb 工作流程解析

本文档旨在帮助理解 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" # 自定义会话名称

这部分代码主要完成以下任务:

  1. 导入必要的库和模块
  2. 加载环境变量配置
  3. 设置故障分析时间范围
  4. 配置SLS参数(项目名、日志库名、区域)
  5. 从环境变量中读取认证信息

其中关键点是使用了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
sequenceDiagram participant U as 用户代码 participant S as STS客户端 participant A as 阿里云STS服务 U->>S: 调用get_sts_credentials() S->>S: 检查必要参数完整性 S->>A: 发送AssumeRole请求 A->>A: 验证主账号AK和角色ARN A->>A: 检查角色信任策略 A->>S: 返回临时凭证(含AccessKeyId, AccessKeySecret, SecurityToken) S->>U: 返回凭证对象

函数执行过程:

  1. 首先检查必要的环境变量是否都已正确配置
  2. 使用主账号的AccessKey ID和Secret创建STS客户端配置
  3. 构造AssumeRole请求,指定角色ARN、会话名称和凭证有效期(3600秒)
  4. 调用STS服务的assume_role方法获取临时凭证
  5. 处理可能的异常情况,如凭证错误、角色配置问题等
  6. 返回获取到的临时凭证对象
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
sequenceDiagram participant U as 用户代码 participant C as 函数create_sls_client_with_sts participant L as LogClient participant S as STS服务 U->>C: 调用create_sls_client_with_sts() C->>C: 调用get_sts_credentials()获取凭证 C->>S: 获取临时凭证 S-->>C: 返回凭证 C->>L: 使用临时凭证创建LogClient实例 L->>L: 初始化客户端配置 L-->>C: 返回客户端实例 C-->>U: 返回SLS客户端实例

函数执行过程:

  1. 调用get_sts_credentials()函数获取临时安全凭证
  2. 如果凭证获取失败,则直接返回None
  3. 根据区域信息构造SLS服务端点
  4. 使用获取到的临时凭证(包括AccessKey ID、AccessKey Secret和Security Token)创建LogClient实例
  5. 返回创建好的SLS客户端

2.2 前置知识

Trace

在微服务架构中,一个用户请求可能会经过多个服务的处理,每个服务可能部署在不同的服务器上。这种架构虽然提高了系统的可扩展性和灵活性,但也增加了问题排查的复杂性。当系统出现错误或性能问题时,很难快速定位问题发生在哪个服务的哪个环节。

分布式追踪技术就是为了解决这个问题而诞生的。它通过在请求流经的每个服务中添加追踪信息,形成一个完整的调用链路,帮助开发者和运维人员快速定位问题。

Trace(追踪)代表了一个完整的请求在分布式系统中的执行路径。它是一系列相关的 Span 的集合,这些 Span 共同构成了一个请求从开始到结束的完整调用链路。

核心特征

  1. Trace ID:Trace 的全局唯一标识符,用于关联同一个请求的所有 Span
  2. 起点和终点:代表了整个请求的开始和结束时间
  3. 完整调用链:包含了请求经过的所有服务和组件

Trace 示例

假设用户在电商网站上进行购物下单操作,这个操作可能涉及以下服务调用:

makefile 复制代码
用户请求: 下单操作
    ↓
前端服务 (frontend)
    ↓
结算服务 (checkout)
    ↓
货币服务 (currency)    购物车服务 (cart)    配送服务 (shipping)    支付服务 (payment)
                                                              ↓
                                                      忠诚度服务 (loyalty)

这个完整的下单操作就构成了一个 Trace,它包含了从用户发起请求到收到响应的整个过程。

在当前任务中,Trace 还包含以下字段信息等:

  • traceId:追踪的全局唯一标识符
  • serviceName:服务名称
  • spanName:跨度名称
Span

在分布式追踪中,Span 是构成 Trace 的基本单元。每个 Span 代表了请求在某个服务中的具体操作。

定义

Span 代表了分布式系统中具有开始时间和执行时长的逻辑运行单元。在当前任务中,Span 包含以下核心字段信息:

  1. spanId:Span 的唯一标识符

  2. parentSpanId:父级 Span 的 ID(用于构建调用树,表示 Span 间的父子关系)

  3. traceId:整个调用链路的唯一标识符(与 Trace 关联)

  4. serviceName:产生该 Span 的服务名称

  5. spanName:Span 的名称,描述这个 Span 做了什么(例如:"PlaceOrder"、"Charge")

  6. statusCode:状态码,用于表示 Span 的执行状态:

    • 0/1:正常状态
    • 2/3:错误/异常状态
  7. statusMessage:错误文本或异常摘要,当 statusCode 为错误状态时提供详细的错误信息

  8. 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: PlaceOrderfrontend: /cart/checkout 的子 Span
  • currency: GetCurrencycart: GetCart 等是 checkout: PlaceOrder 的子 Span

在当前的根因分析任务中,Span 扮演着关键角色:

  1. 问题定位的基本单元:每个 Span 代表一个具体的操作,通过分析这些操作的状态可以定位问题发生的具体位置。
  2. 错误识别:通过检查 Span 的状态码(statusCode),我们可以识别出哪些操作执行失败了。在当前任务中,statusCode > 1 的 Span 被认为是错误 Span。
  3. 调用链分析:通过 traceId 和 parentSpanId,我们可以构建完整的调用链路,分析错误是如何在服务间传播的。
  4. 根本原因分析:通过分析大量的错误 Span,我们可以识别出最常见的错误模式,进而找出导致问题的根本原因服务。

在当前任务中,我们通过以下步骤使用 Span 进行根因分析:

  1. 筛选错误 Span:找出指定时间范围内所有 statusCode > 1 的 Span
  2. 模式分析:分析这些错误 Span 的特征,找出最常见的错误模式
  3. 根因识别:通过比较不同服务的错误频率,识别出最可能的根本原因服务

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。

  1. 构建查询条件

    ini 复制代码
    all_spans_query = "statusCode>1"

    构造查询语句,筛选出所有状态码大于1(表示错误)的 Span。

  2. 执行日志查询

    ini 复制代码
    request = 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。

  3. 按 Trace 分组

    ini 复制代码
    trace_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 进行分析。

  4. 逐个处理 Trace

    ini 复制代码
    all_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。

  5. 返回结果: 返回所有 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 出错。

方法实现流程

  1. 建立映射关系

    • 建立 Span 状态映射(span_status)
    • 建立父子 Span 关系映射(parent_spans 和 child_spans)
  2. 筛选错误 Span: 找出所有状态码大于1的 Span。

  3. 识别根因 Span: 对每个错误 Span,检查其所有子 Span 是否都为正常状态,如果是,则该 Span 为根因 Span。

  4. 返回结果: 返回该 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客户端,任务终止。")

主要执行流程包括以下步骤:

  1. 调用 create_sls_client_with_sts()函数创建SLS客户端实例
  2. 检查客户端是否创建成功
  3. 如果成功,实例化FindRootCauseSpans类,传入客户端和相关配置参数
  4. 调用 find_root_cause_spans()方法执行查找操作
  5. 获取并展示错误span ID列表供后续分析使用
  6. 处理可能发生的异常情况
sequenceDiagram participant U as 用户代码 participant F as FindRootCauseSpans participant L as LogClient participant S as SLS服务 U->>F: 实例化FindRootCauseSpans类 U->>F: 调用find_root_cause_spans()方法 F->>F: 构造SPL查询语句(statusCode>1) F->>L: 发送查询请求 L->>S: 向SLS服务发起查询 S->>S: 在指定时间范围内搜索日志 S->>S: 筛选statusCode>1的日志记录 S-->>L: 返回查询结果 L-->>F: 返回日志数据 F->>F: 解析日志数据提取spanId F->>F: 去重处理,得到根因spanId列表 F-->>U: 返回根因spanId列表

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 代码执行流程

  1. 检查根因 Span 列表

    • 首先检查 root_cause_span_ids 是否存在且非空
    • 如果列表为空,输出错误信息并设置 SPAN_CONDITIONS 为空字符串
  2. 构建查询条件

    • 使用列表推导式和 join() 方法将所有 Span ID 转换为查询条件
    • 格式为:spanId='id1' or spanId='id2' or spanId='id3' ...
    • 这种格式可以直接用于 SPL 查询语句中
  3. 输出和保存

    • 打印生成的查询条件(如果过长则截取前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 查询条件将在后续的分析阶段中发挥关键作用:

  1. 错误特征分析:用于筛选出根因 Span 进行模式匹配分析
  2. 差异模式分析:作为异常组的标识条件
  3. 日志模式分析:用于精确匹配根因 Span 的日志内容

通过这种方式,我们可以确保后续的所有分析都基于之前识别出的根因 Span,从而提高分析的准确性和针对性。

4. 寻找报错特征

在识别出根因 Span 后,下一步是分析这些 Span 的特征,找出导致错误的共同模式。这一部分使用了阿里云 SLS 提供的智能分析函数,包括 get_patternsdiff_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 语句详细解析
  1. 启用远程函数和 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() 构造器的支持,用于后续构建结构化数据行
  1. 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=23 代表错误)
    • error_count: 错误计数器,用于后续聚合
  1. 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() 函数处理
  1. CTE t2: 构造结构化行数据
vbnet 复制代码
t2 as (
    select row(spanName, serviceName) as table_row 
    from t1
)

说明:

  • 使用 row() 构造一个包含多个字段的结构体(类似结构体或元组),这里只包含了 spanNameserviceName
  • 之所以这么做是为了适配 get_patterns() 函数所需的输入格式:它期望接收一个结构化数据行(ROW)以及对应的列名数组
  1. CTE t3: 应用 get_patterns() 函数进行模式分析
csharp 复制代码
t3 as (
    select get_patterns(table_row, ARRAY['spanName', 'serviceName']) as ret 
    from t2
)

说明:

  • 调用阿里云 SLS 提供的智能分析函数 get_patterns()
  • 第一个参数:结构化行数据 table_row
  • 第二个参数:列名数组,告诉函数哪些字段参与模式识别
  • 返回值是一个复杂的嵌套数组结构,表示发现的模式及其频次
  1. 最终输出结果
csharp 复制代码
select * from t3

说明:

  • 输出最终的模式识别结果
代码执行流程
  1. 检查查询条件

    • 首先检查 SPAN_CONDITIONS 是否存在且非空
  2. 构建分析查询

    • 使用之前创建的 SLS 客户端

    • 构建 SPL 查询语句,使用 get_patterns 函数分析数据模式

    • 查询中提取了多个维度的信息:

      • spanName:Span 名称
      • serviceName:服务名称
      • pod_ip:Kubernetes Pod 的 IP 地址
      • node_name:Kubernetes 节点名称
      • service_version:服务版本
      • anomaly_label:异常标签('true' 表示错误,'false' 表示正常)
      • error_count:错误计数
  3. 执行查询

    • 使用 GetLogsRequest 构造查询请求
    • 调用客户端的 get_logs 方法执行查询
输入与输出

输入

  • SPAN_CONDITIONS:之前生成的根因 Span 查询条件

  • 示例:

    ini 复制代码
    spanId='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 次错误,是主要的错误来源。

在根因分析中的作用

  1. 识别主要错误服务:通过分析不同服务的错误频率,找出最可能的根因服务
  2. 模式发现:自动发现错误 Span 中的共同模式,减少人工分析工作量
  3. 数据聚合:将分散的 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"]

这些模式告诉我们:

  • 哪些服务最容易出错
  • 哪些接口调用最容易失败
  • 错误发生的特定上下文环境

模式的价值

  1. 快速定位问题热点

    makefile 复制代码
    模式: ["serviceName=payment"]
    频次: 152次
    含义: payment服务是主要的错误来源
  2. 发现隐藏的关联关系

    less 复制代码
    模式: ["serviceName=payment", "pod_ip=10.0.1.23"]
    频次: 89次
    含义: 特定Pod上的payment服务问题严重
  3. 减少人工分析工作量

    • 自动识别出人眼难以察觉的复杂组合
    • 量化每个模式的重要性

假设有以下错误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

通过模式分析,可能会发现:

  1. 单一模式

    css 复制代码
    ["serviceName=payment"] → 出现120次
  2. 组合模式

    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'])

这个函数会:

  1. 枚举所有可能的属性组合
  2. 统计每种组合的出现频次
  3. 按照频次降序排列
  4. 返回最显著的模式

输出结果解读

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 语句详细解析
  1. 查询条件和会话设置
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函数调用做准备
  1. 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)
  • 这样就创建了两个对比组:异常组和对照组

  1. 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
)

说明:

  • 将所有字段聚合成数组,为构造结构化行数据做准备
  1. CTE t2: 构造结构化行数据
vbnet 复制代码
t2 as (
    select row(serviceName, anomaly_label) as table_row 
    from t1
)

说明:

  • 构造包含serviceNameanomaly_label的结构化行数据
  • anomaly_label作为分组标识字段,用于后续的差异分析
  1. 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':对照组标识
  1. 最终输出
csharp 复制代码
select * from t3
代码执行流程
  1. 检查查询条件

    • 首先检查 SPAN_CONDITIONS 是否存在且非空
  2. 构建差异分析查询

    • 查询条件为 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 函数比较异常组和对照组的差异

  3. 执行查询

    • 使用 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:负数组(对照组)的标识值

工作原理:

  1. 根据group_field将数据分为两组
  2. 分别统计每组中各字段组合的出现频次
  3. 计算两组之间的差异度量
  4. 识别在正数组中显著高于负数组的模式
输入与输出

输入

  • 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'"]:识别出的模式

    1. [152,2]:异常组中各模式的出现次数
    2. [0,304]:对照组中各模式的出现次数
    3. [0.987012987012987,0.012987012987012988]:异常组中各模式的占比
    4. [0.0,0.019740259740259743]:对照组中各模式的占比
    5. [0.987012987012987,-0.006753246753246754]:各模式在异常组和对照组中的差异值
    6. [1.0,1.0]:各模式的显著性
    7. [0.0,0.0]:各模式的效应大小
    8. null:其他信息

    结果解读:

    • payment服务在异常组中出现152次,占比98.7%
    • payment服务在对照组中出现0次,占比0%
    • 这表明payment服务是根因Span的显著特征,几乎只在根因Span中出现

在根因分析中的作用

  1. 差异识别:通过比较根因 Span 和非根因错误 Span,识别出根因 Span 的独特特征
  2. 验证准确性 :帮助验证之前通过 get_patterns 识别出的根因服务的准确性
  3. 增强置信度:通过对比分析增强对根因识别结果的置信度
  4. 量化分析:提供量化的差异度量,而非简单的频次统计
对比
特性 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}")
  1. 会话设置和查询条件
ini 复制代码
statusCode > 0 | set session enable_remote_functions=true; 
set session velox_support_row_constructor_enabled=true;

说明:

  • statusCode > 0:筛选所有错误状态的日志记录
  • 启用远程函数和Velox支持,为get_log_patterns函数调用做准备
  1. CTE t0: 数据提取和消息组合
csharp 复制代码
with t0 as (
  select statusCode, statusMessage, serviceName,
         CONCAT('[', serviceName, '] ', statusMessage) as combined_message
  from log 
)

说明:

  • 提取关键字段:statusCodestatusMessageserviceName
  • 使用CONCAT函数将服务名和服务消息组合成统一格式:[serviceName] statusMessage
  • 这种格式有助于模式识别函数更好地理解日志结构
  1. CTE t1: 数据聚合
vbnet 复制代码
t1 as (
  select array_agg(combined_message) as contents
  from t0
)

说明:

  • 使用array_agg将所有组合后的消息聚合成一个数组
  • 为后续的模式学习函数提供输入数据格式
  1. 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: 最大数字比例(控制数字变量的提取)
  1. 最终输出
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)
  1. 会话设置
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:关闭限制,允许处理大量数据
  1. CTE t0: 数据准备
csharp 复制代码
with t0 as (
    select CONCAT('[', serviceName, '] ', statusMessage) as combined_message
    from log
)

说明:

  • 与第一阶段相同,准备统一格式的日志消息
  1. 模式匹配和筛选
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 代码执行流程

  1. 第一阶段 - 模型训练

    • 查询所有 statusCode > 0 的日志记录
    • 将日志内容组合成特定格式:[serviceName] statusMessage
    • 调用 get_log_patterns 函数进行模式学习,生成日志模式模型
    • 获取模型 ID(model_id)用于后续匹配
  2. 第二阶段 - 模式匹配

    • 使用第一阶段生成的 model_id
    • 调用 match_log_patterns 函数对日志进行模式匹配
    • 筛选出成功匹配的日志记录
    • 对匹配结果进行统计分析
  3. 结果统计和分析

    • 统计每个模式的出现次数
    • 提取服务名进行错误次数统计
    • 按出现频率排序展示结果
get_log_patterns 函数详解

get_log_patterns(contents, delimiters, reserved, reserved, config)

参数说明:

  • contents: 日志内容数组

  • delimiters: 分隔符数组

  • config: 配置参数JSON字符串

    • threshold: 最小出现次数阈值
    • tolerance: 模式容忍度
    • maxDigitRatio: 最大数字比例

工作原理:

  1. 使用分隔符切分日志内容
  2. 识别重复出现的文本模式
  3. 提取变量部分(如数字、变化的文本)
  4. 生成模式模板和正则表达式
  5. 返回模型ID用于后续匹配
match_log_patterns 函数详解

match_log_patterns(model_id, log_content)

参数说明:

  • model_id: 模式模型ID
  • log_content: 待匹配的日志内容

返回值:

  • is_matched: 是否匹配成功
  • pattern_id: 匹配的模式ID
  • pattern: 模式模板
  • regexp: 对应的正则表达式
  • variables: 提取的变量值

5.3 输入与输出

输入
  • 时间范围:start_timestampend_timestamp
  • 查询条件:所有 statusCode > 0 的日志记录
  • 日志内容:serviceNamestatusMessage 字段
输出
  • 控制台输出示例:

    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 次错误
结果解读
  1. 高频模式分析

    • [*]:出现15250次,通常是statusMessage为空的日志
    • [checkout] Payment request failed...:出现152次,包含具体错误信息
    • [checkout] rpc error...:出现152次,详细的RPC错误堆栈
  2. 服务错误频率

    • currency: 13418次错误(最高)
    • checkout: 918次错误
    • payment: 152次错误

5.4 在根因分析中的作用

  1. 内容验证:通过对日志内容的模式分析,验证之前通过 Span 分析得出的根因结论
  2. 细节补充:提供具体的错误信息,帮助更深入地理解问题原因
  3. 多维度确认:从日志内容角度提供额外证据,增强根因分析的可靠性
  4. 异常检测:识别异常的日志模式,发现潜在的系统问题
交叉验证的价值

通过将Span分析结果与日志模式分析结果进行对比:

分析维度 Span分析结果 日志分析结果 交叉验证结论
主要问题服务 payment (152次) checkout (918次) 需要进一步分析
错误模式 服务调用失败 具体错误信息 互为补充
置信度 中等 增强整体置信度

6. 结果总结

在完成所有分析步骤后,最后一步是汇总分析结果并给出最终的诊断结论。这部分代码综合了前面所有分析步骤的结果,通过解析 get_patternsdiff_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

证据生成过程

  1. 执行异常检测算法,识别出异常的Span
  2. 统计异常Span数量:len(root_cause_span_ids)
  3. 设置证据标志:error_span_evidence = True

证据示例

css 复制代码
发现 154 个错误span
第二步:get_patterns分析生成高频模式证据

证据来源 :第4.1章get_patterns分析结果 证据内容

  • get_patterns_result:高频模式分析结果

证据生成过程

  1. 对根因Span进行模式分析
  2. 识别出高频出现的服务名组合
  3. 统计各模式的出现频次

证据示例

csharp 复制代码
[["serviceName=payment","serviceName=frontend-proxy"],[152,2],null,null]

解析结果

makefile 复制代码
get_patterns发现主要错误服务: payment (错误数: 152)
第三步:diff_patterns分析生成差异特征证据

证据来源 :第4.2章diff_patterns分析结果 证据内容

  • diff_patterns_result:差异模式分析结果

证据生成过程

  1. 对比根因Span和非根因错误Span
  2. 识别在根因组中显著高于对照组的模式
  3. 计算差异度量指标

证据示例

css 复制代码
[[""serviceName"='payment'"],[152],[0],[0.987...],[0.0],[0.987...],[1.0],[0.0],null]

解析结果

makefile 复制代码
diff_patterns确认异常服务: payment
✅ 多重证据确认: payment 是主要根因服务
第四步:日志模式分析生成内容验证证据

证据来源 :第5章日志模式分析结果 证据内容

  • 具体的错误日志模式和频率统计

证据生成过程

  1. 对所有错误日志进行模式学习
  2. 识别重复出现的日志模式
  3. 统计各服务的错误频率

证据示例

ini 复制代码
出现次数: 152
模式: [checkout] Payment request failed. Invalid token. app.loyalty.level=gold
...
📊 涉及的服务排序 (按错误频率):
payment: 152 次错误

6.3 证据整合与评估流程

证据整合顺序
flowchart TD A[根因Span证据] --> B{证据存在?} B -->|是| C[get_patterns证据] B -->|否| D[低置信度] C --> E{找到高频服务?} E -->|是| F[diff_patterns证据] E -->|否| G[中置信度] F --> H{验证通过?} H -->|是| I[高置信度] H -->|否| J[中置信度]
证据评估标准
证据类型 评估标准 权重
根因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 最终诊断生成

诊断结论生成流程
  1. 证据收集

    • 收集所有分析阶段的输出结果
  2. 证据解析

    • 解析各阶段的复杂数据结构
    • 提取关键信息(主要是服务名)
  3. 证据验证

    • 检查证据的完整性和一致性
    • 进行多重交叉验证
  4. 置信度评估

    • 根据证据完整性评估置信度等级
  5. 结论输出

    • 生成最终的根因诊断报告
输出格式标准化
python 复制代码
{
    "root_cause": "payment",
    "confidence": "高",
    "evidence": True,
    "supporting_evidence": [
        "发现 154 个错误span",
        "diff_patterns分析表明 payment 服务异常",
        "日志模式分析验证了服务问题",
        "错误特征分析确认了根因位置"
    ],
    "recommendations": [
        "检查 payment 服务的健康状态",
        "查看 payment 的错误日志和异常",
        "验证 payment 的部署和配置",
        "考虑重启或修复 payment 服务"
    ]
}

6.6 代码执行流程

  1. 证据解析

    • 解析 get_patterns_result,提取出现频率最高的服务
    • 解析 diff_patterns_result,验证根因服务的一致性
    • 通过多重证据确认提高置信度
  2. 置信度评估

    • 根据是否有足够的证据(错误 Span 和模式分析结果)评估置信度
    • 分为高、中、低三个等级
  3. 诊断结论生成

    • 输出根因候选服务
    • 给出置信度评估
    • 提供处理建议

6.7 输入与输出

输入
  • get_patterns_resultget_patterns 函数的分析结果
  • diff_patterns_resultdiff_patterns 函数的分析结果
  • root_cause_span_ids:识别出的根因 Span ID 列表
输出
  • 控制台输出示例:

    diff 复制代码
    🏆 根因候选:
       🎯 payment
       📈 置信度:高
       ✅ 证据:TRUE(已检测到异常)
       📝 支持证据:
          - 发现 154 个错误span
          - diff_patterns分析表明 payment 服务异常
          - 日志模式分析验证了服务问题
          - 错误特征分析确认了根因位置
    ​
    💡 建议:
       - 检查 payment 服务的健康状态
       - 查看 payment 的错误日志和异常
       - 验证 payment 的部署和配置
       - 考虑重启或修复 payment 服务
    ​
    ============================================================
    🎯 最终答复:payment
    📈 置信度:高
    🔍 证据:TRUE
    ============================================================

6.8 在根因分析中的作用

  1. 证据整合:将分散在各分析阶段的证据进行系统性整合
  2. 置信度评估:通过多维度证据评估根因识别的可信度
  3. 结论验证:通过交叉验证确保诊断结论的准确性
  4. 结果呈现:以标准化格式呈现分析结果和处理建议

通过这种系统性的证据生成和整合机制,确保了根因分析结果的准确性和可靠性,为后续的问题处理提供了有力的支持。

相关推荐
dishugj2 分钟前
【linux】Redhat 6.3系统安装zabbix-agent软件包,无法使用YUM源问题
linux·运维·zabbix
清月电子4 分钟前
杰理AC109N系列AC1082 AC1074 AC1090 芯片停产替代及资料说明
人工智能·单片机·嵌入式硬件·物联网
Dev7z5 分钟前
非线性MPC在自动驾驶路径跟踪与避障控制中的应用及Matlab实现
人工智能·matlab·自动驾驶
七月shi人15 分钟前
AI浪潮下,前端路在何方
前端·人工智能·ai编程
无奈笑天下22 分钟前
【麒麟镜像vmtools异常排查指导书】
linux·运维·经验分享·云计算·kylin
dajun18112345624 分钟前
PC端中文免费在线跨职能泳道图制作工具
运维·架构·流程图·敏捷流程·交通物流
橙汁味的风34 分钟前
1隐马尔科夫模型HMM与条件随机场CRF
人工智能·深度学习·机器学习
itwangyang5201 小时前
AIDD-人工智能药物设计-AI 制药编码之战:预测癌症反应,选对方法是关键
人工智能
FlourishingMind1 小时前
蓝牙授时CTS (Current Time Service)、PTP、NTP
运维·服务器·网络
蓝桉~MLGT1 小时前
Ai-Agent学习历程—— 阶段1——环境的选择、Pydantic基座、Jupyter Notebook的使用
人工智能·学习·jupyter