zoukankan      html  css  js  c++  java
  • Go jaegerde 应用【logger+gorm+grpc+http】

    在以前的Go语言jaeger和opentracing 有用来做日志,但是很多时候我们希望数据库的操作也可以记录下来,程序一般作为http或者grpc 服务, 所以grpc和http也是需要用中间件来实现的。首先看程序的目录, 只是一个简单的demo:

     因为程序最后会部署到k8s上,计划采用docker来收集,灌到elk或者graylog,所以这里直接输出,程序设计采用切换数据库 实现简单的saas。

    来看看主要的几个文件

    logger.go

    package logger
    
    import (
        "context"
        "fmt"
        "io"
        "runtime"
        "strings"
        "time"
    
        "github.com/opentracing/opentracing-go"
        "github.com/uber/jaeger-client-go"
        "github.com/uber/jaeger-client-go/config"
        "github.com/uber/jaeger-client-go/log"
        "github.com/uber/jaeger-lib/metrics"
        "go.uber.org/zap"
        "go.uber.org/zap/zapcore"
    )
    
    var (
        logTimeFormat = "2006-01-02T15:04:05.000+08:00"
        zapLogger     *zap.Logger
    )
    
    //配置默认初始化
    func init() {
        c := zap.NewProductionConfig()
        c.EncoderConfig.LevelKey = ""
        c.EncoderConfig.CallerKey = ""
        c.EncoderConfig.MessageKey = "logModel"
        c.EncoderConfig.TimeKey = ""
        c.Level = zap.NewAtomicLevelAt(zap.DebugLevel)
        zapLogger, _ = c.Build()
    }
    
    //初始化 Jaeger client
    func NewJaegerTracer(serviceName string, agentHost string) (tracer opentracing.Tracer, closer io.Closer, err error) {
        cfg := config.Configuration{
            ServiceName: serviceName,
            Sampler: &config.SamplerConfig{
                Type:  jaeger.SamplerTypeRateLimiting,
                Param: 10,
            },
            Reporter: &config.ReporterConfig{
                LogSpans:            false,
                BufferFlushInterval: 1 * time.Second,
                LocalAgentHostPort:  agentHost,
            },
        }
    
        jLogger := log.StdLogger
        jMetricsFactory := metrics.NullFactory
    
        tracer, closer, err = cfg.NewTracer(config.Logger(jLogger), config.Metrics(jMetricsFactory))
        if err == nil {
            opentracing.SetGlobalTracer(tracer)
        }
    
        return tracer, closer, err
    }
    
    func Error(ctx context.Context, format interface{}, args ...interface{}) {
        msg := ""
        if e, ok := format.(error); ok {
            msg = fmt.Sprintf(e.Error(), args...)
        } else if e, ok := format.(string); ok {
            msg = fmt.Sprintf(e, args...)
        }
    
        jsonStdOut(ctx, zap.ErrorLevel, msg)
    }
    
    func Warn(ctx context.Context, format string, args ...interface{}) {
        jsonStdOut(ctx, zap.WarnLevel, fmt.Sprintf(format, args...))
    }
    
    func Info(ctx context.Context, format string, args ...interface{}) {
        jsonStdOut(ctx, zap.InfoLevel, fmt.Sprintf(format, args...))
    }
    
    func Debug(ctx context.Context, format string, args ...interface{}) {
        jsonStdOut(ctx, zap.DebugLevel, fmt.Sprintf(format, args...))
    }
    
    //本地打印 Json
    func jsonStdOut(ctx context.Context, level zapcore.Level, msg string) {
        traceId, spanId := getTraceId(ctx)
        if ce := zapLogger.Check(level, "zap"); ce != nil {
            ce.Write(
                zap.Any("message", JsonLogger{
                    LogTime:  time.Now().Format(logTimeFormat),
                    Level:    level,
                    Content:  msg,
                    CallPath: getCallPath(),
                    TraceId:  traceId,
                    SpanId:   spanId,
                }),
            )
        }
    }
    
    type JsonLogger struct {
        TraceId  string        `json:"traceId"`
        SpanId   uint64        `json:"spanId"`
        Content  interface{}   `json:"content"`
        CallPath interface{}   `json:"callPath"`
        LogTime  string        `json:"logDate"` //日志时间
        Level    zapcore.Level `json:"level"`   //日志级别
    }
    
    func getTraceId(ctx context.Context) (string, uint64) {
        span := opentracing.SpanFromContext(ctx)
        if span == nil {
            return "", 0
        }
    
        if sc, ok := span.Context().(jaeger.SpanContext); ok {
    
            return fmt.Sprintf("%v", sc.TraceID()), uint64(sc.SpanID())
        }
        return "", 0
    }
    
    func getCallPath() string {
        _, file, lineno, ok := runtime.Caller(2)
        if ok {
            return strings.Replace(fmt.Sprintf("%s:%d", stringTrim(file, ""), lineno), "%2e", ".", -1)
        }
        return ""
    }
    
    func stringTrim(s, cut string) string {
        ss := strings.SplitN(s, cut, 2)
        if len(ss) == 1 {
            return ss[0]
        }
        return ss[1]
    }

    db.go

    package db
    
    import (
        "context"
        "database/sql/driver"
        "fmt"
        "net/url"
        "reflect"
        "regexp"
        "strings"
        "tracedemo/logger"
        "unicode"
    
        "github.com/jinzhu/gorm"
        "github.com/pkg/errors"
    
        "sync"
    
        "time"
    
        _ "github.com/go-sql-driver/mysql"
        "github.com/opentracing/opentracing-go"
    )
    
    // DB连接配置信息
    type Config struct {
        DbHost string
        DbPort int
        DbUser string
        DbPass string
        DbName string
        Debug  bool
    }
    
    // 连接的数据库类型
    const (
        dbMaster         string = "master"
        jaegerContextKey        = "jeager:context"
        callbackPrefix          = "jeager"
        startTime               = "start:time"
    )
    
    func init() {
        connMap = make(map[string]*gorm.DB)
    }
    
    var (
        connMap  map[string]*gorm.DB
        connLock sync.RWMutex
    )
    
    // 初始化DB
    func InitDb(siteCode string, cfg *Config) (err error) {
        url := url.Values{}
        url.Add("parseTime", "True")
        url.Add("loc", "Local")
        url.Add("charset", "utf8mb4")
        url.Add("collation", "utf8mb4_unicode_ci")
        url.Add("readTimeout", "0s")
        url.Add("writeTimeout", "0s")
        url.Add("timeout", "0s")
    
        dsn := fmt.Sprintf("%s:%s@tcp(%s:%v)/%s?%s", cfg.DbUser, cfg.DbPass, cfg.DbHost, cfg.DbPort, cfg.DbName, url.Encode())
    
        conn, err := gorm.Open("mysql", dsn)
        if err != nil {
            return errors.Wrap(err, "fail to connect db")
        }
    
        //新增gorm插件
        if cfg.Debug == true {
            registerCallbacks(conn)
        }
        //打印日志
        //conn.LogMode(true)
    
        conn.DB().SetMaxIdleConns(30)
        conn.DB().SetMaxOpenConns(200)
        conn.DB().SetConnMaxLifetime(60 * time.Second)
    
        if err := conn.DB().Ping(); err != nil {
            return errors.Wrap(err, "fail to ping db")
        }
    
        connLock.Lock()
        dbName := fmt.Sprintf("%s-%s", siteCode, dbMaster)
        connMap[dbName] = conn
        connLock.Unlock()
    
        go mysqlHeart(conn)
    
        return nil
    }
    
    func GetMaster(ctx context.Context) *gorm.DB {
        connLock.RLock()
        defer connLock.RUnlock()
    
        siteCode := fmt.Sprintf("%v", ctx.Value("SiteCode"))
        if strings.Contains(siteCode, "nil") {
            panic(errors.New("当前上下文没有找到DB"))
        }
    
        dbName := fmt.Sprintf("%s-%s", siteCode, dbMaster)
    
        ctx = context.WithValue(ctx, "DbName", dbName)
    
        db := connMap[dbName]
        if db == nil {
            panic(errors.New(fmt.Sprintf("当前上下文没有找到DB:%s", dbName)))
        }
    
        return db.Set(jaegerContextKey, ctx)
    }
    
    func mysqlHeart(conn *gorm.DB) {
        for {
            if conn != nil {
                err := conn.DB().Ping()
                if err != nil {
                    fmt.Println(fmt.Sprintf("mysqlHeart has err:%v", err))
                }
            }
    
            time.Sleep(3 * time.Minute)
        }
    }
    
    func registerCallbacks(db *gorm.DB) {
        driverName := db.Dialect().GetName()
        switch driverName {
        case "postgres":
            driverName = "postgresql"
        }
        spanTypePrefix := fmt.Sprintf("gorm.db.%s.", driverName)
        querySpanType := spanTypePrefix + "query"
        execSpanType := spanTypePrefix + "exec"
    
        type params struct {
            spanType  string
            processor func() *gorm.CallbackProcessor
        }
        callbacks := map[string]params{
            "gorm:create": {
                spanType:  execSpanType,
                processor: func() *gorm.CallbackProcessor { return db.Callback().Create() },
            },
            "gorm:delete": {
                spanType:  execSpanType,
                processor: func() *gorm.CallbackProcessor { return db.Callback().Delete() },
            },
            "gorm:query": {
                spanType:  querySpanType,
                processor: func() *gorm.CallbackProcessor { return db.Callback().Query() },
            },
            "gorm:update": {
                spanType:  execSpanType,
                processor: func() *gorm.CallbackProcessor { return db.Callback().Update() },
            },
            "gorm:row_query": {
                spanType:  querySpanType,
                processor: func() *gorm.CallbackProcessor { return db.Callback().RowQuery() },
            },
        }
        for name, params := range callbacks {
            params.processor().Before(name).Register(
                fmt.Sprintf("%s:before:%s", callbackPrefix, name),
                newBeforeCallback(params.spanType),
            )
            params.processor().After(name).Register(
                fmt.Sprintf("%s:after:%s", callbackPrefix, name),
                newAfterCallback(),
            )
        }
    }
    
    func newBeforeCallback(spanType string) func(*gorm.Scope) {
        return func(scope *gorm.Scope) {
            ctx, ok := scopeContext(scope)
            if !ok {
                return
            }
            //新增链路追踪
            span, ctx := opentracing.StartSpanFromContext(ctx, spanType)
            if span.Tracer() == nil {
                span.Finish()
                ctx = nil
            }
            scope.Set(jaegerContextKey, ctx)
            scope.Set(startTime, time.Now().UnixNano())
        }
    }
    
    func newAfterCallback() func(*gorm.Scope) {
        return func(scope *gorm.Scope) {
            ctx, ok := scopeContext(scope)
            if !ok {
                return
            }
            span := opentracing.SpanFromContext(ctx)
            if span == nil {
                return
            }
            defer span.Finish()
    
            duration := int64(0)
            if t, ok := scopeStartTime(scope); ok {
                duration = (time.Now().UnixNano() - t) / 1e6
            }
    
            logger.Debug(ctx, "[gorm] [%vms] [RowsReturned(%v)] %v  ", duration, scope.DB().RowsAffected, gormSQL(scope.SQL, scope.SQLVars))
    
            for _, err := range scope.DB().GetErrors() {
                if gorm.IsRecordNotFoundError(err) || err == errors.New("sql: no rows in result set") {
                    continue
                }
                //打印错误日志
                logger.Error(ctx, "%v", err.Error())
            }
            //span.LogFields(traceLog.String("sql", scope.SQL))
        }
    }
    
    func scopeContext(scope *gorm.Scope) (context.Context, bool) {
        value, ok := scope.Get(jaegerContextKey)
        if !ok {
            return nil, false
        }
        ctx, _ := value.(context.Context)
        return ctx, ctx != nil
    }
    
    func scopeStartTime(scope *gorm.Scope) (int64, bool) {
        value, ok := scope.Get(startTime)
        if !ok {
            return 0, false
        }
        t, ok := value.(int64)
        return t, ok
    }
    
    /*===============Log=======================================*/
    var (
        sqlRegexp                = regexp.MustCompile(`?`)
        numericPlaceHolderRegexp = regexp.MustCompile(`$d+`)
    )
    
    func gormSQL(inputSql interface{}, value interface{}) string {
        var sql string
        var formattedValues []string
        for _, value := range value.([]interface{}) {
            indirectValue := reflect.Indirect(reflect.ValueOf(value))
            if indirectValue.IsValid() {
                value = indirectValue.Interface()
                if t, ok := value.(time.Time); ok {
                    if t.IsZero() {
                        formattedValues = append(formattedValues, fmt.Sprintf("'%v'", "0000-00-00 00:00:00"))
                    } else {
                        formattedValues = append(formattedValues, fmt.Sprintf("'%v'", t.Format("2006-01-02 15:04:05")))
                    }
                } else if b, ok := value.([]byte); ok {
                    if str := string(b); isPrintable(str) {
                        formattedValues = append(formattedValues, fmt.Sprintf("'%v'", str))
                    } else {
                        formattedValues = append(formattedValues, "'<binary>'")
                    }
                } else if r, ok := value.(driver.Valuer); ok {
                    if value, err := r.Value(); err == nil && value != nil {
                        formattedValues = append(formattedValues, fmt.Sprintf("'%v'", value))
                    } else {
                        formattedValues = append(formattedValues, "NULL")
                    }
                } else {
                    switch value.(type) {
                    case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, bool:
                        formattedValues = append(formattedValues, fmt.Sprintf("%v", value))
                    default:
                        formattedValues = append(formattedValues, fmt.Sprintf("'%v'", value))
                    }
                }
            } else {
                formattedValues = append(formattedValues, "NULL")
            }
        }
    
        if formattedValues == nil || len(formattedValues) < 1 {
            return sql
        }
    
        // differentiate between $n placeholders or else treat like ?
        if numericPlaceHolderRegexp.MatchString(inputSql.(string)) {
            sql = inputSql.(string)
            for index, value := range formattedValues {
                placeholder := fmt.Sprintf(`$%d([^d]|$)`, index+1)
                sql = regexp.MustCompile(placeholder).ReplaceAllString(sql, value+"$1")
            }
        } else {
            formattedValuesLength := len(formattedValues)
            for index, value := range sqlRegexp.Split(inputSql.(string), -1) {
                sql += value
                if index < formattedValuesLength {
                    sql += formattedValues[index]
                }
            }
        }
    
        return sql
    }
    
    func isPrintable(s string) bool {
        for _, r := range s {
            if !unicode.IsPrint(r) {
                return false
            }
        }
        return true
    }

    server.go

    package apiserver
    
    import (
        contextV2 "context"
        "fmt"
        "runtime/debug"
        "tracedemo/apiserver/userinfo"
        "tracedemo/logger"
    
        "github.com/kataras/iris/v12"
        "github.com/kataras/iris/v12/context"
        "github.com/opentracing/opentracing-go"
    )
    
    func StartApiServerr() {
        addr := ":8080"
    
        app := iris.New()
        app.Use(openTracing())
        app.Use(withSiteCode())
        app.Use(withRecover())
    
        app.Get("/", func(c context.Context) {
            c.WriteString("pong")
        })
    
        initIris(app)
        logger.Info(contextV2.Background(),  "[apiServer]开始监听%s,", addr)
    
        err := app.Run(iris.Addr(addr), iris.WithoutInterruptHandler)
        if err != nil {
            logger.Error(contextV2.Background(), "[apiServer]开始监听%s 错误%v,", addr,err)
        }
    }
    
    func initIris(app *iris.Application) {
       api:= userinfo.ApiServer{}
        userGroup := app.Party("/user")
        {
            userGroup.Get("/test",api.TestUserInfo)
            userGroup.Get("/rpc",api.TestRpc)
        }
    }
    
    func openTracing() context.Handler {
        return func(c iris.Context) {
            span := opentracing.GlobalTracer().StartSpan("apiServer")
            c.ResetRequest(c.Request().WithContext(opentracing.ContextWithSpan(c.Request().Context(), span)))
            logger.Info(c.Request().Context(), "Api请求地址%v", c.Request().URL)
            c.Next()
        }
    }
    
    func withSiteCode() context.Handler {
        return func(c iris.Context) {
            siteCode := c.GetHeader("SiteCode")
            if len(siteCode) < 1 {
                siteCode = "001"
            }
            ctx := contextV2.WithValue(c.Request().Context(), "SiteCode", siteCode)
            c.ResetRequest(c.Request().WithContext(ctx))
    
            c.Next()
        }
    }
    
    func withRecover() context.Handler {
        return func(c iris.Context) {
            defer func() {
                if e := recover(); e != nil {
                    stack := debug.Stack()
                    logger.Error(c.Request().Context(), fmt.Sprintf("Api has err:%v, stack:%v", e, string(stack)))
                }
            }()
    
            c.Next()
        }
    }

    grpc的中间件middleware.go

    package middleware
    
    import (
        "context"
        "encoding/json"
        "fmt"
        "github.com/opentracing/opentracing-go"
        "github.com/opentracing/opentracing-go/ext"
        "google.golang.org/grpc"
        "google.golang.org/grpc/metadata"
        "runtime/debug"
        "strings"
        "time"
        "tracedemo/logger"
    )
    
    type MDCarrier struct {
        metadata.MD
    }
    
    func (m MDCarrier) ForeachKey(handler func(key, val string) error) error {
        for k, strs := range m.MD {
            for _, v := range strs {
                if err := handler(k, v); err != nil {
                    return err
                }
            }
        }
        return nil
    }
    
    func (m MDCarrier) Set(key, val string) {
        m.MD[key] = append(m.MD[key], val)
    }
    
    // ClientInterceptor 客户端拦截器
    func ClientTracing(tracer opentracing.Tracer) grpc.UnaryClientInterceptor {
        return func(ctx context.Context, method string, request, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
            //一个RPC调用的服务端的span,和RPC服务客户端的span构成ChildOf关系
            var parentCtx opentracing.SpanContext
            parentSpan := opentracing.SpanFromContext(ctx)
            if parentSpan != nil {
                parentCtx = parentSpan.Context()
            }
            span := tracer.StartSpan(
                method,
                opentracing.ChildOf(parentCtx),
                opentracing.Tag{Key: string(ext.Component), Value: "gRPC Client"},
                ext.SpanKindRPCClient,
            )
    
            defer span.Finish()
            md, ok := metadata.FromOutgoingContext(ctx)
            if !ok {
                md = metadata.New(nil)
            } else {
                md = md.Copy()
            }
    
            err := tracer.Inject(
                span.Context(),
                opentracing.TextMap,
                MDCarrier{md}, // 自定义 carrier
            )
    
            if err != nil {
                logger.Error(ctx, "ClientTracing inject span error :%v", err.Error())
            }
    
            ///SiteCode
            siteCode := fmt.Sprintf("%v", ctx.Value("SiteCode"))
            if len(siteCode) < 1 || strings.Contains(siteCode, "nil") {
                siteCode = "001"
            }
            md.Set("SiteCode", siteCode)
            //
            newCtx := metadata.NewOutgoingContext(ctx, md)
            err = invoker(newCtx, method, request, reply, cc, opts...)
    
            if err != nil {
                logger.Error(ctx, "ClientTracing call error : %v", err.Error())
            }
            return err
        }
    }
    
    func ClientSiteCode() grpc.UnaryClientInterceptor {
        return func(ctx context.Context, method string, request, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
            md, ok := metadata.FromOutgoingContext(ctx)
            if !ok {
                md = metadata.New(nil)
            } else {
                md = md.Copy()
            }
    
            ///SiteCode
            siteCode := fmt.Sprintf("%v", ctx.Value("SiteCode"))
            if len(siteCode) < 1 || strings.Contains(siteCode, "nil") {
                siteCode = "001"
            }
            md.Set("SiteCode", siteCode)
    
            return invoker(ctx, method, request, reply, cc, opts...)
        }
    }
    
    func ClientTimeLog() grpc.UnaryClientInterceptor {
        return func(ctx context.Context, method string, request, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
            defer func() {
                if e := recover(); e != nil {
                    stack := debug.Stack()
                    logger.Error(ctx, fmt.Sprintf("grpc-client has err:%v, stack:%v", e, string(stack)))
                }
            }()
    
            startTime := time.Now().UnixNano()
            err := invoker(ctx, method, request, reply, cc, opts...)
            duration := (time.Now().UnixNano() - startTime) / 1e6
            requestByte, _ := json.Marshal(request)
            responseByte, _ := json.Marshal(reply)
            logger.Info(ctx, fmt.Sprintf("grpc-client:方法名:%v,耗时:%vms,请求数据:%v,返回数据:%v", method, duration, string(requestByte), string(responseByte)))
            if err != nil {
                logger.Error(ctx, fmt.Sprintf("grpc-client:方法名:%v,耗时:%vms,请求数据:%v,返回错误:%v", method, duration, string(requestByte), err))
            }
    
            return err
        }
    }
    
    // ServerInterceptor Server 端的拦截器
    func ServerTracing(tracer opentracing.Tracer) grpc.UnaryServerInterceptor {
        return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
            md, ok := metadata.FromIncomingContext(ctx)
            if !ok {
                md = metadata.New(nil)
            }
    
            spanContext, err := tracer.Extract(
                opentracing.TextMap,
                MDCarrier{md},
            )
    
            if err != nil && err != opentracing.ErrSpanContextNotFound {
                logger.Error(ctx, "ServerInterceptor extract from metadata err: %v", err)
            } else {
                span := tracer.StartSpan(
                    info.FullMethod,
                    ext.RPCServerOption(spanContext),
                    opentracing.Tag{Key: string(ext.Component), Value: "(gRPC Server)"},
                    ext.SpanKindRPCServer,
                )
                defer span.Finish()
    
                ctx = opentracing.ContextWithSpan(ctx, span)
            }
    
            return handler(ctx, req)
        }
    
    }
    
    func ServerSiteCode() grpc.UnaryServerInterceptor {
        return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (_ interface{}, err error) {
            //读取siteCode
            incomingContext, ok := metadata.FromIncomingContext(ctx)
            siteCode := ""
            if ok {
                siteCodeArr := incomingContext.Get("SiteCode")
                if siteCodeArr != nil && len(siteCodeArr) > 0 {
                    siteCode = siteCodeArr[0]
                }
            } else {
                incomingContext = metadata.New(nil)
            }
    
            if len(siteCode) < 1 {
                siteCode = "001"
            }
    
            //设置siteCode到上下文
            c2 := context.WithValue(ctx, "001", siteCode)
    
            return handler(c2, req)
        }
    }
    
    func ServerTimeLog() grpc.UnaryServerInterceptor {
        return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (_ interface{}, err error) {
            defer func() {
                if e := recover(); e != nil {
                    stack := debug.Stack()
                    logger.Error(ctx, fmt.Sprintf("grpc-client has err:%v, stack:%v", e, string(stack)))
                }
            }()
    
            startTime := time.Now().UnixNano()
            ret, err := handler(ctx, req)
            duration := (time.Now().UnixNano() - startTime) / 1e6
            requestByte, _ := json.Marshal(req)
            responseStr := ""
            if err == nil {
                responseByte, _ := json.Marshal(ret)
                responseStr = string(responseByte)
            }
    
            logger.Info(ctx, fmt.Sprintf("grpc-server:方法名:%v,耗时:%vms,请求数据:%v,返回数据:%v", info.FullMethod, duration, string(requestByte), responseStr))
            if err != nil {
                logger.Error(ctx, fmt.Sprintf("grpc-server:方法名:%v,耗时:%vms,请求数据:%v,返回错误:%v", info.FullMethod, duration, string(requestByte), err))
            }
    
            return ret, err
        }
    }

    grpc的调用userinfo.go

    package userinfo
    
    import (
        "fmt"
        grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
        "github.com/opentracing/opentracing-go"
        "tracedemo/middleware"
        pb "tracedemo/protos"
        "tracedemo/service"
    
        "google.golang.org/grpc"
    
        "github.com/kataras/iris/v12"
    )
    
    type ApiServer struct{}
    
    func (t *ApiServer) TestUserInfo(ctx iris.Context) {
        err := service.TestUserInfo(ctx.Request().Context())
        if err != nil {
            ctx.WriteString("err:" + err.Error())
        } else {
            ctx.WriteString("ok")
        }
    }
    
    func (t *ApiServer) TestRpc(ctx iris.Context) {
        addr := "localhost:9090"
        opts := []grpc.DialOption{
            grpc.WithInsecure(),
            grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(
                middleware.ClientTracing(opentracing.GlobalTracer()),
                middleware.ClientSiteCode(),
                middleware.ClientTimeLog(),
                )),
        }
    
        conn, err := grpc.Dial(addr, opts...)
        if err != nil {
            fmt.Println(err)
        }
    
        defer conn.Close()
    
        client := pb.NewGreeterClient(conn)
        request := &pb.HelloRequest{Name: "gavin"}
        response, err := client.SayHello(ctx.Request().Context(), request)
        if err != nil {
            ctx.WriteString("err:" + err.Error())
        } else {
            ctx.WriteString("rpc:" + response.Message)
        }
    }

    整个main.go

    package main
    
    import (
        "fmt"
        "os"
        "tracedemo/apiserver"
        "tracedemo/db"
        "tracedemo/grpcserver"
        "tracedemo/logger"
    )
    
    func main() {
        //init log
        jaegerHost := "192.168.100.30:6831"
        serverName, _ := os.Hostname()
        serverName = "trace-" + serverName
        _, _, err := logger.NewJaegerTracer(serverName, jaegerHost)
        if err != nil {
            fmt.Println(fmt.Sprintf("初始化JaegerTracer错误%v", err))
        }
    
        //初始化DB
        dbConfig := db.Config{
            DbHost: "192.168.100.30",
            DbPort: 3306,
            DbUser: "root",
            DbPass: "root",
            DbName: "demo",
            Debug:  true,
        }
        err = db.InitDb("001", &dbConfig)
        if err != nil {
            fmt.Println(fmt.Sprintf("初始化Db错误%v", err))
        }
    
        //启动api
        go apiserver.StartApiServerr()
    
        //启动GRPC
        go grpcserver.StartGrpcServer()
    
        select {}
    }

    service里面的实现非常简单

    package service
    
    import (
        "context"
        "tracedemo/db"
        "tracedemo/logger"
        "tracedemo/model"
    )
    
    func TestUserInfo(ctx context.Context) error {
        info := model.UserInfo{
            Name:  "gavin",
            Hobby: "demo",
        }
    
        gormDb := db.GetMaster(ctx)
    
        err := info.Create(gormDb)
        if err != nil {
            logger.Error(ctx, "Create err %v", err)
        } else {
            logger.Info(ctx, "create Success!")
        }
    
        //update
        info.Name = "test"
        err = info.Update(gormDb)
        if err != nil {
            logger.Warn(ctx, "Update err %v", err)
        } else {
            logger.Debug(ctx, "Update Success!")
        }
    
        //get
        infos, err := model.GetAllUser(ctx)
        if err != nil {
            logger.Warn(ctx, "Get err %v", err)
        } else {
            logger.Debug(ctx, "Get Success %v", len(infos))
        }
    
        //delete
        err = info.Delete(gormDb)
        if err != nil {
            logger.Error(ctx, "Delete err %v", err)
        } else {
            logger.Info(ctx, "Delete Success!")
        }
    
        return nil
    }

    运行的日志如下:

    Now listening on: http://localhost:8080
    Application started. Press CTRL+C to shut down.
    {"logModel":"zap","message":{"traceId":"1055f374f34f44da","spanId":1177114561251067098,"content":"Api请求地址/user/rpc","callPath":":/Project/GoProject/tracedemo/logger/logger.go:78","logDate":"2021-05-13T14:22:48.236+08:00",
    "level":"info"}}
    ?[36m[INFO]?[0m 2021/05/13 14:22 200 9.8638496s ::1 GET /user/rpc
    {"logModel":"zap","message":{"traceId":"1055f374f34f44da","spanId":3696752721395975441,"content":"GRPC Send: Resuest By:gavin Response By :172.17.116.1","callPath":":/Project/GoProject/tracedemo/logger/logger.go:82","logDate"
    :"2021-05-13T14:22:58.098+08:00","level":"debug"}}
    {"logModel":"zap","message":{"traceId":"4277d25d011abbc3","spanId":4789528025624132547,"content":"Api请求地址/user/test","callPath":":/Project/GoProject/tracedemo/logger/logger.go:78","logDate":"2021-05-13T14:24:02.817+08:00"
    ,"level":"info"}}
    {"logModel":"zap","message":{"traceId":"4277d25d011abbc3","spanId":6843463915167349993,"content":"[gorm] [4ms] [RowsReturned(1)] INSERT INTO `userInfo` (`name`,`hobby`) VALUES ('gavin','demo')  ","callPath":":/Project/GoProje
    ct/tracedemo/logger/logger.go:82","logDate":"2021-05-13T14:24:08.620+08:00","level":"debug"}}
    ?[36m[INFO]?[0m 2021/05/13 14:24 200 5.8228809s ::1 GET /user/test
    {"logModel":"zap","message":{"traceId":"4277d25d011abbc3","spanId":4789528025624132547,"content":"create Success!","callPath":":/Project/GoProject/tracedemo/logger/logger.go:78","logDate":"2021-05-13T14:24:08.623+08:00","leve
    l":"info"}}
    {"logModel":"zap","message":{"traceId":"4277d25d011abbc3","spanId":3084142635960672264,"content":"[gorm] [7ms] [RowsReturned(1)] UPDATE `userInfo` SET `name` = 'test', `hobby` = 'demo'  WHERE `userInfo`.`id` = 5  ","callPath"
    :":/Project/GoProject/tracedemo/logger/logger.go:82","logDate":"2021-05-13T14:24:08.631+08:00","level":"debug"}}
    {"logModel":"zap","message":{"traceId":"4277d25d011abbc3","spanId":4789528025624132547,"content":"Update Success!","callPath":":/Project/GoProject/tracedemo/logger/logger.go:82","logDate":"2021-05-13T14:24:08.634+08:00","leve
    l":"debug"}}
    {"logModel":"zap","message":{"traceId":"4277d25d011abbc3","spanId":4286223975661076968,"content":"[gorm] [1ms] [RowsReturned(1)]   ","callPath":":/Project/GoProject/tracedemo/logger/logger.go:82","logDate":"2021-05-13T14:24:0
    8.635+08:00","level":"debug"}}
    {"logModel":"zap","message":{"traceId":"4277d25d011abbc3","spanId":4789528025624132547,"content":"Get Success 1","callPath":":/Project/GoProject/tracedemo/logger/logger.go:82","logDate":"2021-05-13T14:24:08.635+08:00","level"
    :"debug"}}
    {"logModel":"zap","message":{"traceId":"4277d25d011abbc3","spanId":349092158832079207,"content":"[gorm] [1ms] [RowsReturned(1)] DELETE FROM `userInfo`  WHERE `userInfo`.`id` = 5  ","callPath":":/Project/GoProject/tracedemo/lo
    gger/logger.go:82","logDate":"2021-05-13T14:24:08.637+08:00","level":"debug"}}
    {"logModel":"zap","message":{"traceId":"4277d25d011abbc3","spanId":4789528025624132547,"content":"Delete Success!","callPath":":/Project/GoProject/tracedemo/logger/logger.go:78","logDate":"2021-05-13T14:24:08.639+08:00","leve
    l":"info"}}

    下载 地址:https://github.com/dz45693/gotrace.git

    windows技术爱好者
  • 相关阅读:
    建造者模式
    设计模式的思考
    与公司开票接口对接的设计
    读EntityFramework.DynamicFilters源码_心得_设计思想_04
    读EntityFramework.DynamicFilters源码_心得_单元测试03
    读EntityFramework.DynamicFilters源码_心得_示例演示02
    带你看懂Dictionary的内部实现
    Working With Taxonomy Field in CSOM
    SharePoint 2013 REST 以及 OData 基础
    SharePoint API如何处理时区问题
  • 原文地址:https://www.cnblogs.com/majiang/p/14766162.html
Copyright © 2011-2022 走看看