Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

net/http set custom logger

Tags:

http

logging

go

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:

  1. What should be the body of serverJsonWriter method?
  2. Should I retrieve zap io.Writer in order to pass it log.Logger? How to do this?
like image 648
Rudziankoŭ Avatar asked Sep 12 '18 11:09

Rudziankoŭ


People also ask

What is .NET logger?

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.

Where can custom logging provider and corresponding logger be registered?

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.


2 Answers

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

like image 148
icza Avatar answered Sep 29 '22 12:09

icza


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"}
like image 29
Xeoncross Avatar answered Sep 29 '22 11:09

Xeoncross