...

Source file src/github.com/go-chi/chi/middleware/logger.go

Documentation: github.com/go-chi/chi/middleware

     1  package middleware
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"log"
     7  	"net/http"
     8  	"os"
     9  	"time"
    10  )
    11  
    12  var (
    13  	// LogEntryCtxKey is the context.Context key to store the request log entry.
    14  	LogEntryCtxKey = &contextKey{"LogEntry"}
    15  
    16  	// DefaultLogger is called by the Logger middleware handler to log each request.
    17  	// Its made a package-level variable so that it can be reconfigured for custom
    18  	// logging configurations.
    19  	DefaultLogger = RequestLogger(&DefaultLogFormatter{Logger: log.New(os.Stdout, "", log.LstdFlags), NoColor: false})
    20  )
    21  
    22  // Logger is a middleware that logs the start and end of each request, along
    23  // with some useful data about what was requested, what the response status was,
    24  // and how long it took to return. When standard output is a TTY, Logger will
    25  // print in color, otherwise it will print in black and white. Logger prints a
    26  // request ID if one is provided.
    27  //
    28  // Alternatively, look at https://github.com/goware/httplog for a more in-depth
    29  // http logger with structured logging support.
    30  func Logger(next http.Handler) http.Handler {
    31  	return DefaultLogger(next)
    32  }
    33  
    34  // RequestLogger returns a logger handler using a custom LogFormatter.
    35  func RequestLogger(f LogFormatter) func(next http.Handler) http.Handler {
    36  	return func(next http.Handler) http.Handler {
    37  		fn := func(w http.ResponseWriter, r *http.Request) {
    38  			entry := f.NewLogEntry(r)
    39  			ww := NewWrapResponseWriter(w, r.ProtoMajor)
    40  
    41  			t1 := time.Now()
    42  			defer func() {
    43  				entry.Write(ww.Status(), ww.BytesWritten(), ww.Header(), time.Since(t1), nil)
    44  			}()
    45  
    46  			next.ServeHTTP(ww, WithLogEntry(r, entry))
    47  		}
    48  		return http.HandlerFunc(fn)
    49  	}
    50  }
    51  
    52  // LogFormatter initiates the beginning of a new LogEntry per request.
    53  // See DefaultLogFormatter for an example implementation.
    54  type LogFormatter interface {
    55  	NewLogEntry(r *http.Request) LogEntry
    56  }
    57  
    58  // LogEntry records the final log when a request completes.
    59  // See defaultLogEntry for an example implementation.
    60  type LogEntry interface {
    61  	Write(status, bytes int, header http.Header, elapsed time.Duration, extra interface{})
    62  	Panic(v interface{}, stack []byte)
    63  }
    64  
    65  // GetLogEntry returns the in-context LogEntry for a request.
    66  func GetLogEntry(r *http.Request) LogEntry {
    67  	entry, _ := r.Context().Value(LogEntryCtxKey).(LogEntry)
    68  	return entry
    69  }
    70  
    71  // WithLogEntry sets the in-context LogEntry for a request.
    72  func WithLogEntry(r *http.Request, entry LogEntry) *http.Request {
    73  	r = r.WithContext(context.WithValue(r.Context(), LogEntryCtxKey, entry))
    74  	return r
    75  }
    76  
    77  // LoggerInterface accepts printing to stdlib logger or compatible logger.
    78  type LoggerInterface interface {
    79  	Print(v ...interface{})
    80  }
    81  
    82  // DefaultLogFormatter is a simple logger that implements a LogFormatter.
    83  type DefaultLogFormatter struct {
    84  	Logger  LoggerInterface
    85  	NoColor bool
    86  }
    87  
    88  // NewLogEntry creates a new LogEntry for the request.
    89  func (l *DefaultLogFormatter) NewLogEntry(r *http.Request) LogEntry {
    90  	useColor := !l.NoColor
    91  	entry := &defaultLogEntry{
    92  		DefaultLogFormatter: l,
    93  		request:             r,
    94  		buf:                 &bytes.Buffer{},
    95  		useColor:            useColor,
    96  	}
    97  
    98  	reqID := GetReqID(r.Context())
    99  	if reqID != "" {
   100  		cW(entry.buf, useColor, nYellow, "[%s] ", reqID)
   101  	}
   102  	cW(entry.buf, useColor, nCyan, "\"")
   103  	cW(entry.buf, useColor, bMagenta, "%s ", r.Method)
   104  
   105  	scheme := "http"
   106  	if r.TLS != nil {
   107  		scheme = "https"
   108  	}
   109  	cW(entry.buf, useColor, nCyan, "%s://%s%s %s\" ", scheme, r.Host, r.RequestURI, r.Proto)
   110  
   111  	entry.buf.WriteString("from ")
   112  	entry.buf.WriteString(r.RemoteAddr)
   113  	entry.buf.WriteString(" - ")
   114  
   115  	return entry
   116  }
   117  
   118  type defaultLogEntry struct {
   119  	*DefaultLogFormatter
   120  	request  *http.Request
   121  	buf      *bytes.Buffer
   122  	useColor bool
   123  }
   124  
   125  func (l *defaultLogEntry) Write(status, bytes int, header http.Header, elapsed time.Duration, extra interface{}) {
   126  	switch {
   127  	case status < 200:
   128  		cW(l.buf, l.useColor, bBlue, "%03d", status)
   129  	case status < 300:
   130  		cW(l.buf, l.useColor, bGreen, "%03d", status)
   131  	case status < 400:
   132  		cW(l.buf, l.useColor, bCyan, "%03d", status)
   133  	case status < 500:
   134  		cW(l.buf, l.useColor, bYellow, "%03d", status)
   135  	default:
   136  		cW(l.buf, l.useColor, bRed, "%03d", status)
   137  	}
   138  
   139  	cW(l.buf, l.useColor, bBlue, " %dB", bytes)
   140  
   141  	l.buf.WriteString(" in ")
   142  	if elapsed < 500*time.Millisecond {
   143  		cW(l.buf, l.useColor, nGreen, "%s", elapsed)
   144  	} else if elapsed < 5*time.Second {
   145  		cW(l.buf, l.useColor, nYellow, "%s", elapsed)
   146  	} else {
   147  		cW(l.buf, l.useColor, nRed, "%s", elapsed)
   148  	}
   149  
   150  	l.Logger.Print(l.buf.String())
   151  }
   152  
   153  func (l *defaultLogEntry) Panic(v interface{}, stack []byte) {
   154  	PrintPrettyStack(v)
   155  }
   156  

View as plain text