惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

U
Unit 42
T
Threatpost
C
CERT Recently Published Vulnerability Notes
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Security Archives - TechRepublic
Security Archives - TechRepublic
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
K
Kaspersky official blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Attack and Defense Labs
Attack and Defense Labs
N
News and Events Feed by Topic
Project Zero
Project Zero
H
Heimdal Security Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Know Your Adversary
Know Your Adversary
Google Online Security Blog
Google Online Security Blog
W
WeLiveSecurity
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Schneier on Security
Schneier on Security
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
N
News | PayPal Newsroom
Hacker News - Newest:
Hacker News - Newest: "LLM"
H
Hacker News: Front Page
L
LINUX DO - 热门话题
Spread Privacy
Spread Privacy
T
Threat Research - Cisco Blogs
Cloudbric
Cloudbric
V
Vulnerabilities – Threatpost
Hacker News: Ask HN
Hacker News: Ask HN
S
Securelist
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
TaoSecurity Blog
TaoSecurity Blog
NISL@THU
NISL@THU
N
News and Events Feed by Topic
S
Security Affairs
The Last Watchdog
The Last Watchdog
T
Tor Project blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
T
The Exploit Database - CXSecurity.com
Simon Willison's Weblog
Simon Willison's Weblog
P
Palo Alto Networks Blog
AWS News Blog
AWS News Blog
P
Proofpoint News Feed
C
Cisco Blogs
C
Cyber Attacks, Cyber Crime and Cyber Security
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
L
LINUX DO - 最新话题
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
T
Tenable Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
S
Schneier on Security

Posts on WKLKEN THINKING

apisix 中的 lrucache apisix 中的服务发现机制 apisix 中的负载均衡 apisix etcd机制 聊聊框架 关于 k8s 的 zero downtime deployment 一些建议 apisix 遇到的一些问题 关于在除夕前一天换了一个洗衣机的故事 Django DRF 性能优化 DRF 的一些实践 Part1: Serializer DRF继承关系图 Better Code: 关于接口的灵活性 新的仓库: wklken/naming 缓存使用的一些经验 Better Code: 抽象: 可扩展性与可维护性的抉择 Better Code: 异常时, 该提示用户哪些信息? Better Code: 更好的异常日志打印 Go: some libs Go: go-redis/cache升级的坑 Go: logrus性能提升 Go: gin validation 远程办公的一点总结 Go: 开发过程中的一些bug 项目管理实践: 风险驱动开发 漫谈技术选型 Go: 基于 apitest 做handler层单元测试 Go: go-sql-driver interpolateparams参数优化 [分享]深度工作 你需要更多的思考时间 Django项目重构小结 工作七年小结: 学习,生活及其他 [分享]bash日常: bash-utils 极客时间推广海报 2017总结: 予时光以意义 k8s APIServer源码: api注册详细细节 k8s APIServer源码: api注册主体流程 k8s APIServer源码: 服务启动 k8s APIServer源码: go-restful框架 重构 - 读书笔记(Python示例) 写给新人的沟通建议 vim 杂谈 - 关于快速编辑 vim 杂谈 - 关于移动 读书笔记-重构: 章11 处理概括关系 读书笔记-重构: 章10 简化函数调用 读书笔记-重构: 章9 简化表达式 读书笔记-重构: 章8 重新组织数据 读书笔记-重构: 章7 在对象之间搬移特性 读书笔记-重构: 章6 重新组织函数 Python 代码规范小结 [分享]关于vim ElasticSearch集群部署文档 Logstash+ElasticSearch处理mysql慢查询日志 [分享]关于代码调试DE那些事 Logstash+ElasticSearch+Kibana- 实现相对通用的数据收集分析 ELK维护的一些点(二) [分享]Python源码剖析-数据结构 一些Centos Python生产环境的部署命令 摘录<<6个月学会任何一种外语>> ELK 维护的一些点 也许是一个新的开始 一些vim的个性化配置 读书笔记-调试九法 这段时间的一些想法 Python 源码阅读 - 垃圾回收机制 我为什么要写博客 APUE笔记-第一章 UNIX基础知识 Python源码阅读-闭包的实现 Python源码阅读-内存管理机制(二) Python源码阅读-内存管理机制(一) Python-基础-数据结构小结 '活动'设计的一些trick 一些简单的Python测试题 我的tmux配置及说明【k-tmux】 Review and Restart 工作四周年小结 vim插件: surround & repeat[成对符号编辑] vim插件: gundo[时光机] vim插件: expand-region[区域选中] vim插件: quickrun[快速执行] vim插件: trailing-whitespace[行尾空格处理] vim插件: closetag[成对标签补全] vim插件: ctrlp[文件搜索] vim插件: airline[状态栏增强] vim插件: theme[主题] vim插件: tagbar[大纲式导航] vim插件: nerdcommenter[快速注释] vim插件: rainbow_parentheses[括号高亮] vim插件: syntastic[语法检查] vim插件: delimitmate[符号自动补全] vim插件: matchit[成对标签跳转] vim插件: easy-align[快速对齐] vim插件: multiple-cursors[多光标操作] vim插件: vim-signature[快速标记跳转] vim插件: easymotion[快速跳转] vim插件: vundle[管理插件] Elasticsearch几个问题的解决 分享一份 Vim 简介PPT k-vim 更新9.0版本 关于知识管理工具的思考 Logstash+ElasticSearch+Kibana处理nginx访问日志
Go: 一种error wrap调用链处理方式
2021-01-26 · via Posts on WKLKEN THINKING

这是在做 Golang 项目中的一些实践

背景

在做一套内部公共服务, 提供后台API调用的时候, 某些情况下, 会返回500错误, 此时有两种做法

  1. 在后台记录详细的500日志, 返回给调用方一个request_id, 调用方拿request_id查询日志定位问题
  2. 直接在response body中返回错误信息

选择了问题排查路径短一些的 2, 错误信息中, 需要包含调用链路以及根因错误的详细信息, 提升问题排查的效率

这样, 开发者在看到报错response body的同时, 可以直接知道调用链, 不需要使用请求重新复现问题, 节约了大量的复现/沟通的时间

最终效果

system error[request_id=0838c4090cfa4f4f9dceea5fd8c7b029]: [Handler:validateSystemSuperUser] impls.ListSubjectRoleSystemID subjectType=`user`, subjectID=`test1` fail%!(EXTRA string=test1) => [Cache:GetSubjectRole] SubjectRoleCache.Get subjectType=`user`, subjectID=`test1` fail => [Cache:GetSubjectPK] SubjectPKCache.Get _type=`user`, id=`test1` fail => [SubjectSVC:GetPK] GetPK _type=`user`, id=`test1` fail => [Raw:Error] sql: no rows in result set

处理机制

使用error wrap, 一层层抛出

1. 定义pkg/errorx

注意, package name使用errorx, 可以规避同标准库命名的冲突; 同时import的时候不需要alias

type Error struct {
	message string
	err     error
}

func (e Error) Error() string {
	return e.message
}

func (e Error) Is(target error) bool {
	if target == nil || e.err == nil {
		return e.err == target
	}

	return errors.Is(e.err, target)
}

func (e *Error) Unwrap() error {
	u, ok := e.err.(interface {
		Unwrap() error
	})
	if !ok {
		return e.err
	}

	return u.Unwrap()
}

2. 封装wrapfunc helper

注意,

  • 这里如果是最原始的err(即wrap前的err), 会使用[Raw:Error]标注
  • 如果是中间层的, 会附加layer(哪一层)和function(哪个函数)这两个信息
  • 这里定义了一个 helper 方法, 用于在
func makeMessage(err error, layer, function, msg string) string {
	var message string
	var e Error
	if errors.As(err, &e) {
		message = fmt.Sprintf("[%s:%s] %s => %s", layer, function, msg, err.Error())
	} else {
		message = fmt.Sprintf("[%s:%s] %s => [Raw:Error] %v", layer, function, msg, err.Error())
	}

	return message
}

func Wrapf(err error, layer string, function string, format string, args ...interface{}) error {
	if err == nil {
		return nil
	}

	msg := fmt.Sprintf(format, args...)

	return Error{
		message: makeMessage(err, layer, function, msg),
		err:     err,
	}
}

type WrapfFuncWithLayerFunction func(err error, format string, args ...interface{}) error

func NewLayerFunctionErrorWrapf(layer string, function string) WrapfFuncWithLayerFunction {
	return func(err error, format string, args ...interface{}) error {
		return Wrapf(err, layer, function, format, args...)
	}
}

3. 使用

在某个函数体内, 涉及到需要return err或中间层

errorWrapf := errorx.NewLayerFunctionErrorWrapf("Handler", "ListSubject")

_, err := svc.ListPaging(body.Type, body.Limit, body.Offset)
if err != nil {
    err = errorWrapf(err, "svc.ListPaging type=`%s` limit=`%d` offset=`%d` fail!",
			body.Type, body.Limit, body.Offset)
		return err
}

注意, wrap时包含关键的调用参数/返回值等等, 但是不能包含敏感数据