当前位置: 首页 > news >正文

Go语言错误处理与日志记录最佳实践

Go语言错误处理与日志记录最佳实践引言错误处理和日志记录是任何生产级应用的关键组成部分。Go语言提供了简洁的错误处理机制同时社区也有丰富的日志库可供选择。本文将深入探讨Go语言的错误处理模式、日志记录最佳实践以及如何构建健壮的错误处理体系。一、Go语言错误处理基础1.1 error接口type error interface { Error() string }1.2 创建错误// 使用errors.New创建简单错误 err : errors.New(connection failed) // 使用fmt.Errorf创建格式化错误 err : fmt.Errorf(failed to connect to %s: %w, addr, err) // 自定义错误类型 type MyError struct { Code int Message string } func (e *MyError) Error() string { return fmt.Sprintf(error %d: %s, e.Code, e.Message) }1.3 错误包装与解包import errors // 包装错误 err : fmt.Errorf(failed to read file: %w, ioErr) // 判断错误类型 if errors.Is(err, os.ErrNotExist) { fmt.Println(file not found) } // 解包错误 var targetErr *MyError if errors.As(err, targetErr) { fmt.Printf(error code: %d\n, targetErr.Code) }二、错误处理模式2.1 错误返回模式func OpenFile(path string) (*os.File, error) { f, err : os.Open(path) if err ! nil { return nil, fmt.Errorf(open file failed: %w, err) } return f, nil }2.2 哨兵错误模式var ErrNotFound errors.New(not found) func GetUser(id int) (*User, error) { user, ok : users[id] if !ok { return nil, ErrNotFound } return user, nil } func main() { user, err : GetUser(1) if err ! nil { if errors.Is(err, ErrNotFound) { fmt.Println(user not found) } else { fmt.Printf(unexpected error: %v\n, err) } } }2.3 自定义错误类型type APIError struct { StatusCode int Message string Err error } func (e *APIError) Error() string { return fmt.Sprintf(API error %d: %s, e.StatusCode, e.Message) } func (e *APIError) Unwrap() error { return e.Err } func HandleRequest(w http.ResponseWriter, r *http.Request) { err : processRequest(r) if err ! nil { var apiErr *APIError if errors.As(err, apiErr) { w.WriteHeader(apiErr.StatusCode) w.Write([]byte(apiErr.Message)) } else { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(internal server error)) } } }三、日志记录最佳实践3.1 使用zap日志库go get go.uber.org/zapfunc main() { logger, err : zap.NewProduction() if err ! nil { panic(err) } defer logger.Sync() // 基本日志 logger.Info(server started, zap.String(host, localhost), zap.Int(port, 8080), ) // 错误日志 logger.Error(failed to connect to database, zap.String(dsn, localhost:3306), zap.Error(err), ) }3.2 结构化日志type RequestLogger struct { logger *zap.Logger } func (rl *RequestLogger) LogRequest(r *http.Request, duration time.Duration, statusCode int) { rl.logger.Info(request completed, zap.String(method, r.Method), zap.String(path, r.URL.Path), zap.Int(status, statusCode), zap.Duration(duration, duration), zap.String(remote_addr, r.RemoteAddr), ) }3.3 日志级别配置config : zap.Config{ Level: zap.NewAtomicLevelAt(zap.InfoLevel), Development: false, DisableCaller: false, DisableStacktrace: false, Sampling: zap.SamplingConfig{ Initial: 100, Thereafter: 100, }, Encoding: json, EncoderConfig: zap.NewProductionEncoderConfig(), OutputPaths: []string{stdout, /var/log/app.log}, ErrorOutputPaths: []string{stderr}, } logger, _ : config.Build()四、错误处理与日志集成4.1 中间件错误处理func ErrorMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer func() { if err : recover(); err ! nil { logger.Error(panic recovered, zap.Any(panic, err), zap.Stack(stacktrace), ) w.WriteHeader(http.StatusInternalServerError) } }() next.ServeHTTP(w, r) }) }4.2 统一错误响应type ErrorResponse struct { Error string json:error Message string json:message,omitempty RequestID string json:request_id,omitempty Timestamp time.Time json:timestamp } func HandleError(w http.ResponseWriter, r *http.Request, err error, statusCode int) { requestID : r.Context().Value(requestIDKey).(string) logger.Error(request error, zap.String(request_id, requestID), zap.Int(status_code, statusCode), zap.Error(err), ) response : ErrorResponse{ Error: http.StatusText(statusCode), Message: err.Error(), RequestID: requestID, Timestamp: time.Now(), } w.Header().Set(Content-Type, application/json) w.WriteHeader(statusCode) json.NewEncoder(w).Encode(response) }五、错误处理最佳实践5.1 错误处理检查清单实践说明不要忽略错误始终检查并处理error返回值错误包装使用%w包装原始错误保留错误链错误类型判断使用errors.Is和errors.As判断错误类型错误日志在错误发生的地方记录详细日志错误传播不要在中间层吞掉错误5.2 避免的错误处理反模式// bad: 吞掉错误 func badExample() { f, _ : os.Open(file.txt) // 错误忽略了错误 defer f.Close() } // bad: 重复包装错误 func badWrap(err error) error { return fmt.Errorf(error: %w, fmt.Errorf(error: %w, err)) } // good: 单层包装保留原始错误 func goodWrap(err error) error { return fmt.Errorf(failed to process: %w, err) }六、日志记录最佳实践6.1 日志格式规范// 推荐格式级别、时间戳、请求ID、消息、键值对 logger.Info(user login, zap.String(request_id, abc123), zap.String(user_id, 12345), zap.String(ip, 192.168.1.1), )6.2 日志轮换import gopkg.in/natefinch/lumberjack.v2 logger, _ : zap.NewProductionConfig().Build( zap.WrapCore(func(core zapcore.Core) zapcore.Core { writer : zapcore.AddSync(lumberjack.Logger{ Filename: /var/log/app.log, MaxSize: 100, // MB MaxBackups: 3, MaxAge: 7, // days Compress: true, }) return zapcore.NewCore( zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()), writer, zap.InfoLevel, ) }), )6.3 敏感信息脱敏func sanitizePassword(s string) zap.Field { return zap.String(password, ***) } logger.Info(user created, zap.String(username, alice), sanitizePassword(secret123), )七、实战案例错误处理框架package errors import ( fmt net/http ) type ErrorCode int const ( ErrInternal ErrorCode iota 1 ErrBadRequest ErrNotFound ErrUnauthorized ) type AppError struct { Code ErrorCode Message string Err error } func (e *AppError) Error() string { return e.Message } func (e *AppError) Unwrap() error { return e.Err } func (e *AppError) StatusCode() int { switch e.Code { case ErrBadRequest: return http.StatusBadRequest case ErrNotFound: return http.StatusNotFound case ErrUnauthorized: return http.StatusUnauthorized default: return http.StatusInternalServerError } } func New(code ErrorCode, message string, err error) *AppError { return AppError{ Code: code, Message: message, Err: err, } }结论错误处理和日志记录是构建可靠应用的基础。Go语言的错误处理机制简洁而强大通过合理使用错误包装、自定义错误类型和结构化日志可以构建健壮的错误处理体系。良好的错误处理实践能够提高应用的可观测性和可维护性是生产级应用不可或缺的一部分。
http://www.zskr.cn/news/1311410.html

相关文章:

  • 利用 Taotoken 模型广场为特定任务选择最优大模型
  • 辽宁草坪价格实测排行:五家源头基地性价比对比 - 奔跑123
  • 论APS智能排产:让生产排程从“经验博弈“到“智能决策“的进化
  • 全国宠物托运机构排行:合规服务与体验实测对比 - 奔跑123
  • Go语言测试与质量保障:从单元测试到集成测试完整指南
  • Dify 工作流实战:用 Workflow 编排一个可控的 AI 自动化处理流程
  • 【Claude Redis缓存方案实战白皮书】:20年架构师亲授高并发场景下99.99%命中率的5层缓存协同设计
  • TI SimpleLink CC26xx/CC13xx超低功耗无线平台架构解析与实战
  • Python 簡單的 股市資料 API 呼叫範例
  • 跟着 MDN 学 HTML day_62:(HTML调试与常见错误修复指南)
  • 百考通AI:锚定研究航向,为广大学子解锁高效、规范、专业的学术起步新路径
  • Atmel Studio ASF框架入门:从零掌握AVR/SAM开发与实战技巧
  • 四川市政管道CCTV检测哪家强?2026年非开挖修复行业优选服务商深度解析 - 深度智识库
  • 软件测试实验六
  • 五相同步电机模型预测控制:原理、算法设计与仿真实现
  • claude windows安装
  • AI工作流编排框架aiflows:构建复杂AI应用的模块化解决方案
  • 终极微信好友检测指南:3分钟找出谁删了你
  • 2026年四川市政管道紫外光固化厂家推荐——专业实力与本土标杆解析 - 深度智识库
  • 替换背景的修图软件有哪些?一文对比20+款工具,找到最适合你的抠图方案
  • 3D视频转2D终极指南:用VR-Reversal解锁沉浸式观影新体验
  • 为OpenClaw智能体工具配置Taotoken作为后端模型服务
  • 李辉《曾国藩日记》笔记:要有先见之明,也还要有耐心!
  • 使用taotoken cli工具一键配置团队github仓库的开发环境
  • 终极指南:如何用Snipe-IT免费开源系统解决企业IT资产追踪难题
  • 体验 Taotoken 官方折扣价带来的模型调用成本下降
  • 基于树莓派与传感器实现智能门情景音效触发系统
  • 别再为地图边界发愁了!Cartopy绘制中国区域气象图的正确姿势与避坑指南
  • 如何重新定义macOS兼容性:OpenCore Legacy Patcher的完整实践指南
  • 解决Matlab硬件支持包安装失败:手把手教你手动部署Autosar工具链