Gin源码阅读(二)

一个躺平程序员的回光返照,内容大部分是有道翻译小部分是百度内容,如果有错误请在评论区提出,马上改

填昨天留的坑

LoggerWithConfig


// LoggerWithConfig instance a Logger middleware with config.
// LoggerWithConfig实例是一个带有config的Logger中间件。
/**
// LoggerConfig defines the config for Logger middleware.
// LoggerConfig定义了Logger中间件的配置。
type LoggerConfig struct {
	// Optional. Default value is gin.defaultLogFormatter
	Formatter LogFormatter

	// Output is a writer where logs are written.
	// Optional. Default value is gin.DefaultWriter.
	// Output是写入日志的写入器。
	//可选的。默认值为“gin.DefaultWriter”。
	Output io.Writer

	// SkipPaths is a url path array which logs are not written.
	// skippath是一个不写入日志的url路径数组。
	// Optional.
	SkipPaths []string
}
*/
func LoggerWithConfig(conf LoggerConfig) HandlerFunc {
	// formatter 赋默认值
	formatter := conf.Formatter
	if formatter == nil {
		formatter = defaultLogFormatter
	}
	// out 赋默认值
	out := conf.Output
	if out == nil {
		out = DefaultWriter
	}

	notlogged := conf.SkipPaths

	isTerm := true
	/**
		out.()没有看懂,断点看返回值是 w: *os.File | 0xc0000d8008   ok: true, 应该是文件读取之类的
		w.Fd()返回引用打开文件的Windows句柄。
		文件读取失败或者配置中TERM == dumb 或者 文件描述符不是terminal|cygwin|msys2, isTerm为false
	*/
	if w, ok := out.(*os.File); !ok || os.Getenv("TERM") == "dumb" ||
		(!isatty.IsTerminal(w.Fd()) && !isatty.IsCygwinTerminal(w.Fd())) {
		isTerm = false
	}

	var skip map[string]struct{}
	
	// 判断不写入日志的url路径,如果有数据则生成对应条数的map切片并赋初值(key)
	if length := len(notlogged); length > 0 {
		skip = make(map[string]struct{}, length)

		for _, path := range notlogged {
			skip[path] = struct{}{}
		}
	}
	// 返回一个上下文处理方法
	return func(c *Context) {
		// Start timer
		start := time.Now()
		path := c.Request.URL.Path
		raw := c.Request.URL.RawQuery

		// Process request
		// 继续执行,日志为后置中间件(钩子)
		c.Next()
		// 代码执行完后 继续日志处理
		// Log only when path is not being skipped
		// 判断数据是否在不记录日志map中,如果不在则进入
		if _, ok := skip[path]; !ok {
			// 日志初始化
			param := LogFormatterParams{
				Request: c.Request,
				// isTerm显示gin的输出描述符是否指向终端。
				isTerm:  isTerm,
				Keys:    c.Keys,
			}

			// Stop timer
			// 完成时间,完善日志参数
			param.TimeStamp = time.Now()
			param.Latency = param.TimeStamp.Sub(start)

			param.ClientIP = c.ClientIP()
			param.Method = c.Request.Method
			param.StatusCode = c.Writer.Status()
			param.ErrorMessage = c.Errors.ByType(ErrorTypePrivate).String()

			param.BodySize = c.Writer.Size()
			// 判断请求参数是否为空,如果不为空则拼接处理path
			if raw != "" {
				path = path + "?" + raw
			}

			param.Path = path
			
			//对其操作数使用默认格式的Fprint格式,并写入w。
			//当两个操作数都不是字符串时,在操作数之间添加空格。
			//返回写入的字节数和遇到的任何写入错误。
			fmt.Fprint(out, formatter(param))
		}
	}
}

RecoveryWithWriter

func RecoveryWithWriter(out io.Writer, recovery ...RecoveryFunc) HandlerFunc {
	if len(recovery) > 0 {
		return CustomRecoveryWithWriter(out, recovery[0])
	}
	return CustomRecoveryWithWriter(out, defaultHandleRecovery)
}

CustomRecoveryWithWriter

// CustomRecoveryWithWriter returns a middleware for a given writer that recovers from any panics and calls the provided handle func to handle it.
// CustomRecoveryWithWriter为给定的写入器返回一个中间件,该中间件可以从任何panics 中恢复,并调用提供的处理程序来处理它
func CustomRecoveryWithWriter(out io.Writer, handle RecoveryFunc) HandlerFunc {	
	// 定义日志
	var logger *log.Logger
	if out != nil {
		// 判断out不为空初始化logger
		logger = log.New(out, "\n\n\x1b[31m", log.LstdFlags)
	}
	// 返回匿名方法
	return func(c *Context) {
		// 定义延迟执行的匿名方法并且立即调用
		// 类似于final在最后执行
		defer func() {
			// 给err赋值并判断是否为空
			if err := recover(); err != nil {
				// Check for a broken connection, as it is not really a
				// condition that warrants a panic stack trace.
				//检查断开的连接,因为这并不是保证panic堆栈跟踪的真正条件。
				var brokenPipe bool
				if ne, ok := err.(*net.OpError); ok {
					if se, ok := ne.Err.(*os.SyscallError); ok {
						// 判断se的错误信息中是否报刊broken pipe 或者包含connection reset by peer 如果包含其中一个则brokenPipe为true(默认值是false)
						if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
							brokenPipe = true
						}
					}
				}
				// 判断logger不为空
				if logger != nil {
					// Stack返回一个格式良好的堆栈帧,跳过skip frames
					stack := stack(3)
					// 返回http请求数据,包含header,body
					httpRequest, _ := httputil.DumpRequest(c.Request, false)
					// 切割
					headers := strings.Split(string(httpRequest), "\r\n")
					for idx, header := range headers {
						
						current := strings.Split(header, ":")
						// 判断header中有Authorization则给对应header重新赋值
						if current[0] == "Authorization" {
							headers[idx] = current[0] + ": *"
						}
					}
					// 将header转成字符串格式
					headersToStr := strings.Join(headers, "\r\n")
					if brokenPipe {
						logger.Printf("%s\n%s%s", err, headersToStr, reset)
					} else if IsDebugging() {
						logger.Printf("[Recovery] %s panic recovered:\n%s\n%s\n%s%s",
							timeFormat(time.Now()), headersToStr, err, stack, reset)
					} else {
						logger.Printf("[Recovery] %s panic recovered:\n%s\n%s%s",
							timeFormat(time.Now()), err, stack, reset)
					}
				}
				if brokenPipe {
					// If the connection is dead, we can't write a status to it.
					// 如果连接已死,我们就不能向它写入状态。
					c.Error(err.(error)) // nolint: errcheck
					//中止阻止挂起的处理程序被调用。注意,这不会停止当前的处理程序。
					//假设你有一个授权中间件来验证当前请求是否被授权。
					//如果授权失败(例如:密码不匹配),调用Abort以确保剩余的处理程序
					c.Abort()
				} else {
					// 调用RecoveryFunc方法
					handle(c, err)
				}
			}
		}()
		// 继续执行
		c.Next()
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值