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  }