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?Logging is the process of recording events in software as they happen in real-time, along with other information such as the infrastructure details, time taken to execute, etc. Logging is an essential part of any software application.
Usage and registration of the custom logger. By convention, registering services for dependency injection happens as part of the startup routine of an application. The registration occurs in the Program class, or could be delegated to a Startup class. In this example, you'll register directly from the Program.
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
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"}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With