V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
guonaihong
V2EX  ›  程序员

golang 二次开发链式库省事方法

  •  
  •   guonaihong ·
    guonaihong · 2019-10-31 09:33:57 +08:00 · 1670 次点击
    这是一个创建于 1849 天前的主题,其中的信息可能已经有所发展或是发生改变。

    这篇说下 go 的一个很重要的技巧,在包装一些链式 API 的时候很实用。

    问题由来

    预热

    现在用 zerolog 举例,zerolog 是个用法很简单的结构化日志库,性能不错。通过下面的例子感受下用法。

    package main
    
    import (
        "github.com/rs/zerolog"
        "github.com/rs/zerolog/log"
    )
    
    func main() {
       
        log.Debug().
            Str("Scale", "833 cents").
            Float64("Interval", 833.09).
            Msg("Fibonacci is everywhere")
        
    }
    
    // Output: 
    // {"level":"debug","Scale":"833 cents","Interval":833.09,"message":"Fibonacci is everywhere"}
    
    

    想要的效果(下面是伪代码)

    想在 zerolog 的链式 API 加入 ID 函数,还能继承原有的一堆 API,比如.ID("1234") 会在 json 字段里面插入{"ID":"1234"}

    package main
    
    import (
        "github.com/rs/zerolog"
        "github.com/rs/zerolog/log"
    )
    
    func main() {
       
        log.Debug().ID("request id")
            Str("Scale", "833 cents").
            Float64("Interval", 833.09).
            Msg("Fibonacci is everywhere")
        
    }
    // Output: {"level":"debug","Scale":"833 cents","Interval":833.09,"message":"Fibonacci is everywhere"}
    

    本能反应,其实不对

    加上一短代码

    func (e *zerolog.Event) ID(id string) *zerolog.Event {
        e.Str("ID", id) 
        return e
    }
    
    // 输出
    // ./wenti.go:9:6: cannot define new methods on non-local type zerolog.Event
    // ./wenti.go:16:13: log.Debug().ID undefined (type *zerolog.Event has no field or method ID)
    
    

    触发了编译器限制,这条路走不通。

    理清思路,重新出发

    go 是没有语法上的继承的,只有组合。结构提内嵌可以实现方法的继承,在结构提里面写过 sync.Mutex 的都知道。

    type Stats struct {
      sync.Mutex
    
    }
    
    func (s *Stats) Snapshot() map[string]int {
      s.Lock()
      defer s.Unlock()
    }
    

    正确代码(核心)

    使用匿名结构体嵌套,实现方法的复用。

    type event struct {
    	*zerolog.Event
    }
    
    func (e *event) ID(id string) *event {
    	return &event{e.Str("ID", id)}
    }
    
    
    

    正确代码(完整)

    package main
    
    import (
    	"github.com/rs/zerolog"
    	"github.com/rs/zerolog/log"
    )
    
    type Ulog struct {
    	zerolog.Logger
    }
    
    func (u *Ulog) Debug() *event {
    	return &event{u.Logger.Debug()}
    }
    
    func (u *Ulog) Info() *event {
    	return &event{u.Logger.Info()}
    }
    
    func (u *Ulog) Warn() *event {
    	return &event{u.Logger.Warn()}
    }
    
    func (u *Ulog) Error() *event {
    	return &event{u.Logger.Error()}
    }
    
    type event struct {
    	*zerolog.Event
    }
    
    func (e *event) ID(id string) *event {
    	return &event{e.Str("ID", id)}
    }
    
    func main() {
    
    	u := Ulog{Logger: log.Logger}
    	u.Debug().
    		ID("hello").
    		Str("Scale", "833 cents").
    		Float64("Interval", 833.09).
    		Msg("Fibonacci is everywhere")
    }
    
    

    github

    https://github.com/guonaihong/gout

    5 条回复    2019-11-04 21:37:13 +08:00
    TypeErrorNone
        1
    TypeErrorNone  
       2019-10-31 11:36:48 +08:00
    不还是包裹嵌套嘛
    guonaihong
        2
    guonaihong  
    OP
       2019-10-31 12:40:20 +08:00
    @TypeErrorNone 看来 ten 兄对 go 也和熟悉。帮看看我的方法可还有提升的地方?
    crclz
        3
    crclz  
       2019-10-31 19:05:19 +08:00
    很流畅( Fluent )
    reus
        4
    reus  
       2019-11-03 13:20:47 +08:00
    这都是入门级别的知识
    guonaihong
        5
    guonaihong  
    OP
       2019-11-04 21:37:13 +08:00
    @reus 厉害厉害,可否帮我看下,上面可有能优化的地方。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2300 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 15:57 · PVG 23:57 · LAX 07:57 · JFK 10:57
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.