sequence

This paper mainly studies AccessLogFilter of Dubo-Go-proxy

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}) } } }Copy the code

Init method registers context.filterfunc named constant.accessLogFilter with extension.setFilterfunc and then executes accessLogwriter.write (); The accessLog method returns context.filterfunc, which builds accessLogMsg by buildAccessLogMsg and then executes 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() }Copy the code

The buildAccessLogMsg method builds the accesslog using strings.Builder and prints 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
	}
}
Copy the code

AccessLogWriter defines the AccessLogDataChan attribute, which is a channel of type AccessLogData. The Writer method writes accessLogData to AccessLogDataChan

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)
}
Copy the code

Write Starts the asynchronous thread to traverse the AccessLogDataChan, executing 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 }Copy the code

The WriteToFile method splits the log by date and finally writes it to logfile.writeString

summary

Dubo-go-proxy AccessLogFilter registers context.filterfunc named constant.AccessLogFilter with extension.setFilterfunc, The func builds accessLogMsg by buildAccessLogMsg, and then writes accessLogData to AccessLogDataChan; Accesslogwriter.write () then initiates the coroutine to traverse the AccessLogDataChan, executing writeLogToFile.

doc

  • dubbo-go-proxy