在经历了一年半的折腾后,go-spring v1.1.1 终于发布了,这是一个全面重构的版本,更加符合 go 语言的开发习惯。
- 它是一个全新的版本,命名更加符合 go 规范,模块划分更加合理,核心设计也更加简洁;
- 它是一个具有重大突破的版本,突破性的支持统一日志框架,突破性的支持流量录制和回放;
- 它是一个功能庞大的版本,涵盖了日常开发所需的方方面面,再也不用纠结使用哪个依赖包。
1. 新版本 log 模块全面遵循 log4j2 的架构,具有超级灵活的适配能力。
func init() {
log.RegisterPlugin("ExampleLayout", log.PluginTypeLayout, (*ExampleLayout)(nil))
}type ExampleLayout struct {
LineBreak bool `PluginAttribute:"lineBreak,default=true"`
}func (c *ExampleLayout) ToBytes(e *log.Event) ([]byte, error) {
buf := bytes.NewBuffer(nil)
enc := log.NewFlatEncoder(buf, "||")
prefix := fmt.Sprintf("[%s][%s:%d][%s] ", e.Level(), e.File(), e.Level(), e.Time().Format("2006-01-02 15:04:05.000"))
err := enc.AppendBuffer([]byte(prefix))
if err != nil {
return nil, err
}
if ctx := e.Entry().Context(); ctx != nil {
span := SpanFromContext(ctx)
if span != nil {
s := fmt.Sprintf("trace_id=%s||span_id=%s||", span.TraceID, span.SpanID)
err = enc.AppendBuffer([]byte(s))
if err != nil {
return nil, err
}
}
}
for _, f := range e.Fields() {
err = enc.AppendKey(f.Key)
if err != nil {
return nil, err
}
err = f.Val.Encode(enc)
if err != nil {
return nil, err
}
}
if c.LineBreak {
buf.WriteByte('\n')
}
return buf.Bytes(), nil
}func main() {
config := `
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console">
<ExampleLayout/>
</Console>
</Appenders>
<Loggers>
<Root level="trace">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
`err := log.RefreshBuffer(config, ".xml")
util.Panic(err).When(err != nil)logger := log.GetLogger("xxx")
logger.Info("a", "=", "1")
logger.Infof("a=1")
logger.Infow(log.Message("a=%d", 1))span := &Span{TraceID: "1111", SpanID: "2222"}
ctx := ContextWithSpan(context.Background(), span)
logger.WithContext(ctx).Info("a", "=", "1")
logger.WithContext(ctx).Infof("a=1")
logger.WithContext(ctx).Infow(log.Message("a=%d", 1))
}///////////////////////////// observability /////////////////////////////
type Span struct {
TraceID string
SpanID string
}type spanKeyType int
var spanKey spanKeyType
func SpanFromContext(ctx context.Context) *Span {
v := ctx.Value(spanKey)
if v == nil {
return nil
}
return v.(*Span)
}func ContextWithSpan(ctx context.Context, span *Span) context.Context {
return context.WithValue(ctx, spanKey, span)
}
2. 新版本 IoC 容器支持 Logger 注入,日常开发非常方便,同时支持结构化日志。
type MyController struct {
Logger *log.Logger `logger:""`
}func (c *MyController) onInit(ctx gs.Context) error {
ctx.Go(func(ctx context.Context) {
defer func() { c.Logger.Info("exit after waiting in ::Go") }()ticker := time.NewTicker(10 * time.Millisecond)
defer ticker.Stop()for {
select {
case <-ctx.Done():
return
case <-ticker.C:
c.Logger.Info("::Go")
}
}
})
return nil
}
3. 新版本支持完全不使用 Go-Spring 内置的启动架构,只使用 IoC 容器也能启动 Web 服务。
package main
import (
"fmt"
"net/http""github.com/gin-gonic/gin"
"github.com/go-spring/spring-base/log"
"github.com/go-spring/spring-base/util"
"github.com/go-spring/spring-core/gs"
)//---------------- Controller -----------------------------//
type Controller struct {
HelloController
}type HelloController struct {
Service *HelloService `autowire:""`
}func (c *HelloController) Hello(ctx *gin.Context) {
s := c.Service.Hello(ctx.Query("name"))
ctx.String(http.StatusOK, s)
}//---------------- Service -------------------------------//
type HelloService struct {
}func (s *HelloService) Hello(name string) string {
return "hello " + name + "!"
}//---------------- Engine --------------------------------//
type Engine struct {
Engine *gin.Engine
Address string `value:"${http.addr:=:8080}"`
Controller *Controller `autowire:""`
Exit chan struct{} `autowire:""`
}func (e *Engine) Init() {
e.Engine = gin.Default()
e.Engine.GET("/hello", e.Controller.Hello)
go func() {
err := e.Engine.Run(e.Address)
fmt.Println(err)
e.Exit <- struct{}{}
}()
}//---------------- main ---------------------------------//
func main() {
config := `
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="Console"/>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
`
err := log.RefreshBuffer(config, ".xml")
util.Panic(err).When(err != nil)exit := make(chan struct{})
c := gs.New()
c.Object(exit)
c.Object(new(Controller))
c.Object(new(HelloService))
c.Object(new(Engine)).Init((*Engine).Init)
err = c.Refresh()
util.Panic(err).When(err != nil)
<-exit
c.Close()
}