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

2019-10-31 09:33:57 +08:00
 guonaihong

这篇说下 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

1586 次点击
所在节点    程序员
5 条回复
TypeErrorNone
2019-10-31 11:36:48 +08:00
不还是包裹嵌套嘛
guonaihong
2019-10-31 12:40:20 +08:00
@TypeErrorNone 看来 ten 兄对 go 也和熟悉。帮看看我的方法可还有提升的地方?
crclz
2019-10-31 19:05:19 +08:00
很流畅( Fluent )
reus
2019-11-03 13:20:47 +08:00
这都是入门级别的知识
guonaihong
2019-11-04 21:37:13 +08:00
@reus 厉害厉害,可否帮我看下,上面可有能优化的地方。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/614689

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX