restgologginggo-chi

How to log only errors like 404 with chi in go?


I'm using chi with our Go webservices.

How to configure the logger (middleware) so it only logs requests that ended up with errors (like 404) but it doesn't log successful requests (with status code 200)?

Here's our current implementation (with no logging at all)

r := chi.NewRouter()

if DEBUG_LOGS {
    r.Use(middleware.Logger)
} else {

}

Solution

  • The easiest way is to implement the logging function by yourself using the example from the chi package (for simplicity, I removed the colors).

    package main
    
    import (
        "bytes"
        "fmt"
        "log"
        "net/http"
        "os"
        "time"
    
        "github.com/go-chi/chi/v5"
        "github.com/go-chi/chi/v5/middleware"
    )
    
    const DEBUG_LOGS = true
    
    func main() {
        api := &http.Server{Addr: ":8000"}
    
        r := chi.NewRouter()
    
        if DEBUG_LOGS {
            // create default logger/zerolog/logrus
            logger := log.New(os.Stdout, "", log.LstdFlags)
            r.Use(middleware.RequestLogger(&StructuredLogger{logger}))
        }
    
        r.Get("/tea", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusTeapot) })
        r.Get("/ok", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })
    
        api.Handler = r
    
        err := api.ListenAndServe()
        if err != nil {
            log.Fatal(err)
        }
    }
    
    // below is the implementation of the custom logger.
    
    type StructuredLogger struct {
        Logger middleware.LoggerInterface
    }
    
    type LogEntry struct {
        *StructuredLogger
        request  *http.Request
        buf      *bytes.Buffer
        useColor bool
    }
    
    func (l *StructuredLogger) NewLogEntry(r *http.Request) middleware.LogEntry {
        entry := &LogEntry{
            StructuredLogger: l,
            request:          r,
            buf:              &bytes.Buffer{},
            useColor:         false,
        }
    
        reqID := middleware.GetReqID(r.Context())
        if reqID != "" {
            fmt.Fprintf(entry.buf, "[%s] ", reqID)
        }
    
        fmt.Fprintf(entry.buf, "\"")
        fmt.Fprintf(entry.buf, "%s ", r.Method)
    
        scheme := "http"
        if r.TLS != nil {
            scheme = "https"
        }
        fmt.Fprintf(entry.buf, "%s://%s%s %s\" ", scheme, r.Host, r.RequestURI, r.Proto)
    
        entry.buf.WriteString("from ")
        entry.buf.WriteString(r.RemoteAddr)
        entry.buf.WriteString(" - ")
    
        return entry
    }
    
    func (l *LogEntry) Write(status, bytes int, header http.Header, elapsed time.Duration, extra interface{}) {
        // Do nothing if status code is 200/201/eg
        if status < 300 {
            return
        }
    
        fmt.Fprintf(l.buf, "%03d", status)
        fmt.Fprintf(l.buf, " %dB", bytes)
    
        l.buf.WriteString(" in ")
        if elapsed < 500*time.Millisecond {
            fmt.Fprintf(l.buf, "%s", elapsed)
        } else if elapsed < 5*time.Second {
            fmt.Fprintf(l.buf, "%s", elapsed)
        } else {
            fmt.Fprintf(l.buf, "%s", elapsed)
        }
    
        l.Logger.Print(l.buf.String())
    }
    
    func (l *LogEntry) Panic(v interface{}, stack []byte) {
        middleware.PrintPrettyStack(v)
    }