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. 结果呈现:以标准化格式呈现分析结果和处理建议

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

相关推荐
shao9185162 小时前
Gradio全解11——Streaming:流式传输的视频应用(5)——RT-DETR:实时端到端检测模型
人工智能·nms·objects365·rt-detr·rt-detrv2·高效混合编码器·iou交并比
chanalbert2 小时前
信息检索技术综述:从传统稀疏检索到现代深度学习方法
人工智能·深度学习·全文检索
吠品2 小时前
自动化SSL证书管理:应对域名SSL证书更新焦虑
运维·自动化·ssl
vivo互联网技术2 小时前
聚焦结构化注意力,探索提升多模态大模型文档问答性能
人工智能
nmxiaocui3 小时前
Linux 用户和用户组管理
linux·运维·系统安全
慧星云3 小时前
魔多 AI 支持 Kontext 在线训练 :超高角色一致性助您创作
人工智能·云计算·aigc
小周不长肉3 小时前
AI幻觉的罪魁祸首
人工智能
小关会打代码3 小时前
计算机视觉进阶教学之图像投影(透视)变换
人工智能·计算机视觉
星期天要睡觉3 小时前
(纯新手教学)计算机视觉(opencv)实战十四——模板与多个对象匹配
人工智能·opencv·计算机视觉