【问题标题】:net/http set custom loggernet/http 设置自定义记录器
【发布时间】:2019-02-17 01:05:11
【问题描述】:

我想以我自己的格式记录来自 net/http 的错误。在 net/http 包中我找到了Server struct:

type Server struct {
        //...
        ErrorLog *log.Logger
}

我想用我自己的实现替换记录器:

type AppLogger struct {
    log *zap.SugaredLogger
}

func (l *AppLogger) Error(message string, keyAndValues ...interface{}) {
    l.log.Errorw(message, keyAndValues...)
}

实现这个的正确方法是什么?


更新:

我有 zap 记录器,配置如下:

cfg := zap.Config{
    Encoding:         encoding,
    Level:            zap.NewAtomicLevelAt(zap.DebugLevel),
    OutputPaths:      []string{"stdout"},
    ErrorOutputPaths: []string{"stdout"},
    EncoderConfig:    encCfg,
}
logger, err := cfg.Build()

它配置为以 json 格式写入。我希望 net/http 中的错误以与 zap 相同的方式编写。我创建以下内容:

type serverJsonWriter struct {
    io.Writer
}

// ListenAndServeTLS - with custom log Writer
func ListenAndServeTLS(addr, certFile, keyFile string, handler http.Handler) error {
    server := &http.Server{
        Addr: addr,
        Handler: handler,
        ErrorLog: logger.New(serverJsonWriter{}, "", 0),
    }
}

func (w serverJsonWriter) Write(p []byte) (n int, err error){
    // {"error":{"type":"net/http error","message":"header too long"}}
}

问题:

  1. serverJsonWriter 方法的主体应该是什么?
  2. 我应该检索zap io.Writer 以传递log.Logger 吗?如何做到这一点?

【问题讨论】:

  • 不幸的是,这是不可能的,因为*log.Logger 是一个具体的类型。您可以做的最接近的方法是使用记录到您自己的io.Writer 的记录器。
  • a proposal 可以让log.Logger 成为一个接口,这样会更容易。
  • 你想如何将net/http记录的单个错误信息转换为messagekeyAndValues这两个SugaredLogger.Errorw()的参数?
  • @icza,我将跳过keyAndValues。我只想从net/http传递message

标签: http go logging


【解决方案1】:

这很容易实现,因为log.Logger 类型保证每条日志消息都通过单个Writer.Write() 调用传递到目标io.Writer

每个日志记录操作都会调用 Writer 的 Write 方法。一个 Logger 可以同时从多个 goroutine 中使用;它保证序列化对 Writer 的访问。

所以基本上你只需要创建一个实现io.Writer 的类型,其Write() 方法只需调用你的记录器。

这是一个简单的实现:

type fwdToZapWriter struct {
    logger *zap.SugaredLogger
}

func (fw *fwdToZapWriter) Write(p []byte) (n int, err error) {
    fw.logger.Errorw(string(p))
    return len(p), nil
}

仅此而已。你可以像这样在你的http.Server“安装”这个作家:

server := &http.Server{
    Addr:     addr,
    Handler:  handler,
    ErrorLog: logger.New(&fwdToZapWriter{logger}, "", 0),
}

以上示例中的logger来自您的示例:logger, err := cfg.Build()

如果需要,您可以轻松转发到您的 AppLogger 而不是 logger

查看类似问题:Go: Create io.Writer inteface for logging to mongodb database

【讨论】:

  • 如果我们只是尝试登录,返回 len(p), nil 的原因是什么?
  • @TheRealFakeNews 因为Write()方法是实现io.Writer,它的contract要求我们返回写入器字节数和写入错误(如果有是任何)。
  • 我尝试按照您的示例进行操作,但是当我包含 ErrorLog: logger.New(&netHTTPLoggerToZap{logger}, "", 0)(刚刚重命名它)时,我收到 New 的错误,显示 logger.New(&netHTTPLoggerToZap{logger}, "", 0), 和第二个 logger 的错误: cannot use logger (variable of type *zap.Logger) as *zap.SugaredLogger value in struct literalcompilerIncompatibleAssign。虽然我选择使用logger, err := zap.NewProduction()...但不确定这是否会有所不同。
  • 错误应该是不言自明的:您尝试将值分配给具有不同类型的fwdToZapWriter.logger 字段。
  • 好的,我明白了。所以我然后像这样传递糖:logger.New(&fwdToZapWriter{sugar}, "", 0)。但是,我仍然在New:logger.New undefined (type *zap.Logger has no field or method New)compilerMissingFieldOrMethod 下收到错误消息。非常感谢您的帮助!
【解决方案2】:

您可以使用zap.NewStdLog() 获取*log.Logger 的新实例。

https://godoc.org/go.uber.org/zap#NewStdLog

logger := zap.NewExample()
defer logger.Sync()

std := zap.NewStdLog(logger)
std.Print("standard logger wrapper")

// Output:
// {"level":"info","msg":"standard logger wrapper"}

【讨论】:

    猜你喜欢
    • 2017-03-01
    • 2014-07-12
    • 1970-01-01
    • 2016-11-04
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-09
    相关资源
    最近更新 更多