前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >聊聊dubbo-go-proxy的AccessLogFilter

聊聊dubbo-go-proxy的AccessLogFilter

作者头像
code4it
发布2021-02-08 22:14:08
3100
发布2021-02-08 22:14:08
举报
文章被收录于专栏:码匠的流水账码匠的流水账

本文主要研究一下dubbo-go-proxy的AccessLogFilter

AccessLogFilter

dubbo-go-proxy/pkg/filter/accesslog/access_log.go

代码语言:javascript
复制
var accessLogWriter = &model.AccessLogWriter{AccessLogDataChan: make(chan model.AccessLogData, constant.LogDataBuffer)}

func init() {
    extension.SetFilterFunc(constant.AccessLogFilter, accessLog())
    accessLogWriter.Write()
}

// access log filter
func accessLog() context.FilterFunc {
    return func(c context.Context) {
        alc := config.GetBootstrap().StaticResources.AccessLogConfig
        if !alc.Enable {
            return
        }
        start := time.Now()
        c.Next()
        latency := time.Now().Sub(start)
        // build access_log message
        accessLogMsg := buildAccessLogMsg(c, latency)
        if len(accessLogMsg) > 0 {
            accessLogWriter.Writer(model.AccessLogData{AccessLogConfig: alc, AccessLogMsg: accessLogMsg})
        }
    }
}

init方法通过extension.SetFilterFunc注册了名为constant.AccessLogFilter的context.FilterFunc,然后执行accessLogWriter.Write();accessLog方法返回context.FilterFunc,该func通过buildAccessLogMsg构建accessLogMsg,然后执行accessLogWriter.Writer

buildAccessLogMsg

dubbo-go-proxy/pkg/filter/accesslog/access_log.go

代码语言:javascript
复制
func buildAccessLogMsg(c context.Context, cost time.Duration) string {
    req := c.(*http.HttpContext).Request
    valueStr := req.URL.Query().Encode()
    if len(valueStr) != 0 {
        valueStr = strings.ReplaceAll(valueStr, "&", ",")
    }

    builder := strings.Builder{}
    builder.WriteString("[")
    builder.WriteString(time.Now().Format(constant.MessageDateLayout))
    builder.WriteString("] ")
    builder.WriteString(req.RemoteAddr)
    builder.WriteString(" -> ")
    builder.WriteString(req.Host)
    builder.WriteString(" - ")
    if len(valueStr) > 0 {
        builder.WriteString("request params: [")
        builder.WriteString(valueStr)
        builder.WriteString("] ")
    }
    builder.WriteString("cost time [ ")
    builder.WriteString(strconv.Itoa(int(cost)) + " ]")
    err := c.(*http.HttpContext).Err
    if err != nil {
        builder.WriteString(fmt.Sprintf("invoke err [ %v", err))
        builder.WriteString("] ")
    }
    resp := c.(*http.HttpContext).TargetResp.Data
    rbs, err := getBytes(resp)
    if err != nil {
        builder.WriteString(fmt.Sprintf(" response can not convert to string"))
        builder.WriteString("] ")
    } else {
        builder.WriteString(fmt.Sprintf(" response [ %+v", string(rbs)))
        builder.WriteString("] ")
    }
    //builder.WriteString("\n")
    return builder.String()
}

buildAccessLogMsg方法使用strings.Builder构建accesslog,依次打印time、remoteAddr、host、request params、cost time、err、resp

AccessLogWriter

dubbo-go-proxy/pkg/model/log.go

代码语言:javascript
复制
// access log chan
type AccessLogWriter struct {
    AccessLogDataChan chan AccessLogData
}

// access log data
type AccessLogData struct {
    AccessLogMsg    string
    AccessLogConfig AccessLogConfig
}

// writer msg into chan
func (alw *AccessLogWriter) Writer(accessLogData AccessLogData) {
    select {
    case alw.AccessLogDataChan <- accessLogData:
        return
    default:
        logger.Warn("the channel is full and the access logIntoChannel data will be dropped")
        return
    }
}

AccessLogWriter定义了AccessLogDataChan属性,它是一个AccessLogData类型的channel;Writer方法往AccessLogDataChan写入accessLogData

Write

dubbo-go-proxy/pkg/model/log.go

代码语言:javascript
复制
// write log into out put path
func (alw *AccessLogWriter) Write() {
    go func() {
        for accessLogData := range alw.AccessLogDataChan {
            alw.writeLogToFile(accessLogData)
        }
    }()
}

// write log to file or console
func (alw *AccessLogWriter) writeLogToFile(ald AccessLogData) {
    alc := ald.AccessLogConfig
    alm := ald.AccessLogMsg
    if len(alc.OutPutPath) == 0 || alc.OutPutPath == constant.Console {
        logger.Info(alm)
        return
    }
    _ = WriteToFile(alm, alc.OutPutPath)
}

Write启动异步线程遍历AccessLogDataChan,执行writeLogToFile

WriteToFile

dubbo-go-proxy/pkg/model/log.go

代码语言:javascript
复制
// write message to access log file
func WriteToFile(accessLogMsg string, filePath string) error {
    pd := filepath.Dir(filePath)
    if _, err := os.Stat(pd); err != nil {
        if os.IsExist(err) {
            logger.Warnf("can not open log dir: %s, %v", filePath, err)
        }
        err = os.MkdirAll(pd, os.ModePerm)
        if err != nil {
            logger.Warnf("can not create log dir: %s, %v", filePath, err)
            return err
        }
    }
    logFile, err := os.OpenFile(filePath, os.O_CREATE|os.O_APPEND|os.O_RDWR, constant.LogFileMode)
    if err != nil {
        logger.Warnf("can not open the access log file: %s, %v", filePath, err)
        return err
    }
    now := time.Now().Format(constant.FileDateFormat)
    fileInfo, err := logFile.Stat()
    if err != nil {
        logger.Warnf("can not get the info of access log file: %s, %v", filePath, err)
        return err
    }
    last := fileInfo.ModTime().Format(constant.FileDateFormat)

    // this is confused.
    // for example, if the last = '2020-03-04'
    // and today is '2020-03-05'
    // we will create one new file to log access data
    // By this way, we can split the access log based on days.
    if now != last {
        err = os.Rename(fileInfo.Name(), fileInfo.Name()+"."+now)
        if err != nil {
            logger.Warnf("can not rename access log file: %s, %v", fileInfo.Name(), err)
            return err
        }
        logFile, err = os.OpenFile(fileInfo.Name(), os.O_CREATE|os.O_APPEND|os.O_RDWR, constant.LogFileMode)
        if err != nil {
            logger.Warnf("can not open access log file: %s, %v", fileInfo.Name(), err)
            return err
        }
    }
    _, err = logFile.WriteString(accessLogMsg + "\n")
    if err != nil {
        logger.Warnf("can not write to access log file: %s, v%", fileInfo.Name(), err)
        return err
    }
    return nil
}

WriteToFile方法会按日期对log进行分割,最后通过logFile.WriteString写入日志

小结

dubbo-go-proxy的AccessLogFilter通过extension.SetFilterFunc注册了名为constant.AccessLogFilter的context.FilterFunc,该func通过buildAccessLogMsg构建accessLogMsg,然后往AccessLogDataChan写入accessLogData;然后执行accessLogWriter.Write()启动协程遍历AccessLogDataChan,执行writeLogToFile。

doc

  • dubbo-go-proxy
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-02-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码匠的流水账 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • AccessLogFilter
  • buildAccessLogMsg
  • AccessLogWriter
  • Write
  • WriteToFile
  • 小结
  • doc
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档