github.com/mendersoftware/go-lib-micro@v0.0.0-20240304135804-e8e39c59b148/rest_utils/response_helpers.go (about) 1 // Copyright 2023 Northern.tech AS 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package rest_utils 15 16 import ( 17 "net/http" 18 "runtime" 19 20 "github.com/ant0ine/go-json-rest/rest" 21 "github.com/pkg/errors" 22 "github.com/sirupsen/logrus" 23 24 "github.com/mendersoftware/go-lib-micro/accesslog" 25 "github.com/mendersoftware/go-lib-micro/log" 26 "github.com/mendersoftware/go-lib-micro/requestid" 27 ) 28 29 func getCalleeFrame(skip int) *runtime.Frame { 30 const calleeDepth = 2 31 var pc [1]uintptr 32 i := runtime.Callers(calleeDepth+skip, pc[:]) 33 frame, _ := runtime.CallersFrames(pc[:i]). 34 Next() 35 if frame.Func != nil { 36 return &frame 37 } 38 return nil 39 } 40 41 // return selected http code + error message directly taken from error 42 // log error 43 func RestErrWithLog(w rest.ResponseWriter, r *rest.Request, l *log.Logger, e error, code int) { 44 restErrWithLogMsg(w, r, l, e, code, "", logrus.ErrorLevel) 45 } 46 47 // return http 500, with an "internal error" message 48 // log full error 49 func RestErrWithLogInternal(w rest.ResponseWriter, r *rest.Request, l *log.Logger, e error) { 50 msg := "internal error" 51 restErrWithLogMsg(w, r, l, e, http.StatusInternalServerError, msg, logrus.ErrorLevel) 52 } 53 54 // return an error code with an overriden message (to avoid exposing the details) 55 // log full error as debug 56 func RestErrWithDebugMsg(w rest.ResponseWriter, r *rest.Request, l *log.Logger, e error, code int, msg string) { 57 restErrWithLogMsg(w, r, l, e, code, msg, logrus.DebugLevel) 58 } 59 60 // return an error code with an overriden message (to avoid exposing the details) 61 // log full error as info 62 func RestErrWithInfoMsg(w rest.ResponseWriter, r *rest.Request, l *log.Logger, e error, code int, msg string) { 63 restErrWithLogMsg(w, r, l, e, code, msg, logrus.InfoLevel) 64 } 65 66 // return an error code with an overriden message (to avoid exposing the details) 67 // log full error as warning 68 func RestErrWithWarningMsg(w rest.ResponseWriter, r *rest.Request, l *log.Logger, e error, code int, msg string) { 69 restErrWithLogMsg(w, r, l, e, code, msg, logrus.WarnLevel) 70 } 71 72 // same as RestErrWithErrorMsg - for backward compatibility purpose 73 // return an error code with an overriden message (to avoid exposing the details) 74 // log full error as error 75 func RestErrWithLogMsg(w rest.ResponseWriter, r *rest.Request, l *log.Logger, e error, code int, msg string) { 76 restErrWithLogMsg(w, r, l, e, code, msg, logrus.ErrorLevel) 77 } 78 79 // return an error code with an overriden message (to avoid exposing the details) 80 // log full error as error 81 func RestErrWithErrorMsg(w rest.ResponseWriter, r *rest.Request, l *log.Logger, e error, code int, msg string) { 82 restErrWithLogMsg(w, r, l, e, code, msg, logrus.ErrorLevel) 83 } 84 85 // return an error code with an overriden message (to avoid exposing the details) 86 // log full error as fatal 87 func RestErrWithFatalMsg(w rest.ResponseWriter, r *rest.Request, l *log.Logger, e error, code int, msg string) { 88 restErrWithLogMsg(w, r, l, e, code, msg, logrus.FatalLevel) 89 } 90 91 // return an error code with an overriden message (to avoid exposing the details) 92 // log full error as panic 93 func RestErrWithPanicMsg(w rest.ResponseWriter, r *rest.Request, l *log.Logger, e error, code int, msg string) { 94 restErrWithLogMsg(w, r, l, e, code, msg, logrus.PanicLevel) 95 } 96 97 // return an error code with an overriden message (to avoid exposing the details) 98 // log full error with given log level 99 func restErrWithLogMsg(w rest.ResponseWriter, r *rest.Request, l *log.Logger, 100 e error, code int, msg string, logLevel logrus.Level) { 101 if msg != "" { 102 e = errors.WithMessage(e, msg) 103 } else { 104 msg = e.Error() 105 } 106 var emitLog bool 107 if lc := accesslog.GetContext(r.Context()); lc != nil { 108 err := e 109 // Try push error to accesslog middleware. 110 f := getCalleeFrame(2) 111 if f != nil { 112 // Add the call frame to the error message: 113 // "<call frame>: <error message>" 114 err = errors.WithMessage(e, log.FmtCaller(*f)) 115 } 116 emitLog = !lc.PushError(err) 117 } 118 119 w.WriteHeader(code) 120 err := w.WriteJson(ApiError{ 121 Err: msg, 122 ReqId: requestid.GetReqId(r), 123 }) 124 if err != nil { 125 panic(err) 126 } 127 if emitLog { 128 // Only emit a log entry if we failed to push the error to 129 // accessloger. 130 // Set the caller frame for the entry 131 l = l.WithCallerContext(2) 132 switch logLevel { 133 case logrus.DebugLevel: 134 l.Debug(e.Error()) 135 case logrus.InfoLevel: 136 l.Info(e.Error()) 137 case logrus.WarnLevel: 138 l.Warn(e.Error()) 139 case logrus.ErrorLevel: 140 l.Error(e.Error()) 141 case logrus.FatalLevel: 142 l.Fatal(e.Error()) 143 case logrus.PanicLevel: 144 l.Panic(e.Error()) 145 } 146 } 147 }