...

Source file src/github.com/go-chi/chi/_examples/logging/main.go

Documentation: github.com/go-chi/chi/_examples/logging

     1  //
     2  // Custom Structured Logger
     3  // ========================
     4  // This example demonstrates how to use middleware.RequestLogger,
     5  // middleware.LogFormatter and middleware.LogEntry to build a structured
     6  // logger using the amazing sirupsen/logrus package as the logging
     7  // backend.
     8  //
     9  // Also: check out https://github.com/goware/httplog for an improved context
    10  // logger with support for HTTP request logging, based on the example below.
    11  //
    12  package main
    13  
    14  import (
    15  	"fmt"
    16  	"net/http"
    17  	"time"
    18  
    19  	"github.com/go-chi/chi"
    20  	"github.com/go-chi/chi/middleware"
    21  	"github.com/sirupsen/logrus"
    22  )
    23  
    24  func main() {
    25  
    26  	// Setup the logger backend using sirupsen/logrus and configure
    27  	// it to use a custom JSONFormatter. See the logrus docs for how to
    28  	// configure the backend at github.com/sirupsen/logrus
    29  	logger := logrus.New()
    30  	logger.Formatter = &logrus.JSONFormatter{
    31  		// disable, as we set our own
    32  		DisableTimestamp: true,
    33  	}
    34  
    35  	// Routes
    36  	r := chi.NewRouter()
    37  	r.Use(middleware.RequestID)
    38  	r.Use(NewStructuredLogger(logger))
    39  	r.Use(middleware.Recoverer)
    40  
    41  	r.Get("/", func(w http.ResponseWriter, r *http.Request) {
    42  		w.Write([]byte("welcome"))
    43  	})
    44  	r.Get("/wait", func(w http.ResponseWriter, r *http.Request) {
    45  		time.Sleep(1 * time.Second)
    46  		LogEntrySetField(r, "wait", true)
    47  		w.Write([]byte("hi"))
    48  	})
    49  	r.Get("/panic", func(w http.ResponseWriter, r *http.Request) {
    50  		panic("oops")
    51  	})
    52  	http.ListenAndServe(":3333", r)
    53  }
    54  
    55  // StructuredLogger is a simple, but powerful implementation of a custom structured
    56  // logger backed on logrus. I encourage users to copy it, adapt it and make it their
    57  // own. Also take a look at https://github.com/pressly/lg for a dedicated pkg based
    58  // on this work, designed for context-based http routers.
    59  
    60  func NewStructuredLogger(logger *logrus.Logger) func(next http.Handler) http.Handler {
    61  	return middleware.RequestLogger(&StructuredLogger{logger})
    62  }
    63  
    64  type StructuredLogger struct {
    65  	Logger *logrus.Logger
    66  }
    67  
    68  func (l *StructuredLogger) NewLogEntry(r *http.Request) middleware.LogEntry {
    69  	entry := &StructuredLoggerEntry{Logger: logrus.NewEntry(l.Logger)}
    70  	logFields := logrus.Fields{}
    71  
    72  	logFields["ts"] = time.Now().UTC().Format(time.RFC1123)
    73  
    74  	if reqID := middleware.GetReqID(r.Context()); reqID != "" {
    75  		logFields["req_id"] = reqID
    76  	}
    77  
    78  	scheme := "http"
    79  	if r.TLS != nil {
    80  		scheme = "https"
    81  	}
    82  	logFields["http_scheme"] = scheme
    83  	logFields["http_proto"] = r.Proto
    84  	logFields["http_method"] = r.Method
    85  
    86  	logFields["remote_addr"] = r.RemoteAddr
    87  	logFields["user_agent"] = r.UserAgent()
    88  
    89  	logFields["uri"] = fmt.Sprintf("%s://%s%s", scheme, r.Host, r.RequestURI)
    90  
    91  	entry.Logger = entry.Logger.WithFields(logFields)
    92  
    93  	entry.Logger.Infoln("request started")
    94  
    95  	return entry
    96  }
    97  
    98  type StructuredLoggerEntry struct {
    99  	Logger logrus.FieldLogger
   100  }
   101  
   102  func (l *StructuredLoggerEntry) Write(status, bytes int, header http.Header, elapsed time.Duration, extra interface{}) {
   103  	l.Logger = l.Logger.WithFields(logrus.Fields{
   104  		"resp_status": status, "resp_bytes_length": bytes,
   105  		"resp_elapsed_ms": float64(elapsed.Nanoseconds()) / 1000000.0,
   106  	})
   107  
   108  	l.Logger.Infoln("request complete")
   109  }
   110  
   111  func (l *StructuredLoggerEntry) Panic(v interface{}, stack []byte) {
   112  	l.Logger = l.Logger.WithFields(logrus.Fields{
   113  		"stack": string(stack),
   114  		"panic": fmt.Sprintf("%+v", v),
   115  	})
   116  }
   117  
   118  // Helper methods used by the application to get the request-scoped
   119  // logger entry and set additional fields between handlers.
   120  //
   121  // This is a useful pattern to use to set state on the entry as it
   122  // passes through the handler chain, which at any point can be logged
   123  // with a call to .Print(), .Info(), etc.
   124  
   125  func GetLogEntry(r *http.Request) logrus.FieldLogger {
   126  	entry := middleware.GetLogEntry(r).(*StructuredLoggerEntry)
   127  	return entry.Logger
   128  }
   129  
   130  func LogEntrySetField(r *http.Request, key string, value interface{}) {
   131  	if entry, ok := r.Context().Value(middleware.LogEntryCtxKey).(*StructuredLoggerEntry); ok {
   132  		entry.Logger = entry.Logger.WithField(key, value)
   133  	}
   134  }
   135  
   136  func LogEntrySetFields(r *http.Request, fields map[string]interface{}) {
   137  	if entry, ok := r.Context().Value(middleware.LogEntryCtxKey).(*StructuredLoggerEntry); ok {
   138  		entry.Logger = entry.Logger.WithFields(fields)
   139  	}
   140  }
   141  

View as plain text