Reputation: 11251
I would like to log errors from net/http in my own format. In net/http package I have found Server struct:
type Server struct {
//...
ErrorLog *log.Logger
}
I would like to substitute logger with my own implementation:
type AppLogger struct {
log *zap.SugaredLogger
}
func (l *AppLogger) Error(message string, keyAndValues ...interface{}) {
l.log.Errorw(message, keyAndValues...)
}
What is the correct way of implementing this?
Update:
I have zap logger with following config:
cfg := zap.Config{
Encoding: encoding,
Level: zap.NewAtomicLevelAt(zap.DebugLevel),
OutputPaths: []string{"stdout"},
ErrorOutputPaths: []string{"stdout"},
EncoderConfig: encCfg,
}
logger, err := cfg.Build()
It configured to write in json format. I would like errors from net/http
be written in the same way as zap. I create following:
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"}}
}
Questions:
serverJsonWriter
method?zap
io.Writer in order to pass it log.Logger
? How to do this?Upvotes: 5
Views: 5038
Reputation: 520
type serverErrorLogWriter struct{}
func (*serverErrorLogWriter) Write(p []byte) (int, error) {
m := string(p)
if strings.HasPrefix(m, "http: TLS handshake error") && strings.HasSuffix(m, ": EOF\n") {
// handle EOF error
} else {
// handle other errors
}
return len(p), nil
}
func newServerErrorLog() *log.Logger {
return log.New(&serverErrorLogWriter{}, "", 0)
}
And then
server := &http.Server{
Addr: addr,
Handler: handler,
ErrorLog: log.New(&serverErrorLogWriter{}, "", 0),
}
Upvotes: 0
Reputation: 57184
You can use zap.NewStdLog()
to get a new instance of a *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"}
Upvotes: 3
Reputation: 417592
This is easily doable, because the log.Logger
type guarantees that each log message is delivered to the destination io.Writer
with a single Writer.Write()
call:
Each logging operation makes a single call to the Writer's Write method. A Logger can be used simultaneously from multiple goroutines; it guarantees to serialize access to the Writer.
So basically you just need to create a type which implements io.Writer
, and whose Write()
method simply calls your logger.
Here's a simple implementation which does that:
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
}
And that's all. You can "install" this writer at your http.Server
like this:
server := &http.Server{
Addr: addr,
Handler: handler,
ErrorLog: logger.New(&fwdToZapWriter{logger}, "", 0),
}
logger
in the above example is from your example: logger, err := cfg.Build()
If you want, you can just as easily forward to your AppLogger
instead of logger
.
See similar question: Go: Create io.Writer inteface for logging to mongodb database
Upvotes: 7