
























做 toB 一个非常高的成本是, 用户的环境/网络/数据等, 可能跟你预期的差异很大, 再加上沟通相对困难(涉及三方/四方, 无法便捷地登机器排查问题, 无法便捷地获取日志, 接口人业务或技术水平参差不齐).
而这时候, 你会面临一个问题, 如何在信息不足/沟通不畅的场景下, 尽量提升效率, 降低成本(大部分情况下, 逢单必结, 你必须解决问题, 没有选择的余地)
TLDR:
秒懂(如果不方便展示给用户, 可以通过request_id或者隐藏在页面等方式)request_id能汇总相关日志等等)有个代码初始化数据到数据库的逻辑报错了, 不管是什么原因引起的, 你需要解决!
Setting.objects.create(
key=k, value=v, defaults=x
)
此时, 新装环境没问题, 升级环境会报错,
通过异常信息知道创建由于唯一性限制报错了, 是由于用户手动插入了数据导致报错
此时, 用户问你, 数据有几百条, 哪条数据引起的? 我该怎么修复(成本: 小时级)
打印上下文!
try:
Setting.objects.create(
key=k, value=v, default=x
)
except Exception as e:
logger.exception("create setting fail, k=%s, v=%s, default=%s", k, v, x)
raise e
此时, 通过异常信息上下文可以看到哪条数据有问题!(成本: 分钟级)
代码考虑兼容性, 已发的版本/存量的系统, 最好能顺滑升级!
例如, 这里的create, 可以考虑用get_or_create替代(当然, 前提是符合需求)
有个功能是支持用户配置ldap相关地址, 拉取用户数据, 用户点击:
测试连接.
try:
test_connection(**params)
except Exception as e:
raise Exception("测试连接失败, 请检查配置")
此时, 用户坚信自己的配置是对的, 你如何帮他定位呢? (成本: 几个小时甚至一两天)
try:
test_connection(**params)
except Exception as e:
# 注意, 这里一定要logger.exception, 用error意义不大!
logger.exception("测试连接失败, 请检查配置")
raise Exception("测试连接失败, 请检查配置")
这时候的调试方式, 用户找你, 你告诉用户去哪里看日志!(成本: 分钟到几个小时)
try:
test_connection(**params)
except Exception as e:
# 注意, 这里一定要logger.exception, 用error意义不大!
logger.exception("测试连接失败, 请检查配置")
error_detail = f" ({type(e).__module__}.{type(e).__name__}: {str(e)})"
raise Exception("测试连接失败, 请检查配置" + error_detail)
这时候, 在页面上, 用户就能看到 报错的类型及错误信息
KeyError/ValueError等, 大概率是自己哪里代码逻辑处理不正确.invalid server address, socket connection error while opening: [Errno 111] Connection refused之类的;此时, 用户绝大多数可以在前端提示中看到, 无需支持!
有必要的话
invalid server address, 可以翻译成不合法的地址, 请检查xxxx字段, 必须是一个合法的域名, 建议同时带上一个error code方便快速确定问题/文档中检索关键字上下文, 但是需要注意脱敏用户登录, 输入的用户名/密码有问题或者账号本身有问题时;由于安全原因, 不能明确告诉对方具体是哪里有问题. 在用户看来, 无论怎么尝试, 报错总是一样的.
那么, 如果用户坚持自己输入是对的, 账号是正常的, 如何帮助其排查问题?
if user not exists:
raise PasswordWrongException()
if user.status in [DISABLED, LOCKED]:
raise PasswordWrongException()
if user login hit secure policies:
raise PasswordWrongException()
这种实现, 你只能看代码, 逐个同用户沟通检查(成本: 小时/天)
if user not exists:
logger.error("login fail, user<%s> not exists", user)
raise PasswordWrongException()
if user.status in [DISABLED, LOCKED]:
logger.error("login fail, user<%s> status is %s", user, user.status)
raise PasswordWrongException()
if user login hit secure policies:
# 这里有必要的话, 甚至需要明确到具体哪个policy
logger.error("login fail, user<%s> hit xxx secure policies", user)
raise PasswordWrongException()
这种实现, 让用户捞下日志就能明确具体问题(成本: 分钟)
如果应用本身依赖于一个第三方库, 且第三方库默认是有logger的.
此时, 调用第三方库报错, 可能返回的异常信息不足以帮助用户定位问题;
例如依赖ldap3同步用户数据
此时数据链路: 应用 -> ldap3 -> 网络 -> ldap服务器
网络/ldap服务器类型及配置差异较大, 导致非常容易出现异常ldap服务器往往由另一个团队维护, 具体的服务端配置对于配置用户并不透明ldap3库本身不同版本也存在bug此时, 用户配置了ldap相关参数, 但是点击报错, 并且, 用户坚信自己的配置是对的; 如何帮助用户调试?
我们是否可以支持, 开启ldap3的debug模式?
requests, ldap3是支持的, ldap3 logging上面的例子, 我们的应用实现上支持
from ldap3.utils import log as ldap3log
if settings.ENABLE_LDAP3_DEBUG:
ldap3log.set_library_log_detail_level(ldap3log.EXTENDED)
ldap3log.set_library_log_activation_level(logging.DEBUG)
那么, 在用户遇到问题无法解决的时候, 可以通过配置开启, 进入debug模式; 在日志中可以看到详细的 ldap3 -> 网络 -> ldap服务器 的数据细节.
如果基建有otel, 建议接入; 如果基建有日志采集汇聚, 建议接入!
能极大提升问题排查效率
所以, 尽量整合相应的SDK, 并且提供可插拔的配置方式, 让有条件的使用方开启.
如果:
用户 -> A -> B -> C -> D此时, 某个用户一个请求报错(前端或 API), 如何定位问题?
首先明确一个原则, A -> B 如果出现调用失败, 一定要记录日志, 包括url/request detail(header/body/credentials)/response detail(status/header/body)等等(如果什么都不记, 让用户或运维去 B 看日志, 这是不合理的!)
此时, 如果请求量很大, 调用链路长, 其实很难逐层定位每一层的处理/报错, 光去捞日志就已经非常费劲了(大部分时候, url或请求中并没有明显的特征)
如果整个调用链路接入了 OTEL, 那么问题相对简单; 但是如果没有接入, 如何处理?
我们可以通过 HTTP HEADER中带一个request_id来解决!
x-request-id网关/ESB/nginx等地方, 需要显示配置透传request_id, 如果有向下一级调用的请求, 需要传递该request_idrequest_idlogging.DEBUG主要目的是, 开启后, 可以准确定位问题!
所以, 在一些关键的逻辑中
debug日志, 覆盖所有关键的处理逻辑和返回debug需要带上足够多的上下文例如, 当关键一个函数, 中间有超过 5 个return, 开启debug之后, 需要能明确知道, 在哪里return的, 为什么会被return
防御不管是用户的输入, 还是从某些 API 拉取到的返回值, 尽量在获取到数据的入口处做好防御和检查, 例如, 类型/格式/非空等等
而不是, 层层传递后, 在某个地方异常报错, 这个时候排查的代价会大很多.
场景: 依赖于ldap3, 能确认用户的配置是正确的, 网络是联通的, 此时无论如何都连不上, 开debug模式也没看出问题
解决: 确定库版本, 2.6.1, 在官方github issue的bug report中查找相关报错信息, 然后升级了一个版本就成功了
常见的, 例如requests/加密相关库/ssl相关库等等
场景: 在一台机器部署, 连接另外一个服务始终连不上, 运维确定网络是没问题的; 后来在另一台机器部署后正常.
场景: 有一台机器reuqests库一直会报错, 换一台机器部署正常.(openssl相关)
场景: 对方上了网络策略, 存量机器没有处理导致原先正常的环境突然有问题了
很多时候, 你被拉去排查问题, 并不是因为你负责的服务有问题, 而是更底层的环境问题, 只是因为环境出问题你的服务刚好打了异常, 对方看到了而已.
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。