net/http 设置自定义记录器

Posted

技术标签:

【中文标题】net/http 设置自定义记录器【英文标题】:net/http set custom logger 【发布时间】: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"

问题:

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

【问题讨论】:

不幸的是,这是不可能的,因为*log.Logger 是一个具体的类型。您可以做的最接近的方法是使用记录到您自己的io.Writer 的记录器。 有a proposal 可以让log.Logger 成为一个接口,这样会更容易。 你想如何将net/http记录的单个错误信息转换为messagekeyAndValues这两个SugaredLogger.Errorw()的参数? @icza,我将跳过keyAndValues。我只想从net/http传递message 【参考方案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(&fwdToZapWriterlogger, "", 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(&netHTTPLoggerToZaplogger, "", 0)(刚刚重命名它)时,我收到 New 的错误,显示 logger.New(&netHTTPLoggerToZaplogger, "", 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(&fwdToZapWritersugar, "", 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"

【讨论】:

以上是关于net/http 设置自定义记录器的主要内容,如果未能解决你的问题,请参考以下文章

Golang 标准库net/http自定义404页面

Python日志记录:如何向LogRecord添加自定义字段,并注册全局回调以设置其值

一段简单的代码记录如何通过 js 给 HTML 设置自定义属性,并且通过点击事件获取到所设置的自定义属性值

使用 Ruby 发送自定义 HTTP 标头

对使用自定义子域设置 Firebase 动态链接所需的 A 记录的担忧

WordPress记录———自定义小工具自定义图片