package
0.0.0-20211014111418-6d66e078db1f
Repository: https://github.com/samb233/easyblog.git
Documentation: pkg.go.dev

# README

log

原则

  1. 记录日志的目的是为了出现问题时能快速找到根因。
  2. 日志应该有一套易读的结构。

需求

  1. 应用内并不管log输出格式等,只管调用方法将信息输出,log的格式化由log包来做。
  2. log提供等级DebugInfoWarningError
  3. log对象可以指定前缀,并且一次指定,包内生效。

设计

  1. 看了logrus的源码后,发现logrus完全符合我的需求,并且依赖简单,所以决定使用logrus进行日志输出。
  2. 为了不强依赖logrus,在log包内提供Logger接口供应用调用。logger结构体包装logrus.Entry,并实现Logger接口。

问题

logger设置相关的问题

在我实现logger结构体时,主要遇到了以下两个问题:

  1. Level的处理:为了方便,我打算直接在新建(NewLogger())方法中传参string类型的level,然后通过switch将这个level传给logrus,但这样做还是有问题的,比如当遇到不支持的Level时如何处理?常规来说应该返回一个错误,但这种NewXXX的新建方法我认为不应该返回一个错误。
  2. 输出格式的处理:通过一种什么姿势,把输出格式加载到logrus中?

这两个问题的本质其实是:当一个包内同时包含对外暴露的接口,和不对外暴露的实现类时,我如何初始化这个实现类,如何将配置加载到这个实现类中。

那么,开源项目中有哪些这样的例子呢?

  1. go-kratos/kratos中,log包的stdLogger结构体:

    stdLogger使用标准库log实现其定义的Logger接口。通过NewStdLogger()方法来初始化。标准库的log.New()方法需要三个参数,而在NewStdLogger()中只要求其中一个writer,另外两个以默认值。

    // NewStdLogger new a logger with writer.
    func NewStdLogger(w io.Writer) Logger {
    	return &stdLogger{
    		log: log.New(w, "", 0),
            ......
    }
    

    而关于Level,则是在每次调用Log方法时将Level传入:

  2. Level的处理。为了方便,我打算直接在新建(NewLogger)方法中传参string类型的level,然后通过switch将这个level传给logrus,但这样做还是有问题的:

    1. 通过switch处理level时,当遇到不支持的Level时如何处理,即default怎么写?

      答:应该返回error,但由于逻辑是在NewLogger()方法中,这种新建的方法我认为不应该返回一个错误,所以应该把等级设置的逻辑从NewLogger()中取出。

      并且,可以不用自己switch。用logrus提供的ParseLevel()或者Level.UnmarshalText()方法更好。这个方法个估计就是给我这种懒汉准备的,logrus包内并没有用这个方法。

      // 取自logrus.go
      // ParseLevel takes a string level and returns the Logrus log level constant.
      func ParseLevel(lvl string) (Level, error) {
      	switch strings.ToLower(lvl) {
      	case "panic":
      		return PanicLevel, nil
      	......
      	}
      
      	var l Level
      	return l, fmt.Errorf("not a valid logrus Level: %q", lvl)
      }
      
      // UnmarshalText implements encoding.TextUnmarshaler.
      func (level *Level) UnmarshalText(text []byte) error {
      	l, err := ParseLevel(string(text))
      	if err != nil {
      		return err
      	}
      
      	*level = l
      
      	return nil
      }
      

      参考:

      正经的log包的设计:

      1. logrus包内的处理方式:不处理。logrus在赋值level时,是通过atomic.StoreUint32()方法,直接将Logger.Level的指针指向入参level。反正Logger.Level是个uint32型,输出时只比大小,管你传什么数字进来。并且,一般来说也只会传一个logrus.xxxLevel进来,不会有这个问题。

WithFields方法

阅读列表

  • logrus的设计
  • glog的设计

# Functions

logrus formatter option.
log level option TODO: logrus提供了Fatal等level 但我的接口是不提供的 虽说自己用不会雍错,但这里需不需要做点操作来避免?.
No description provided by the author
No description provided by the author

# Interfaces

No description provided by the author

# Type aliases

logrus Fields.
logrus options.