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
记录的单个错误信息转换为message
和keyAndValues
这两个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 设置自定义记录器的主要内容,如果未能解决你的问题,请参考以下文章
Python日志记录:如何向LogRecord添加自定义字段,并注册全局回调以设置其值
一段简单的代码记录如何通过 js 给 HTML 设置自定义属性,并且通过点击事件获取到所设置的自定义属性值