一站式 Go 开发框架 Go-Spring 正式发布 v1.1.1 版本
在经历了一年半的折腾后,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() }