【问题标题】:Proper logging implementation in Golang packageGolang 包中正确的日志记录实现
【发布时间】:2020-07-09 04:16:09
【问题描述】:

我有一个小的 Golang package 可以做一些工作。这项工作假设可能会产生大量错误,这是可以的。目前所有错误都被忽略。是的,它可能看起来很奇怪,但请访问链接并检查包的主要用途。 我想扩展包的功能并提供在运行时查看错误的能力。但由于缺乏软件设计技能,我有一些问题没有答案。

起初,我想使用现有的日志记录(zerolog、zap 或其他任何东西)在包内实现日志记录。但是,包的用户可以吗?因为他们可能想使用其他日志记录包并想修改输出格式。 也许可以为用户提供一种方法来注入它自己的日志记录?

我希望能够提供易于配置的日志记录方式,可以根据用户需求打开或关闭。

【问题讨论】:

    标签: go logging packaging software-design


    【解决方案1】:

    一些 go lib 使用这样的日志记录

    在你的包中定义一个记录器接口

    type Yourlogging interface{
          Errorf(...)
          Warningf(...)
          Infof(...)
          Debugf(...)
    }
    

    并为此接口定义一个变量

      var mylogger Yourlogging
      func SetLogger(l yourlogging)error{
           mylogger = l
      }
    

    在你的函数中,你可以调用它们进行日志记录

      mylogger.Infof(..)
    
      mylogger.Errorf(...)
    

    你不需要实现这个接口,但是你可以使用那些实现这个接口的人

     for example:
         SetLogger(os.Stdout)    //logging output to stdout
         SetLogger(logrus.New()) // logging output to logrus  (github.com/sirupsen/logrus)
      
    

    【讨论】:

      【解决方案2】:

      在 Go 中,您会看到一些库实现了日志接口,就像其他答案所建议的那样。但是,例如,如果您以不同的方式构建应用程序,则可以完全避免您的包需要记录。

      例如,在您链接的示例应用程序中,您的主应用程序运行时调用idleexacts.Run(),它会启动此函数。

      // startLoop starts workload using passed settings and database connection.
      func startLoop(ctx context.Context, log log.Logger, pool db.DB, tables []string, jobs uint16, minTime, maxTime time.Duration) error {
          rand.Seed(time.Now().UnixNano())
      
          // Increment maxTime up to 1 due to rand.Int63n() never return max value.
          maxTime++
      
          // While running, keep required number of workers using channel.
          // Run new workers only until there is any free slot.
          guard := make(chan struct{}, jobs)
          for {
              select {
              // Run workers only when it's possible to write into channel (channel is limited by number of jobs).
              case guard <- struct{}{}:
                  go func() {
                      table := selectRandomTable(tables)
                      naptime := time.Duration(rand.Int63n(maxTime.Nanoseconds()-minTime.Nanoseconds()) + minTime.Nanoseconds())
      
                      err := startSingleIdleXact(ctx, pool, table, naptime)
                      if err != nil {
                          log.Warnf("start idle xact failed: %s", err)
                      }
      
                      // When worker finishes, read from the channel to allow starting another worker.
                      <-guard
                  }()
              case <-ctx.Done():
      
                  return nil
              }
          }
      }
      

      这里的问题是您的所有逻辑编排都发生在您的包中。相反,这个循环应该在你的主应用程序中运行,并且这个包应该为用户提供简单的操作,例如selectRandomTable()createTempTable()

      如果代码编排在您的主应用程序中,并且包仅提供简单的操作。作为函数调用的一部分,将错误返回给用户会容易得多。

      它还可以让其他人更容易重复使用您的包,因为它们具有简单的操作和开放用户以其他方式使用它们而不是您想要的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-29
        • 2020-12-07
        • 1970-01-01
        • 1970-01-01
        • 2023-03-06
        • 2015-05-13
        相关资源
        最近更新 更多