github.com/epsagon/epsagon-go@v1.39.0/wrappers/net/http/handler_wrapper.go (about) 1 package epsagonhttp 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "net/url" 8 "runtime/debug" 9 10 "github.com/epsagon/epsagon-go/epsagon" 11 "github.com/epsagon/epsagon-go/protocol" 12 "github.com/epsagon/epsagon-go/tracer" 13 ) 14 15 // HandlerFunction is a generic http handler function 16 type HandlerFunction func(http.ResponseWriter, *http.Request) 17 18 func processRawQuery(urlObj *url.URL, wrapperTracer tracer.Tracer) string { 19 if urlObj == nil { 20 return "" 21 } 22 processed, err := json.Marshal(urlObj.Query()) 23 if err != nil { 24 wrapperTracer.AddException(&protocol.Exception{ 25 Type: "trigger-creation", 26 Message: fmt.Sprintf("Failed to serialize query params %s", urlObj.RawQuery), 27 Traceback: string(debug.Stack()), 28 Time: tracer.GetTimestamp(), 29 }) 30 return "" 31 } 32 return string(processed) 33 } 34 35 // CreateHTTPTriggerEvent creates an HTTP trigger event 36 func CreateHTTPTriggerEvent(wrapperTracer tracer.Tracer, request *http.Request, resourceName string) *protocol.Event { 37 name := resourceName 38 if len(name) == 0 { 39 name = request.Host 40 } 41 event := &protocol.Event{ 42 Id: "", 43 Origin: "trigger", 44 StartTime: tracer.GetTimestamp(), 45 Resource: &protocol.Resource{ 46 Name: name, 47 Type: "http", 48 Operation: request.Method, 49 Metadata: map[string]string{ 50 "path": request.URL.Path, 51 }, 52 }, 53 } 54 if !wrapperTracer.GetConfig().MetadataOnly { 55 event.Resource.Metadata["query_string_parameters"] = processRawQuery( 56 request.URL, wrapperTracer) 57 headers, body := epsagon.ExtractRequestData(request) 58 event.Resource.Metadata["request_headers"] = headers 59 event.Resource.Metadata["request_body"] = body 60 } 61 return event 62 } 63 64 // WrapHandleFunc wraps a generic http.HandleFunc handler function with Epsagon 65 // Last two optional paramerts are the name of the handler (will be the resource name of the events) and an optional hardcoded hostname 66 func WrapHandleFunc( 67 config *epsagon.Config, handler HandlerFunction, names ...string) HandlerFunction { 68 var hostName, handlerName string 69 if config == nil { 70 config = &epsagon.Config{} 71 } 72 if len(names) >= 1 { 73 handlerName = names[0] 74 } 75 if len(names) >= 2 { 76 hostName = names[1] 77 } 78 return func(rw http.ResponseWriter, request *http.Request) { 79 wrapperTracer := tracer.CreateTracer(&config.Config) 80 wrapperTracer.Start() 81 defer wrapperTracer.Stop() 82 83 triggerEvent := CreateHTTPTriggerEvent( 84 wrapperTracer, request, hostName) 85 wrapperTracer.AddEvent(triggerEvent) 86 triggerEvent.Resource.Metadata["status_code"] = "200" 87 defer func() { 88 if userError := recover(); userError != nil { 89 triggerEvent.Resource.Metadata["status_code"] = "500" 90 panic(userError) 91 } 92 }() 93 94 newRequest := request.WithContext( 95 epsagon.ContextWithTracer(wrapperTracer, request.Context())) 96 97 if !config.MetadataOnly { 98 rw = &WrappedResponseWriter{ 99 ResponseWriter: rw, 100 resource: triggerEvent.Resource, 101 } 102 } 103 defer func() { 104 if wrappedResponseWriter, ok := rw.(*WrappedResponseWriter); ok { 105 wrappedResponseWriter.UpdateResource() 106 } 107 }() 108 109 wrapper := epsagon.WrapGenericFunction( 110 handler, config, wrapperTracer, false, handlerName, 111 ) 112 wrapper.Call(rw, newRequest) 113 } 114 }