聊聊dubbo-go-proxy的AccessLogFilter

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

AccessLogFilter

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

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

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

// 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

// 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

// 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值