156 lines
3.6 KiB
Go
156 lines
3.6 KiB
Go
package callog
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"reflect"
|
|
"regexp"
|
|
"runtime"
|
|
"strings"
|
|
"time"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/status-im/status-go/internal/sentry"
|
|
)
|
|
|
|
var sensitiveKeys = []string{
|
|
"password",
|
|
"newPassword",
|
|
"mnemonic",
|
|
"openseaAPIKey",
|
|
"poktToken",
|
|
"alchemyArbitrumMainnetToken",
|
|
"raribleTestnetAPIKey",
|
|
"alchemyOptimismMainnetToken",
|
|
"statusProxyBlockchainUser",
|
|
"alchemyEthereumSepoliaToken",
|
|
"alchemyArbitrumSepoliaToken",
|
|
"infuraToken",
|
|
"raribleMainnetAPIKey",
|
|
"alchemyEthereumMainnetToken",
|
|
"alchemyOptimismSepoliaToken",
|
|
"verifyENSURL",
|
|
"verifyTransactionURL",
|
|
}
|
|
|
|
var sensitiveRegexString = fmt.Sprintf(`(?i)(".*?(%s).*?")\s*:\s*("[^"]*")`, strings.Join(sensitiveKeys, "|"))
|
|
|
|
var sensitiveRegex = regexp.MustCompile(sensitiveRegexString)
|
|
|
|
func getFunctionName(fn any) string {
|
|
return runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()
|
|
}
|
|
|
|
func getShortFunctionName(fn any) string {
|
|
fullName := getFunctionName(fn)
|
|
parts := strings.Split(fullName, ".")
|
|
return parts[len(parts)-1]
|
|
}
|
|
|
|
// Call executes the given function and logs request details if logging is enabled
|
|
//
|
|
// Parameters:
|
|
// - fn: The function to be executed
|
|
// - params: A variadic list of parameters to be passed to the function
|
|
//
|
|
// Returns:
|
|
// - The result of the function execution (if any)
|
|
//
|
|
// Functionality:
|
|
// 1. Sets up panic recovery to log and re-panic
|
|
// 2. Records start time if request logging is enabled
|
|
// 3. Uses reflection to Call the given function
|
|
// 4. If request logging is enabled, logs method name, parameters, response, and execution duration
|
|
// 5. Removes sensitive information before logging
|
|
func Call(logger, requestLogger *zap.Logger, fn any, params ...any) any {
|
|
defer Recover(logger)
|
|
|
|
startTime := requestStartTime(requestLogger != nil)
|
|
fnValue := reflect.ValueOf(fn)
|
|
fnType := fnValue.Type()
|
|
if fnType.Kind() != reflect.Func {
|
|
panic("fn must be a function")
|
|
}
|
|
|
|
args := make([]reflect.Value, len(params))
|
|
for i, param := range params {
|
|
args[i] = reflect.ValueOf(param)
|
|
}
|
|
|
|
results := fnValue.Call(args)
|
|
|
|
var resp any
|
|
|
|
if len(results) > 0 {
|
|
resp = results[0].Interface()
|
|
}
|
|
|
|
if requestLogger != nil {
|
|
methodName := getShortFunctionName(fn)
|
|
Log(requestLogger, methodName, params, resp, startTime)
|
|
}
|
|
|
|
return resp
|
|
}
|
|
|
|
func CallWithResponse(logger, requestLogger *zap.Logger, fn any, params ...any) string {
|
|
resp := Call(logger, requestLogger, fn, params...)
|
|
if resp == nil {
|
|
return ""
|
|
}
|
|
return resp.(string)
|
|
}
|
|
|
|
func removeSensitiveInfo(jsonStr string) string {
|
|
// see related test for the usage of this function
|
|
return sensitiveRegex.ReplaceAllStringFunc(jsonStr, func(match string) string {
|
|
parts := sensitiveRegex.FindStringSubmatch(match)
|
|
return fmt.Sprintf(`%s:"***"`, parts[1])
|
|
})
|
|
}
|
|
|
|
func requestStartTime(enabled bool) time.Time {
|
|
if !enabled {
|
|
return time.Time{}
|
|
}
|
|
return time.Now()
|
|
}
|
|
|
|
func Recover(logger *zap.Logger) {
|
|
err := recover()
|
|
if err == nil {
|
|
return
|
|
}
|
|
|
|
logger.Error("panic found in call",
|
|
zap.Any("error", err),
|
|
zap.Stack("stacktrace"))
|
|
|
|
sentry.RecoverError(err)
|
|
|
|
panic(err)
|
|
}
|
|
|
|
func Log(logger *zap.Logger, method string, params any, resp any, startTime time.Time) {
|
|
if logger == nil {
|
|
return
|
|
}
|
|
duration := time.Since(startTime)
|
|
logger.Debug("call",
|
|
zap.String("method", method),
|
|
zap.Duration("duration", duration),
|
|
dataField("request", params),
|
|
dataField("response", resp),
|
|
)
|
|
}
|
|
|
|
func dataField(name string, data any) zap.Field {
|
|
dataString := removeSensitiveInfo(fmt.Sprintf("%+v", data))
|
|
var paramsParsed any
|
|
if json.Unmarshal([]byte(dataString), ¶msParsed) == nil {
|
|
return zap.Any(name, paramsParsed)
|
|
}
|
|
return zap.String(name, dataString)
|
|
}
|