V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
guonaihong
V2EX  ›  程序员

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

  •  
  •   guonaihong ·
    guonaihong · Oct 31, 2019 · 2042 views
    This topic created in 2375 days ago, the information mentioned may be changed or developed.

    这篇说下 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 replies    2019-11-04 21:37:13 +08:00
    TypeErrorNone
        1
    TypeErrorNone  
       Oct 31, 2019
    不还是包裹嵌套嘛
    guonaihong
        2
    guonaihong  
    OP
       Oct 31, 2019
    @TypeErrorNone 看来 ten 兄对 go 也和熟悉。帮看看我的方法可还有提升的地方?
    crclz
        3
    crclz  
       Oct 31, 2019
    很流畅( Fluent )
    reus
        4
    reus  
       Nov 3, 2019
    这都是入门级别的知识
    guonaihong
        5
    guonaihong  
    OP
       Nov 4, 2019
    @reus 厉害厉害,可否帮我看下,上面可有能优化的地方。
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   757 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 20:57 · PVG 04:57 · LAX 13:57 · JFK 16:57
    ♥ Do have faith in what you're doing.