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 }