一站式 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()
}

猜你喜欢

转载自www.oschina.net/news/208145/go-spring-1-1-1-released