普通视图

发现新文章,点击刷新页面。
昨天以前首页

以Vultr供应商的VPS为例、十分钟自建一个自己的VPN(图文并茂)

作者 水冗水孚
2026年4月21日 22:49

本文图文并茂记录购买Vultr供应商的VPS,通过shell脚本的方式,快速部署一个属于自己的VPN,从而实现逛GitHub自由...

1. 什么是VPS?和现在的云服务器的区别

VPS就是Virtual Private Server,虚拟专用服务器

  • 对比于现在的火山引擎、腾讯云、阿里云等云服务器而言,VPS可以理解为迷你版的云服务器

  • 依托于服务器虚拟化技术,可以把一个配置高的服务器,虚拟切割成好几台配置低的服务器 比如16核32G的可以切成两台8核16G,这样就可以卖给两个用户,减少资源闲置多挣米

  • 但是对比云服务器和VPS,前者可以智能灵活调度(成百上千台服务器组成的服务器资源池子) 而VPS就是一台物理服务器的切割,相当于我们租赁了一个小单间

  • 所以,云服务器挂了,智能调度会立刻新启用一台虚拟服务器,备份并重新启动相关服务 但是,VPS要是挂了,因为是小单间模式嘛,高可用是无法做到位的

  • 所以,学习Linux、搭个小网站、个人VPN购买便宜的VPS就够用了

  • 不过公司业务、需要高可用、随时要扩容,就得选真正的云服务器了

无论VPS还是云服务器,都是物理意义上的服务器上的一部分,不存在两个物理服务器各出一半

2. 买VPS服务器做相应配置

VPS服务器供应商不少,不赘述,笔者买的是Vultr,还不错

官网:www.vultr.com/zh/

至于购买步骤流程,可以参考这个文章:zhuanlan.zhihu.com/p/701057606

建议,提前准备好一个VISA信用卡哦

服务器配置如下图参考

第一步——选配置

111.png

第二步——选择操作系统(防火墙规则【相当于云服务器的安全组概念】)

222.png

第三步——创建实例,等待一会

333.png

第四步——有了自己的公网ip了,可以ssh链接了

444.png

第五步——防火墙组设置

555.png

使用udp搭配443端口(要放开哦,要不然无法连接VPN服务)

666.png

3. 执行一键部署VPN脚本

部署脚本是setup-hysteria.sh这个文件,名字无所谓,主要是内容如下:

PASSWORD="password123" 是示例,实际上可以设置复杂一些,在搭配fail2ban这样可以保证服务器安全不被爆破

#!/bin/bash
set -e

# 注意,要赋予此脚本执行权限:chmod +x setup-hysteria.sh
# 然后在执行:./setup-hysteria.sh

# ==================== 配置变量(按需修改) ====================
PASSWORD="password123"
LISTEN_PORT="443"
MASQUERADE_URL="https://www.bing.com"
CERT_DAYS="365"
HY_VERSION="v2.8.1"
# ============================================================

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN}   Hysteria ${HY_VERSION} VPN 服务器安装   ${NC}"
echo -e "${GREEN}          Ubuntu 22.04 专用           ${NC}"
echo -e "${GREEN}========================================${NC}"

if [[ $EUID -ne 0 ]]; then
    echo -e "${RED}错误:请使用 root 用户执行此脚本 (sudo ./script.sh)${NC}"
    exit 1
fi

echo -e "${YELLOW}[1/7] 更新系统并安装依赖...${NC}"
apt update -qq
apt install -y -qq wget curl openssl ufw

echo -e "${YELLOW}[2/7] 创建目录结构...${NC}"
mkdir -p /etc/hysteria /etc/ssl/hysteria

echo -e "${YELLOW}[3/7] 生成 SSL 证书(有效期${CERT_DAYS}天)...${NC}"
openssl req -x509 -newkey rsa:4096 -nodes \
    -keyout /etc/ssl/hysteria/key.pem \
    -out /etc/ssl/hysteria/cert.pem \
    -days ${CERT_DAYS} \
    -subj "/CN=www.bing.com"

chmod 644 /etc/ssl/hysteria/key.pem
chmod 644 /etc/ssl/hysteria/cert.pem
echo -e "${GREEN}✓ 证书权限已设置为 644${NC}"

echo -e "${YELLOW}[4/7] 创建配置文件 /etc/hysteria/config.yaml ...${NC}"
cat > /etc/hysteria/config.yaml << YAML
listen: :${LISTEN_PORT}

tls:
  cert: /etc/ssl/hysteria/cert.pem
  key: /etc/ssl/hysteria/key.pem

auth:
  type: password
  password: ${PASSWORD}

masquerade:
  type: proxy
  proxy:
    url: ${MASQUERADE_URL}
    rewriteHost: true

quic:
  initStreamReceiveWindow: 8388608
  maxStreamReceiveWindow: 8388608
  initConnReceiveWindow: 20971520
  maxConnReceiveWindow: 20971520
YAML

echo -e "${YELLOW}[5/7] 使用官方脚本安装 Hysteria ${HY_VERSION} ...${NC}"
bash <(curl -fsSL https://get.hy2.sh/) --version ${HY_VERSION}

echo -e "${YELLOW}[6/7] 配置防火墙 (ufw)...${NC}"
ufw allow ${LISTEN_PORT}/udp
echo -e "${GREEN}已允许 UDP ${LISTEN_PORT} 端口${NC}"

echo -e "${YELLOW}[7/7] 重启 Hysteria 服务并应用配置...${NC}"
systemctl stop hysteria-server || true
systemctl start hysteria-server
systemctl enable hysteria-server
sleep 3

if systemctl is-active --quiet hysteria-server; then
    SERVER_IP=$(curl -s ifconfig.me)
    echo -e "\n${GREEN}========================================${NC}"
    echo -e "${GREEN}✓ Hysteria 部署成功!${NC}"
    echo -e "${GREEN}========================================${NC}"
    echo -e "${YELLOW}服务状态:${NC}$(systemctl status hysteria-server --no-pager | grep "Active:")"
    echo -e "${YELLOW}端口监听:${NC}"
    ss -tulnp | grep ":${LISTEN_PORT}" | grep -v grep || echo "  等待端口监听..."
    echo ""
    echo -e "${GREEN}客户端连接信息:${NC}"
    echo -e "  服务器地址:${SERVER_IP}:${LISTEN_PORT}"
    echo -e "  密码:${PASSWORD}"
    echo -e "  协议:Hysteria ${HY_VERSION}"
    echo ""
    echo -e "${YELLOW}常用管理命令:${NC}"
    echo -e "  查看状态: systemctl status hysteria-server"
    echo -e "  查看日志: journalctl -u hysteria-server -f"
    echo -e "  重启服务: systemctl restart hysteria-server"
    echo -e "  停止服务: systemctl stop hysteria-server"
else
    echo -e "${RED}服务启动失败!查看错误日志:${NC}"
    journalctl -u hysteria-server -n 20 --no-pager
    exit 1
fi

然后,把这个·setup-hysteria.sh·脚本丢到服务器上(ssh链接)比如笔者是放在var目录下的

root@vultr:/var# ls
backups  crash  local  log   opt  setup-hysteria.sh  spool
cache    lib    lock   mail  run  snap               tmp

然后 chmod +x setup-hysteria.sh 给权限,再 ./setup-hysteria.sh 就可以一键部署好vpn服务了

如下日志图:

777.png

查看服务状态也是在运行的

root@vultr:/var# systemctl status hysteria-server
● hysteria-server.service - Hysteria Server Service (config.yaml)
     Loaded: loaded (/etc/systemd/system/hysteria-server.service; enabled; vendor preset: enabled)
     Active: active (running) since Tue 2026-04-21 14:17:33 UTC; 2min 34s ago
   Main PID: 8156 (hysteria)
      Tasks: 7 (limit: 1001)
     Memory: 5.9M
        CPU: 57ms
     CGroup: /system.slice/hysteria-server.service
             └─8156 /usr/local/bin/hysteria server --config /etc/hysteria/config.yaml

Apr 21 14:17:33 vultr systemd[1]: Started Hysteria Server Service (config.yaml).
Apr 21 14:17:33 vultr hysteria[8156]: 2026-04-21T14:17:33Z        INFO        server mode
Apr 21 14:17:33 vultr hysteria[8156]: 2026-04-21T14:17:33Z        INFO        server up and running        {"listen": ":443"}
root@vultr:/var#

至此,我们的VPS服务器上的VPN服务就部署好了,接下来,我们在自己的本机电脑上,使用一些客户端工具,就可以使用VPN服务了

3. 使用clash-verge-rev进行订阅VPN服务(通过配置文件的方式)

首先安装clash-verge-rev,这个软件客户端:github.com/clash-verge…

如下图:

888.png

然后准备一个conf.yaml文件,内容如下:

  • 注意:server: 64.176.80.218 就是 VPS服务器的ip
  • password: "password123" 也就是服务器的VPN的密码
  • 等,不赘述
  • 和 !!!setup-hysteria.sh 这个文件里面配置信息要对上!!!
  • rule-providers也可以根据个人情况,适当修改
# ========== 代理节点配置 ==========
proxies:
  - name: "VPS-Hysteria2"
    type: hysteria2
    server: 64.176.80.218
    port: 443
    password: "password123"
    sni: www.bing.com
    skip-cert-verify: true
    # 以下为可选优化参数
    up: "100 Mbps"
    down: "500 Mbps"

# ========== 规则集配置(可选,用于增强分流)==========
rule-providers:
  reject:
    type: http
    behavior: domain
    url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/reject.txt"
    path: ./ruleset/reject.yaml
    interval: 86400

  icloud:
    type: http
    behavior: domain
    url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/icloud.txt"
    path: ./ruleset/icloud.yaml
    interval: 86400

  apple:
    type: http
    behavior: domain
    url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/apple.txt"
    path: ./ruleset/apple.yaml
    interval: 86400

  google:
    type: http
    behavior: domain
    url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/google.txt"
    path: ./ruleset/google.yaml
    interval: 86400

  proxy:
    type: http
    behavior: domain
    url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/proxy.txt"
    path: ./ruleset/proxy.yaml
    interval: 86400

  direct:
    type: http
    behavior: domain
    url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/direct.txt"
    path: ./ruleset/direct.yaml
    interval: 86400

  gfw:
    type: http
    behavior: domain
    url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/gfw.txt"
    path: ./ruleset/gfw.yaml
    interval: 86400

  tld-not-cn:
    type: http
    behavior: domain
    url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/tld-not-cn.txt"
    path: ./ruleset/tld-not-cn.yaml
    interval: 86400

  telegramcidr:
    type: http
    behavior: ipcidr
    url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/telegramcidr.txt"
    path: ./ruleset/telegramcidr.yaml
    interval: 86400

  cncidr:
    type: http
    behavior: ipcidr
    url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/cncidr.txt"
    path: ./ruleset/cncidr.yaml
    interval: 86400

  lancidr:
    type: http
    behavior: ipcidr
    url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/lancidr.txt"
    path: ./ruleset/lancidr.yaml
    interval: 86400

  applications:
    type: http
    behavior: classical
    url: "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/applications.txt"
    path: ./ruleset/applications.yaml
    interval: 86400

# ========== 代理组配置 ==========
proxy-groups:
  - name: "🚀 节点选择"
    type: select
    proxies:
      - "VPS-Hysteria2"
      - "DIRECT"

  - name: "🎬 流媒体"
    type: select
    proxies:
      - "VPS-Hysteria2"
      - "DIRECT"

  - name: "🤖 AI服务"
    type: select
    proxies:
      - "VPS-Hysteria2"
      - "DIRECT"

# ========== 规则配置 ==========
rules:
  # ===== 1. 规则集分流(如果不想用可以删除本块)=====
  - RULE-SET,applications,DIRECT
  - RULE-SET,icloud,DIRECT
  - RULE-SET,apple,DIRECT
  - RULE-SET,google,🚀 节点选择
  - RULE-SET,proxy,🚀 节点选择
  - RULE-SET,direct,DIRECT
  - RULE-SET,lancidr,DIRECT
  - RULE-SET,cncidr,DIRECT
  - RULE-SET,telegramcidr,🚀 节点选择

  # ===== 2. 国内网站强制直连 =====
  # 通用规则
  - DOMAIN-SUFFIX,cn,DIRECT
  - GEOIP,CN,DIRECT,no-resolve
  - GEOSITE,CN,DIRECT

  # 常见国内网站关键词
  - DOMAIN-KEYWORD,baidu,DIRECT
  - DOMAIN-KEYWORD,taobao,DIRECT
  - DOMAIN-KEYWORD,alipay,DIRECT
  - DOMAIN-KEYWORD,qq,DIRECT
  - DOMAIN-KEYWORD,weixin,DIRECT
  - DOMAIN-KEYWORD,bilibili,DIRECT
  - DOMAIN-KEYWORD,bytedance,DIRECT
  - DOMAIN-KEYWORD,zhihu,DIRECT
  - DOMAIN-KEYWORD,jd,DIRECT
  - DOMAIN-KEYWORD,meituan,DIRECT
  - DOMAIN-KEYWORD,douyin,DIRECT
  - DOMAIN-KEYWORD,pinduoduo,DIRECT

  # 局域网与保留地址
  #- IP-CIDR,192.168.0.0/16,DIRECT,no-resolve
  #- IP-CIDR,10.0.0.0/8,DIRECT,no-resolve
  #- IP-CIDR,172.16.0.0/12,DIRECT,no-resolve
  #- IP-CIDR,127.0.0.0/8,DIRECT,no-resolve
  #- IP-CIDR,100.64.0.0/10,DIRECT,no-resolve
  #- IP-CIDR,17.0.0.0/8,DIRECT,no-resolve

  # ===== 3. AI 服务走代理 =====
  # OpenAI
  - DOMAIN-KEYWORD,openai,🤖 AI服务
  - DOMAIN-SUFFIX,openai.com,🤖 AI服务
  - DOMAIN-SUFFIX,chatgpt.com,🤖 AI服务
  - DOMAIN-SUFFIX,ai.com,🤖 AI服务
  - DOMAIN-SUFFIX,oaistatic.com,🤖 AI服务
  - DOMAIN-SUFFIX,oaiusercontent.com,🤖 AI服务
  - DOMAIN-KEYWORD,chatgpt,🤖 AI服务

  # Anthropic (Claude)
  - DOMAIN-SUFFIX,anthropic.com,🤖 AI服务
  - DOMAIN-SUFFIX,claude.ai,🤖 AI服务

  # Google (Gemini/Bard/DeepMind)
  - DOMAIN-SUFFIX,gemini.google.com,🤖 AI服务
  - DOMAIN-SUFFIX,bard.google.com,🤖 AI服务
  - DOMAIN-SUFFIX,deepmind.google,🤖 AI服务
  - DOMAIN-SUFFIX,deepmind.com,🤖 AI服务
  - DOMAIN-SUFFIX,ai.google.dev,🤖 AI服务
  - DOMAIN-SUFFIX,generativeai.google,🤖 AI服务
  - DOMAIN-SUFFIX,proactivebackend-pa.googleapis.com,🤖 AI服务
  - DOMAIN-KEYWORD,generativelanguage,🤖 AI服务

  # Meta (Llama)
  - DOMAIN-SUFFIX,meta.ai,🤖 AI服务
  - DOMAIN-SUFFIX,llama.com,🤖 AI服务
  - DOMAIN-SUFFIX,llama.meta.com,🤖 AI服务

  # 其他海外AI服务
  - DOMAIN-SUFFIX,perplexity.ai,🤖 AI服务
  - DOMAIN-SUFFIX,pplx.ai,🤖 AI服务
  - DOMAIN-KEYWORD,perplexity,🤖 AI服务
  - DOMAIN-SUFFIX,x.ai,🤖 AI服务
  - DOMAIN-KEYWORD,grok,🤖 AI服务
  - DOMAIN-SUFFIX,poe.com,🤖 AI服务
  - DOMAIN-SUFFIX,you.com,🤖 AI服务

  # Hugging Face (AI模型社区)
  - DOMAIN-SUFFIX,huggingface.co,🤖 AI服务
  - DOMAIN-SUFFIX,hf.co,🤖 AI服务

  # 平台/聚合类AI服务
  - DOMAIN-SUFFIX,openrouter.ai,🤖 AI服务
  - DOMAIN-SUFFIX,together.ai,🤖 AI服务

  # Cursor AI 编辑器
  - DOMAIN-SUFFIX,cursor.com,🤖 AI服务
  - DOMAIN-SUFFIX,cursor.sh,🤖 AI服务
  - DOMAIN-SUFFIX,cursor-cdn.com,🤖 AI服务
  - DOMAIN-SUFFIX,workos.com,🤖 AI服务
  - DOMAIN-SUFFIX,challenges.cloudflare.com,🤖 AI服务

  # Amazon Kiro / Amazon AI 服务
  - DOMAIN-SUFFIX,kiro.dev,🤖 AI服务
  - DOMAIN-SUFFIX,amazonkiro.com,🤖 AI服务
  - DOMAIN-KEYWORD,kiro,🤖 AI服务
  - DOMAIN-SUFFIX,aws.amazon.com,🤖 AI服务
  - DOMAIN-SUFFIX,amazonaws.com,🤖 AI服务
  - DOMAIN-SUFFIX,bedrock.aws,🤖 AI服务
  - DOMAIN-KEYWORD,amazonbedrock,🤖 AI服务
  - DOMAIN-SUFFIX,q.aws.amazon.com,🤖 AI服务
  - DOMAIN-SUFFIX,codecatalyst.aws,🤖 AI服务
  - DOMAIN-SUFFIX,sagemaker.aws,🤖 AI服务

  # 国内AI服务 (默认直连,如需走代理请取消注释并修改策略)
  # - DOMAIN-SUFFIX,deepseek.com,DIRECT
  # - DOMAIN-SUFFIX,yiyan.baidu.com,DIRECT
  # - DOMAIN-SUFFIX,tongyi.aliyun.com,DIRECT
  # - DOMAIN-SUFFIX,doubao.com,DIRECT
  # - DOMAIN-SUFFIX,chatglm.cn,DIRECT
  # - DOMAIN-SUFFIX,xinghuo.xfyun.cn,DIRECT
  # - DOMAIN-SUFFIX,kimi.moonshot.cn,DIRECT
  # - DOMAIN-SUFFIX,yuanbao.tencent.com,DIRECT

  # ===== 4. 流媒体走代理 =====
  - DOMAIN-KEYWORD,youtube,🎬 流媒体
  - DOMAIN-KEYWORD,netflix,🎬 流媒体
  - DOMAIN-KEYWORD,disney,🎬 流媒体
  - DOMAIN-KEYWORD,hbo,🎬 流媒体
  - DOMAIN-KEYWORD,hulu,🎬 流媒体
  - DOMAIN-KEYWORD,spotify,🎬 流媒体
  - DOMAIN-KEYWORD,twitch,🎬 流媒体
  - DOMAIN-SUFFIX,googlevideo.com,🎬 流媒体
  - DOMAIN-SUFFIX,ytimg.com,🎬 流媒体
  - DOMAIN-SUFFIX,ggpht.com,🎬 流媒体
  - DOMAIN-SUFFIX,fastly.com,🎬 流媒体

  # ===== 5. 其他常用国外服务走代理 =====
  - DOMAIN-KEYWORD,github,🚀 节点选择
  - DOMAIN-SUFFIX,github.com,🚀 节点选择
  - DOMAIN-SUFFIX,github.io,🚀 节点选择
  - DOMAIN-SUFFIX,githubassets.com,🚀 节点选择
  - DOMAIN-SUFFIX,githubusercontent.com,🚀 节点选择
  - DOMAIN-KEYWORD,google,🚀 节点选择
  - DOMAIN-KEYWORD,twitter,🚀 节点选择
  - DOMAIN-KEYWORD,facebook,🚀 节点选择
  - DOMAIN-KEYWORD,instagram,🚀 节点选择
  - DOMAIN-KEYWORD,reddit,🚀 节点选择
  - DOMAIN-KEYWORD,telegram,🚀 节点选择
  - DOMAIN-KEYWORD,whatsapp,🚀 节点选择
  - DOMAIN-KEYWORD,zoom,🚀 节点选择
  - DOMAIN-KEYWORD,slack,🚀 节点选择
  - DOMAIN-KEYWORD,notion,🚀 节点选择

  # ===== 6. 最终兜底规则 =====
  # 所有未被上述规则匹配的流量,默认走代理节点
  - MATCH,🚀 节点选择

然后,在clash的订阅这里,新建、Local、随便起个名字,再上传刚刚准备好的订阅conf.yaml配置文件

9.png

然后,点击上图的保存按钮,再右键使用之,就订阅好了

10.png

而后开启代理

10.5.png

在clash里面也能看到我们的ip已经变成了新加坡了

11.png

至此,VPN搞定完毕,就可以正常访问github,用谷歌搜索学习代码知识啦

日志诊断 Skill:用 AI + MCP 一键解决BUG|得物技术

作者 得物技术
2026年4月2日 11:17

一、概述

做后端开发,调 BUG 有一个让人头疼的固定流程:打开日志平台,输入 traceId 或关键词,搜日志;从几十上百条日志里,找到关键的那几条;把日志里的类名、方法名复制出来,去 IDE 里找对应代码;结合代码逻辑,判断哪里出了问题;如果一次找不准,回去再搜日志,再翻代码……

这个过程相对固定,但非常耗时间。每次 BUG 定位,光在日志平台和 IDE 之间来回切换,就能消耗掉大半的时间。

最开始在去年 Q3 想到这个问题的时候,脑子里浮现的第一个方案是:用 Cursor + MCP,把日志平台接进来,再挂一个代码知识库,让 AI 帮我查日志。但这个方案有缺陷 —— 日志查询是「动态的」,它依赖环境、应用、时间范围,没办法静态预置。此外,这样处理没有办法做到比较丝滑地读代码、改代码。

后来开始用 Claude Code,接触到了 Skill 的概念:可以在项目里定义一套自定义命令,描述 AI 应该怎么执行这个命令的每个步骤,于是整个思路变得清晰了。

日志平台有 MCP,Claude Code 有 Skill,两者结合,就能让 AI 自动完成「查日志 → 找关键信息 → 扫描代码 → 定位问题」这整个闭环。然后在 PM 的帮助下,才有了 /log-diagnosis 这个 Skill。

二、日志平台 MCP 是什么

MCP 原理

日志平台推出了基于 MCP(Model Context Protocol)协议的日志查询服务,让 Claude 可以直接调用日志平台的能力,无需人工在日志平台上手动查询。

MCP 本质上是一种标准化的「工具调用协议」,Claude Code 通过 SSE(Server-Sent Events)长连接与 MCP Server 通信,实时获取日志数据。

MCP 环境对照

核心 MCP 工具

鉴权流程

secretKey(日志平台后管申请)
    ↓ acquireTokenTool
accessToken(1小时有效,最多同时存在5个)
    ↓ 携带 accessToken
logsQuery / logSqlQuery / countLogTool ...

secretKey 申请地址:进入日志管理后台 → 日志权限 → 我的应用 → 生成密钥。

三、/log-diagnosis Skill 是什么

Skill 工作原理

log-diagnosis 是一个运行在 Claude Code 里的自定义诊断命令。Claude Code 支持通过 .claude/skills/ 目录定义自定义技能(Skill),以 Markdown 文件描述行为规范,Claude 在收到对应命令时会自动加载并执行。你只需要把 traceId 或告警信息告诉它,剩下的全部交给 AI。完整执行链路如下:

用户输入 /log-diagnosis {环境} {代码分支} {诉求}
    ↓
Claude 加载 .claude/skills/log-diagnosis/SKILL.md
    ↓
读取 .diagnosis/config.json 获取当前环境配置
    ↓
检查 accessToken 是否过期,过期则自动刷新
    ↓
从 traceId 计算日志时间范围(取第9-16位16进制时间戳)
    ↓
调用日志平台 MCP 分页拉取全量日志(最多20页,不遗漏)
    ↓
切换到指定代码分支,结合日志关键词检索代码
    ↓
综合分析:上游日志 + 当前服务日志 + 代码逻辑 → 根因
    ↓
生成诊断报告(飞书文档 or 本地 Markdown)
    ↓
恢复原始代码分支

两种诊断入口

核心能力

  • Token 自动管理:accessToken 过期自动刷新,无需手动维护;
  • 分页全量拉取:自动分页拉完所有日志,禁止只查第一页就下结论(最多 20 页);
  • 跨服务分析:自动识别上下游服务,拉取关联服务日志交叉验证;
  • 代码联动:日志里出现的类名/方法名,直接在代码里精确定位。

queryString 语法规则

# 格式
{field} {操作符} "{值}" {连接符} {field} {操作符} "{值}"
# 操作符
=  : 精确匹配
≈  : 模糊匹配(like)
# 连接符
AND / OR / NOT
# 示例
trace_id"a1b2c3d4e5f6789012345678abcdef01"
trace_id"xxx" AND log_level = "ERROR"
endpoint ≈ "/api/your-endpoint" AND log_level"ERROR"
message ≈ "timeout"

注意:时间范围只通过 start/end 参数控制,不要写在 queryString 中。

四、安装与配置

安装日志平台 MCP

Claude Code

在 Claude Code 命令行中执行,按需安装对应环境:

# 测试环境
claude mcp add --transport sse dw-log-mcp-t1 https://{your-t1-aigw-domain}/api/v1/mcp/log-mcp/sse
# 预发环境
claude mcp add --transport sse dw-log-mcp-pre https://{your-pre-aigw-domain}/api/v1/mcp/log-mcp/sse
# 生产环境
claude mcp add --transport sse dw-log-mcp-prd https://{your-prd-aigw-domain}/api/v1/mcp/log-mcp/sse

安装后重启 Claude Code,执行 /mcp 确认连接状态正常。

Cursor

  1. 打开 Cursor Setting;

  2. 点击 Tools & MCP,添加 MCP Server;

  3. 添加 URL,MCP Server 名称任意。

建议按需安装 MCP Server,避免额外消耗 token,示例配置:

{
  "mcpServers": {
    "dw-log-mcp-t1": {
      "url": "https://{your-t1-aigw-domain}/api/v1/mcp/log-mcp/sse"
    },
    "dw-log-mcp-pre": {
      "url": "https://{your-pre-aigw-domain}/api/v1/mcp/log-mcp/sse"
    },
    "dw-log-mcp-prd": {
      "url": "https://{your-prd-aigw-domain}/api/v1/mcp/log-mcp/sse"
    },
    "dw-log-mcp-oversea-prd": {
      "url": "https://{your-oversea-aigw-domain}/api/v1/mcp/log-mcp/sse"
    }
  }
}

4. 返回设置,就可以看到已经连接上。

安装 /log-diagnosis Skill

将 log-diagnosis 目录放到项目的对应目录下:

Claude Code

your-project/
└── .claude/
    └── skills/
        └── log-diagnosis/
            ├── SKILL.md        # 技能行为规范(核心)
            ├── README.md       # 使用说明
            └── reference.md   # 附录:时间脚本、queryString 示例等

Cursor

your-project/
└── .cursor/
    └── skills/
        └── log-diagnosis/
            ├── SKILL.md        # 技能行为规范(核心)
            ├── README.md       # 使用说明
            └── reference.md   # 附录:时间脚本、queryString 示例等

配置 .diagnosis/config.json

首次运行会自动引导创建 (直接调用 /log-diagnosis,Skill 会一步步指示你给出 secret key),也可手动在项目根目录创建 .diagnosis/config.json:

your-project/
└── .cursor/
    └── skills/
        └── log-diagnosis/
            ├── SKILL.md        # 技能行为规范(核心)
            ├── README.md       # 使用说明
            └── reference.md   # 附录:时间脚本、queryString 示例等

字段说明:

secretKey:唯一需要人工填写的字段,在日志平台后管申请;

accessToken:首次使用时由 AI 自动调用 acquireTokenTool 获取,过期自动刷新;

accessTokenExpireAt:从 acquireTokenTool 返回值自动填充;

fields:调用 logFields 工具自动获取。

五、使用方式

命令格式:

/log-diagnosis {环境} {代码分支(可选)} {诉求描述}

参数说明:

  • {环境}:T1 / PRE / PRD(按实际环境标识填写);
  • {代码分支}:可选,留空则使用当前分支;
  • {诉求描述}:包含 traceId 或告警信息的问题描述,用自然语言书写即可。

示例:

# 用 traceId 定位接口异常
/log-diagnosis T1 feature/your-branch trace_id: "your-trace" 为什么最终没有返回数据
# 用告警信息分析错误原因
/log-diagnosis PRD master 告警详情:【接口:YourService/yourMethod】【业务码:10002000】【业务码消息:系统异常,请稍后重试】帮我分析问题可能性

一行命令,AI 全程接管,几分钟内给出根因分析。

六、实战案例:一个隐蔽的 SQL BUG

背景

某搜索接口在测试环境反馈没有返回数据。拿到 traceId,直接执行:

/log-diagnosis T1 feature/your-branch trace_id: "your-trace" 为什么最终没有返回数据

← 就这一句话,接下来全部交给 AI。

AI 自动拉取日志

Skill 触发后,AI 自动完成:

  • 从 traceId 推算出日志时间范围(2026-02-27 全天);
  • 检查 accessToken 已过期,自动刷新;
  • 调用日志平台 MCP,分 2 页拉取完整日志,共 73 条。

请求入参(从日志自动提取):

{
  "assembleByOrg": true,
  "channelType": "MANUAL",
  "orderNo": "your-order-no",
  "status": 1,
  "ticketNo": "your-ticket-no"
}

AI 还原完整调用链路

AI 自动识别出关键节点:resultList is empty,SQL 查询返回了空结果。问题在 DB 层,而不在业务逻辑层。

AI 提取组装后的查询 DTO

从日志中提取到 toSearchDTO 组装结果:

{
  "channelType": "MANUAL",
  "customerTag": 1,
  "deliveryMode": "某配送方式",
  "orderStatus": "8010",
  "orderType": "0",
  "productCategoryIds": [29],
  "status": 1,
  "ticketSource": 67,
  "ticketTypeId": 5802
}

AI 从日志中提取实际执行的 SQL 发现根因

ORM 框架在日志中打印了实际执行的 SQL,AI 直接读取并分析:

SELECT a.id, a.pid, a.name, a.mode, a.status, a.org_id, a.org_ids,
       a.ticket_group_id, a.tenant_id, a.is_del, a.channel_types
FROM your_type_table a
LEFT JOIN your_relation_table b
    ON b.tenant_id = 1 AND a.id = b.type_id AND b.type = 3 AND b.is_del = 0
WHERE a.tenant_id = 1 AND a.mode = 2 AND a.is_del = 0
  AND a.status = 1
  AND (a.channel_types IS NULL OR a.channel_types = '' OR FIND_IN_SET('MANUAL', a.channel_types) > 0)
  AND (b.root_id is null or b.root_id in (29))
  AND (a.order_types IS NULL OR a.order_types = '' OR FIND_IN_SET('0', a.order_types) > 0)
  AND (a.order_statuses IS NULL OR a.order_statuses = '' OR FIND_IN_SET('8010', a.order_statuses) > 0)
  AND (a.delivery_modes IS NULL OR a.delivery_modes = '' OR FIND_IN_SET('某配送方式', a.delivery_modes) > 0)
  AND (a.ticket_sources IS NULL OR a.ticket_sources = '' OR FIND_IN_SET(67, a.ticket_sources) > 0)
  AND (a.customer_tag IS NULL OR a.customer_tag = 1)   ← BUG 在此

AI 发现:其他字段都处理了 IS NULL 和 = ''(空字符串代表 “不限制”)两种情况,唯独 customer_tag 只判断了 IS NULL,遗漏了空字符串 '' 的情况。

SQL 语义对比:

-- 其他字段(正确):IS NULL 和 '' 都处理了
AND (a.order_types IS NULL OR a.order_types'' OR FIND_IN_SET('0', a.order_types) > 0)
AND (a.delivery_modes IS NULL OR a.delivery_modes'' OR FIND_IN_SET('某配送方式', a.delivery_modes) > 0)
AND (a.ticket_sources IS NULL OR a.ticket_sources'' OR FIND_IN_SET(67, a.ticket_sources) > 0)
-- customer_tag(遗漏了 = '' 的判断)← BUG
AND (a.customer_tag IS NULL OR a.customer_tag1)

DB 中现有的数据,customer_tag 字段都存的是空字符串(未配置),按业务语义本应匹配所有请求,却因为这个遗漏被全部过滤掉了。

AI 定位代码,给出修复方案

AI 在代码中直接找到对应的 MyBatis Mapper XML:

<!-- 问题代码 -->
<if test="customerTag != null">
    and (a.customer_tag IS NULL OR a.customer_tag = #{customerTag})
</if>
<!-- 修复后 -->
<if test="customerTag != null">
    and (a.customer_tag IS NULL OR a.customer_tag = '' OR a.customer_tag = #{customerTag})
</if>

效率对比

这个 BUG 的隐蔽性在于:SQL 语法正确,逻辑上也「看起来」没问题——只有对比了其他字段的写法,才能发现 customer_tag 独自遗漏了空字符串的处理。这类细节差异,人工排查很容易忽略,AI 反而很擅长。

七、诊断效率关键点

  • 有 traceId 时优先用 traceId 拉日志,可精准获取单次请求的完整链路,比关键词搜索精确得多;
  • 关注关键日志节点:toSearchDTO finished / search begins / resultList is empty / search finished 等,快速判断数据在哪一层丢失;
  • SQL 打印日志(ORM 框架输出)是黄金线索,直接反映最终执行的查询条件,AI 能从中发现肉眼难以察觉的差异;
  • 分页必须拉完:日志平台一次只返回部分数据,AI 会严格执行分页直到取完,确保不遗漏关键日志。

八、总结

核心思路:用「协议 + 规范」让 AI 接管固定流程:

这篇文章的本质,是一次对重复性工程劳动的自动化尝试。调 BUG 的过程——查日志、提取关键信息、找代码、分析原因——逻辑固定,步骤繁琐,但并不需要太多创造性思维。这类工作恰好是 AI 最擅长接管的。

实现这个闭环,靠的是两个关键组合:

  • MCP:让 AI 能够调用外部系统(日志平台),突破了「AI 只能处理静态上下文」的限制,实现了对动态数据的实时获取。
  • Skill:给 AI 一份行为规范,告诉它每一步该怎么做、先做什么后做什么、遇到什么情况怎么处理,把「一次性对话」变成「可复用的工程化能力」。

两者缺一不可。只有 MCP,AI 能查日志但不知道怎么系统地分析;只有 Skill,AI 有流程但没有数据来源。组合起来,才形成了真正可落地的闭环。

值得借鉴的地方:

识别「固定流程」是自动化的起点:不是所有工作都适合 AI 接管,但凡是「步骤固定、信息来源明确、输出格式可预期」的工作,都值得尝试用 Skill + MCP 的方式来自动化。排查 BUG 是一个典型,类似的还有:代码审查、性能分析报告生成、告警巡检等。

Skill 的本质是「给 AI 写操作手册」:Skill 文件不是在「训练模型」,而是在给 AI 一份清晰的 SOP。写得越细、约束越明确(比如「禁止只查第一页就下结论」「必须分页拉完所有数据」),AI 的执行质量越稳定。这和写给人看的文档本质上是一回事。

AI 擅长发现「横向对比」类的 BUG:本文的案例揭示了一个有意思的规律:AI 在处理「同类字段逻辑不一致」这类问题时,表现往往比人工更好。原因在于 AI 没有「先入为主」的经验偏见,不会因为「这段代码看起来没问题」就跳过,它会对所有字段做同等的审查。

最后说一句:AI 时代,工程师的核心竞争力不只是「能写代码」,更是「能把自己的经验和流程转化成可复用的 AI 能力」。/log-diagnosis 是一次小小的尝试,但背后的思路,值得在更多场景里延伸。

往期回顾

1.Redis 自动化运维最佳实践|得物技术

2.Claude在得物App数仓的深度集成与效能演进

3.Claude Code + OpenSpec 正在加速 AICoding 落地:从模型博弈到工程化的范式转移|得物技术

4.大禹平台:流批一体离线Dump平台的设计与应用|得物技术

5.基于 Cursor Agent 的流水线 AI CR 实践|得物技术

文 /阿程

关注得物技术,每周更新技术干货

要是觉得文章对你有帮助的话,欢迎评论转发点赞~

未经得物技术许可严禁转载,否则依法追究法律责任。

❌
❌