github.com/blend/go-sdk@v1.20220411.3/webutil/http_request_event.go (about)

     1  /*
     2  
     3  Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file.
     5  
     6  */
     7  
     8  package webutil
     9  
    10  import (
    11  	"context"
    12  	"fmt"
    13  	"io"
    14  	"net/http"
    15  	"time"
    16  
    17  	"github.com/blend/go-sdk/ansi"
    18  	"github.com/blend/go-sdk/logger"
    19  	"github.com/blend/go-sdk/stringutil"
    20  	"github.com/blend/go-sdk/timeutil"
    21  )
    22  
    23  var (
    24  	_ logger.Event        = (*HTTPRequestEvent)(nil)
    25  	_ logger.TextWritable = (*HTTPRequestEvent)(nil)
    26  	_ logger.JSONWritable = (*HTTPRequestEvent)(nil)
    27  )
    28  
    29  // NewHTTPRequestEvent is an event representing a request to an http server.
    30  func NewHTTPRequestEvent(req *http.Request, options ...HTTPRequestEventOption) HTTPRequestEvent {
    31  	hre := HTTPRequestEvent{
    32  		Request: req,
    33  	}
    34  	for _, option := range options {
    35  		option(&hre)
    36  	}
    37  	return hre
    38  }
    39  
    40  // NewHTTPRequestEventListener returns a new web request event listener.
    41  func NewHTTPRequestEventListener(listener func(context.Context, HTTPRequestEvent)) logger.Listener {
    42  	return func(ctx context.Context, e logger.Event) {
    43  		if typed, isTyped := e.(HTTPRequestEvent); isTyped {
    44  			listener(ctx, typed)
    45  		}
    46  	}
    47  }
    48  
    49  // NewHTTPRequestEventFilter returns a new http request event filter.
    50  func NewHTTPRequestEventFilter(filter func(context.Context, HTTPRequestEvent) (HTTPRequestEvent, bool)) logger.Filter {
    51  	return func(ctx context.Context, e logger.Event) (logger.Event, bool) {
    52  		if typed, isTyped := e.(HTTPRequestEvent); isTyped {
    53  			return filter(ctx, typed)
    54  		}
    55  		return e, false
    56  	}
    57  }
    58  
    59  // HTTPRequestEventOption is a function that modifies an http request event.
    60  type HTTPRequestEventOption func(*HTTPRequestEvent)
    61  
    62  // OptHTTPRequestRequest sets a field.
    63  func OptHTTPRequestRequest(req *http.Request) HTTPRequestEventOption {
    64  	return func(hre *HTTPRequestEvent) { hre.Request = req }
    65  }
    66  
    67  // OptHTTPRequestRoute sets a field.
    68  func OptHTTPRequestRoute(route string) HTTPRequestEventOption {
    69  	return func(hre *HTTPRequestEvent) { hre.Route = route }
    70  }
    71  
    72  // OptHTTPRequestContentLength sets a field.
    73  func OptHTTPRequestContentLength(contentLength int) HTTPRequestEventOption {
    74  	return func(hre *HTTPRequestEvent) { hre.ContentLength = contentLength }
    75  }
    76  
    77  // OptHTTPRequestContentType sets a field.
    78  func OptHTTPRequestContentType(contentType string) HTTPRequestEventOption {
    79  	return func(hre *HTTPRequestEvent) { hre.ContentType = contentType }
    80  }
    81  
    82  // OptHTTPRequestContentEncoding sets a field.
    83  func OptHTTPRequestContentEncoding(contentEncoding string) HTTPRequestEventOption {
    84  	return func(hre *HTTPRequestEvent) { hre.ContentEncoding = contentEncoding }
    85  }
    86  
    87  // OptHTTPRequestStatusCode sets a field.
    88  func OptHTTPRequestStatusCode(statusCode int) HTTPRequestEventOption {
    89  	return func(hre *HTTPRequestEvent) { hre.StatusCode = statusCode }
    90  }
    91  
    92  // OptHTTPRequestElapsed sets a field.
    93  func OptHTTPRequestElapsed(elapsed time.Duration) HTTPRequestEventOption {
    94  	return func(hre *HTTPRequestEvent) { hre.Elapsed = elapsed }
    95  }
    96  
    97  // OptHTTPRequestHeader sets a field.
    98  func OptHTTPRequestHeader(header http.Header) HTTPRequestEventOption {
    99  	return func(hre *HTTPRequestEvent) { hre.Header = header }
   100  }
   101  
   102  // HTTPRequestEvent is an event type for http requests.
   103  type HTTPRequestEvent struct {
   104  	Request         *http.Request
   105  	Route           string
   106  	ContentLength   int
   107  	ContentType     string
   108  	ContentEncoding string
   109  	StatusCode      int
   110  	Elapsed         time.Duration
   111  	Header          http.Header
   112  }
   113  
   114  // GetFlag implements event.
   115  func (e HTTPRequestEvent) GetFlag() string { return FlagHTTPRequest }
   116  
   117  // WriteText implements TextWritable.
   118  func (e HTTPRequestEvent) WriteText(tf logger.TextFormatter, wr io.Writer) {
   119  	if ip := GetRemoteAddr(e.Request); len(ip) > 0 {
   120  		fmt.Fprint(wr, ip)
   121  		fmt.Fprint(wr, logger.Space)
   122  	}
   123  	fmt.Fprint(wr, tf.Colorize(e.Request.Method, ansi.ColorBlue))
   124  	fmt.Fprint(wr, logger.Space)
   125  	fmt.Fprint(wr, e.Request.URL.String())
   126  	fmt.Fprint(wr, logger.Space)
   127  	fmt.Fprint(wr, ColorizeStatusCodeWithFormatter(tf, e.StatusCode))
   128  	fmt.Fprint(wr, logger.Space)
   129  	fmt.Fprint(wr, e.Elapsed.String())
   130  	if len(e.ContentType) > 0 {
   131  		fmt.Fprint(wr, logger.Space)
   132  		fmt.Fprint(wr, e.ContentType)
   133  	}
   134  	fmt.Fprint(wr, logger.Space)
   135  	fmt.Fprint(wr, stringutil.FileSize(e.ContentLength))
   136  }
   137  
   138  // Decompose implements JSONWritable.
   139  func (e HTTPRequestEvent) Decompose() map[string]interface{} {
   140  	return map[string]interface{}{
   141  		"ip":              GetRemoteAddr(e.Request),
   142  		"userAgent":       GetUserAgent(e.Request),
   143  		"verb":            e.Request.Method,
   144  		"path":            e.Request.URL.Path,
   145  		"route":           e.Route,
   146  		"query":           e.Request.URL.RawQuery,
   147  		"host":            e.Request.Host,
   148  		"contentLength":   e.ContentLength,
   149  		"contentType":     e.ContentType,
   150  		"contentEncoding": e.ContentEncoding,
   151  		"statusCode":      e.StatusCode,
   152  		"elapsed":         timeutil.Milliseconds(e.Elapsed),
   153  	}
   154  }