github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/go-chi/chi/middleware/logger.go (about)

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