ctx.Header(key string, value string)
中间件
var logger = log.New(os.Stdout, "", 0)

func hello(wr http.ResponseWriter, r *http.Request) {
    timeStart := time.Now()        //用于统计hello服务的耗时
    wr.Write([]byte("hello"))      //业务代码
    timeElapsed := time.Since(timeStart)  
    logger.Println(timeElapsed)
}

对于大多数的场景来讲,非业务的需求都是在http请求处理前做一些事情,并且在响应完成之后做一些事情。我们有没有办法使用一些重构思路把这些公共的非业务功能代码剥离出去呢?

//业务代码
func hello(wr http.ResponseWriter, r *http.Request) {
    wr.Write([]byte("hello"))
}

//中间件逻辑
func timeMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(wr http.ResponseWriter, r *http.Request) {
        timeStart := time.Now()

        // next handler
        next.ServeHTTP(wr, r)

        timeElapsed := time.Since(timeStart)
        logger.Println(timeElapsed)
    })
}
gin.Recovery()gin.Logger()
第三方日志库logrus
  • 由于Go语言内置的日志库功能比较简洁,实际开发中通常会选择使用第三方的日志库来进行开发。

  • 有七种日志级别:Trace, Debug, Info, Warning, Error, Fataland, Panic;Trace级别最高(最详细),Panic最低,默认Info。高于当前级别的信息不会输出。

    Trace:很细粒度的信息
    Debug:一般程序中输出的调试信息
    Info:关键操作,核心流程的日志
    Warn:警告信息,提示程序员注意
    Error:错误日志
    Fatal:致命错误,程序无法正常运转
    Panic:记录日志,调用panic()

  • 可扩展的Hook机制,允许使用者通过Hook的方式将日志分发到任意地方,如本地文件系统,logstash,elasticsearch或者mq等,或者通过Hook定义日志内容和格式等

  • 可选的日志输出格式,内置了两种日志格式JSONFormater和TextFormatter,还可以自定义日志格式

  • Field机制,通过Filed机制进行结构化的日志记录

  • 线程安全

testing包
func TestXxx(*testing.T)
//被测函数
func StringSliceEqual(a, b []string) bool {
    if len(a) != len(b) {
        return false
    }

    if (a == nil) != (b == nil) {
        return false
    }

    for i, v := range a {
        if v != b[i] {
            return false
        }
    }
    return true
}
//测试代码
import (
    "testing"
    . "github.com/smartystreets/goconvey/convey"
)

func TestStringSliceEqual(t *testing.T) {
    Convey("TestStringSliceEqual should return true when a != nil  && b != nil", t, func() {
        a := []string{"hello", "goconvey"}
        b := []string{"hello", "goconvey"}
        So(StringSliceEqual(a, b), ShouldBeTrue)
    })
}
  1. import goconvey包时,前面加点号".",以减少冗余的代码。凡是在测试代码中看到Convey和So两个方法,肯定是convey包的,不要在产品代码中定义相同的函数名 在这里插入图片描述
  2. 测试函数的名字必须以Test开头,而且参数类型必须为*testing.T
  3. 每个测试用例必须使用Convey函数包裹起来,它的第一个参数为string类型的测试描述,第二个参数为测试函数的入参(类型为*testing.T),第三个参数为不接收任何参数也不返回任何值的函数(习惯使用闭包)
  4. Convey函数的第三个参数闭包的实现中通过So函数完成断言判断,它的第一个参数为实际值,第二个参数为断言函数变量,第三个参数或者没有(当第二个参数为类ShouldBeTrue形式的函数变量)或者有(当第二个函数为类ShouldEqual形式的函数变量)