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

Go之封装Http请求和日志

小伙伴们,你们好呀!我是老寇!跟我一起学习封装Http请求和日志

Http请求

封装Http请求,直接使用 net/http 就行,主要是有两点需要注意,Https如何关闭校验客户端上传文件

Https如何关闭校验

// 跳过TLS证书校验
client := &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true},},
}

客户端上传文件

上传文件需遵循 RFC 1867 标准,因此,请求头 Content-Type设为 multipart/form-data;boundary=xxx

http.go

import ("bytes""crypto/tls""errors""io""mime/multipart""net/http"
)func SendRequest(method, url string, param io.Reader, header map[string]string) (*http.Response, error) {request, err := http.NewRequest(method, url, param)if err != nil {return nil, errors.New("创建 Request 失败,错误信息:" + err.Error())}if header != nil {for k, v := range header {request.Header.Set(k, v)}}return sendHttpRequest(request)
}func GetFormFile(fileName string, buf []byte) (io.Reader, string, error) {body := &bytes.Buffer{}writer := multipart.NewWriter(body)part, err := writer.CreateFormFile("file", fileName)if err != nil {return nil, "", errors.New("创建 FormFile 失败,错误信息:" + err.Error())}_, err = io.Copy(part, bytes.NewReader(buf))if err != nil {return nil, "", errors.New("复制字节数组失败,错误信息:" + err.Error())}err = writer.Close()if err != nil {return nil, "", errors.New("关闭 Writer 失败,错误信息:" + err.Error())}return body, writer.FormDataContentType(), nil
}func SendRequestAndGetBody(method, url string, param io.Reader, header map[string]string) ([]byte, error) {response, err := SendRequest(method, url, param, header)if err != nil {return nil, err}body, err := io.ReadAll(response.Body)if err != nil {return nil, errors.New("读取响应失败,错误信息:" + err.Error())}defer response.Body.Close()return body, nil
}func sendHttpRequest(request *http.Request) (*http.Response, error) {client := &http.Client{Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true},},}response, err := client.Do(request)if err != nil {return nil, errors.New("发送 HTTP 请求失败,错误信息:" + err.Error())}return response, nil
}

http_test.go

func Test_Http(t *testing.T) {// Get请求header = map[string]string{"Cookie": 'token=123',}_, _ = SendRequest(http.MethodGet, "https://localhost/api/test", nil ,header)// Post请求_, _ = SendRequest(http.MethodPost, "https://localhost/api/test", strings.NewReader("username=a&password=123") ,header)// Post上传文件【表单】// 这里便于演示,随便定义一个字节数组buf := []byte('123')formFile, contentType, err := core.GetFormFile("image.jpg", buf)if err != nil {fmt.println(err.Error())return}header = map[string]string{"Content-Type": contentType,}// 上传文件_, _ = SendRequest(http.MethodPost, "https://localhost/api/upload", formFile, header)
}

日志

日志对于一个项目来说是非常重要的,因此,很有必要根据业务进行挑选,go的常用日志包有gloglogruszap

维度glog (Google)logrus (社区维护)zap (Uber)
项目背景Kubernetes 等基础设施项目常用,轻量级早期 Go 社区主流,已停止更新(仅维护)高性能工业级实践,活跃维护
性能中等(基于标准库优化)较低(反射序列化,内存分配多)极高(预分配内存、零反射,每秒百万级日志)
日志级别4 级(Info/Warning/Error/Fatal)6 级(Trace/Debug/Info/Warn/Error/Fatal)7 级(Debug/Info/Warn/Error/Dpanic/Panic/Fatal)
结构化日志❌ 不支持(仅文本格式)✅ 支持(WithFields 动态类型)✅ 强类型(zap.String()),SugaredLogger 兼容非结构化
API 风格类似标准库(如 glog.Info("msg")链式调用(logrus.WithField().Info()显式类型(zap.Info("msg", zap.String("k","v"))
日志轮转❌ 需手动实现或第三方库❌ 依赖插件(如 file-rotatelogs)2✅ 原生支持(集成 lumberjack
内存分配较少较多(反射+动态字段)极少(预分配+非反射)
动态调级❌ 不支持✅ 需手动实现2✅ 原生支持(AtomicLevel
学习成本低(类似标准库)低(类似 fmt中(结构化 API 稍复杂,SugaredLogger 简化)
适用场景轻量级工具、K8s 生态兼容旧项目维护、插件依赖型应用高并发服务、云原生、性能敏感场景

考虑到网关设备,为了节约内存空间和极致的性能,我们采用zap封装,zap不支持日志滚动,所以,我们使用第三方组件 timberjack增强,支持日志滚动,日志保留天数,日志级别

log.go

import ("errors""github.com/DeRuina/timberjack""go.uber.org/zap""go.uber.org/zap/zapcore""log""time"
)const (PROD = "prod"
)type LogConfig struct {// 日志级别Level string `yaml:"level"`// 环境Profile string `yaml:"profile"`// 日志格式Pattern string `yaml:"pattern"`// 日志文件路径FilePath string `yaml:"file-path"`// 日志最大容量【单位M】MaxSize int `yaml:"max-size"`// 日志最大数量MaxBackups int `yaml:"max-backups"`// 日志保留时间【单位天】MaxAge int `yaml:"max-age"`// 是否压缩Compress bool `yaml:"compress"`// 本地时间,默认值:false(使用UTC)LocalTime bool `yaml:"local-time"`// json格式JsonFormat bool `yaml:"json-format"`// 转换频率RotationInterval time.Duration `yaml:"rotation-interval"`// 转换时间【分钟】RotateAtMinutes []int `yaml:"rotate-at-minutes"`// 日志时间格式BackupTimeFormat string `yaml:"backup-time-format"`
}func (c *LogConfig) InitLogger() (*zap.Logger, error) {timberjackLogger := &timberjack.Logger{Filename:         c.FilePath,MaxSize:          c.MaxSize,MaxBackups:       c.MaxBackups,MaxAge:           c.MaxAge,Compress:         c.Compress,LocalTime:        c.LocalTime,RotationInterval: c.RotationInterval,RotateAtMinutes:  c.RotateAtMinutes,BackupTimeFormat: c.BackupTimeFormat,}log.SetOutput(timberjackLogger)defer timberjackLogger.Close()writeSyncer := zapcore.AddSync(timberjackLogger)// 配置日志级别levelConfig := zap.NewAtomicLevel()level, err := zapcore.ParseLevel(c.Level)if err != nil {return nil, errors.New("日志级别不存在,请重新配置,错误信息:" + err.Error())}levelConfig.SetLevel(level)// 配置环境var encoderConfig zapcore.EncoderConfigswitch c.Profile {case PROD:encoderConfig = zap.NewProductionEncoderConfig()default:encoderConfig = zap.NewDevelopmentEncoderConfig()}// 设置时间格式encoderConfig.EncodeTime = c.customTimeEncodervar encoder zapcore.Encoder// Json格式if c.JsonFormat {encoder = zapcore.NewJSONEncoder(encoderConfig)} else {encoder = zapcore.NewConsoleEncoder(encoderConfig)}core := zapcore.NewCore(encoder, writeSyncer, levelConfig)return zap.New(core, zap.AddCaller()), nil
}func (c *LogConfig) customTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {enc.AppendString(t.Format(c.Pattern))
}

application.yaml

log:# 级别level: error# 环境profile: prod# 日志格式pattern: 2006-01-02 15:04:05.000# 日志文件路径file-path: test.log# 日志最大容量【单位M】max-size: 500# 日志最大数量max-backups: 10# 日志保留时间【单位天】max-age: 30# 是否压缩compress: true# 本地时间,默认值:false(使用UTC)local-time: true# json格式,true为json格式日志json-format: false# 转换频率rotation-interval: 24h# 转换时间【分钟】rotate-at-minutes:- 0- 15- 30- 45# 日志时间格式backup-time-format: 2006-01-02-15-04-05

config.go

import ("errors""gopkg.in/yaml.v3""os"
)type SystemConfig struct {Log LogConfig `yaml:"log"`
}func GetSystemConfig(path string) (*SystemConfig, error) {data, err := os.ReadFile(path)if err != nil {return nil, errors.New("读取配置文件失败,错误信息:" + err.Error())}sysConfig := &SystemConfig{}err = yaml.Unmarshal(data, sysConfig)if err != nil {return nil, errors.New("配置文件反序列化失败,错误信息:" + err.Error())}return sysConfig, nil
}

log_test.go

import ("testing"
)func Test_Log(t *testing.T) {config, err := GetSystemConfig("application.yaml")if err != nil {t.Error(err.Error())return}logger, err := config.Log.InitLogger()if err != nil {t.Error(err.Error())return}logger.Info("信息")logger.Warn("警告")logger.Error("错误")t.Log("日志测试通过")
}

注意:部署到网关上面,日志无法打印,需要对日志增加权限和不能以sudo运行

# 增加日志权限
sudo chmod -R 7777 /home/a/app/logs
# 运行项目,不能以sudo运行项目,否则日志无法打印,这是我遇到的坑
nohup /home/a/app/log > /dev/null 2>&1 &

我是老寇,我们下次再见啦!

http://www.dtcms.com/a/327908.html

相关文章:

  • 【新启航】飞机起落架减震筒的深孔测量方法探究 - 激光频率梳 3D 轮廓检测
  • 简单认识CSRF
  • 常见认证信息的传递方式
  • 深入理解数据库架构:从原理到实践的完整指南
  • 【QT】QT6下载安装
  • @(AJAX)
  • JS 模块化与打包工具
  • 基于Hadoop的农产品价格数据分析与可视化【Springboot】
  • 【已解决】win10为什么git无法弹出用户登录框呢?
  • 家政小程序系统开发:推动家政行业数字化转型,共创美好未来
  • unity shader ——屏幕故障
  • hashmap如何解决碰撞
  • Pytorch编译
  • 1.Ansible 自动化介绍
  • 网站测评-利用缓存机制实现XSS的分步测试方法
  • 设置默认的pip下载清华源(国内镜像源)和pip使用清华源
  • SQL tutorials
  • 鸿蒙下载图片保存到相册,截取某个组件保存到相册
  • 农业园区气象站在高标准农田的用处
  • 行业热点丨智能仿真时代:电子工程多物理场解决方案创新实践
  • USLR:一款用于脑MRI无偏倚平滑纵向配准的开源工具|文献速递-医学影像算法文献分享
  • 体育数据api接口,足球api篮球api电竞api,比赛赛事数据api
  • vmware虚拟机Ubuntu系统奔溃修复
  • 西安国际数字科创产业园:政策赋能筑长安数字产业集群
  • Linux学习-软件编程(标准IO)
  • 【ROS2】ROS2 基础学习教程 以lerobot-so100为例
  • specCPU2017在麒麟系统的简单测试
  • VisionPro——1.VP与C#联合
  • 前端/在vscode中创建Vue3项目
  • 【实时Linux实战系列】实时环境监测系统架构设计