diff --git a/gateway/interx/block.go b/gateway/interx/block.go old mode 100644 new mode 100755 index a4c6942..251d736 --- a/gateway/interx/block.go +++ b/gateway/interx/block.go @@ -9,6 +9,7 @@ import ( "github.com/KiraCore/interx/common" "github.com/KiraCore/interx/config" + "github.com/KiraCore/interx/log" "github.com/KiraCore/interx/types" kiratypes "github.com/KiraCore/sekai/types" multistaking "github.com/KiraCore/sekai/x/multistaking/types" @@ -61,12 +62,19 @@ func queryBlocksHandle(rpcAddr string, r *http.Request) (interface{}, interface{ func QueryBlocksRequest(gwCosmosmux *runtime.ServeMux, rpcAddr string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var statusCode int + + log.CustomLogger().Info("Starting 'QueryBlocksRequest' request...") + request := common.GetInterxRequest(r) response := common.GetResponseFormat(request, rpcAddr) - common.GetLogger().Info("[query-blocks] Entering Blocks query") - if !common.RPCMethods["GET"][config.QueryBlocks].Enabled { + + log.CustomLogger().Error(" `QueryBlocksRequest` is disabled.", + "method", request.Method, + "endpoint", request.Endpoint, + ) + response.Response, response.Error, statusCode = common.ServeError(0, "", "API disabled", http.StatusForbidden) } else { if common.RPCMethods["GET"][config.QueryBlocks].CachingEnabled { @@ -75,7 +83,13 @@ func QueryBlocksRequest(gwCosmosmux *runtime.ServeMux, rpcAddr string) http.Hand response.Response, response.Error, statusCode = cacheResponse, cacheError, cacheStatus common.WrapResponse(w, request, *response, statusCode, false) - common.GetLogger().Info("[query-blocks] Returning from the cache") + log.CustomLogger().Info("Cache hit for 'QueryBlocksRequest' request.", + "method", request.Method, + "endpoint", request.Endpoint, + "params", request.Params, + "error", response.Error, + ) + return } } @@ -83,7 +97,16 @@ func QueryBlocksRequest(gwCosmosmux *runtime.ServeMux, rpcAddr string) http.Hand response.Response, response.Error, statusCode = queryBlocksHandle(rpcAddr, r) } + log.CustomLogger().Info("Processed 'QueryBlocksRequest' request.", + "method", request.Method, + "endpoint", request.Endpoint, + "params", request.Params, + "error", response.Error, + ) + common.WrapResponse(w, request, *response, statusCode, common.RPCMethods["GET"][config.QueryBlocks].CachingEnabled) + + log.CustomLogger().Info("Finished 'QueryBlocksRequest' request.") } } @@ -91,6 +114,11 @@ func queryBlockByHeightOrHashHandle(rpcAddr string, height string) (interface{}, success, err, statusCode := common.MakeTendermintRPCRequest(rpcAddr, "/block", fmt.Sprintf("height=%s", height)) if err != nil { + log.CustomLogger().Error(" `queryBlockByHeightOrHashHandle` failed to execute.", + "height", height, + "method", "/block", + "err", err, + ) success, err, statusCode = common.MakeTendermintRPCRequest(rpcAddr, "/block_by_hash", fmt.Sprintf("hash=%s", height)) } @@ -106,9 +134,17 @@ func QueryBlockByHeightOrHashRequest(gwCosmosmux *runtime.ServeMux, rpcAddr stri request := common.GetInterxRequest(r) response := common.GetResponseFormat(request, rpcAddr) - common.GetLogger().Info("[query-blocks-by-height] Entering Block query by height: ", height) + log.CustomLogger().Info("Starting `QueryBlockByHeightOrHashRequest` request...") if !common.RPCMethods["GET"][config.QueryBlockByHeightOrHash].Enabled { + + log.CustomLogger().Error("Query `QueryBlockByHeightOrHashRequest` is disabled.", + "method", request.Method, + "endpoint", request.Endpoint, + "params", request.Params, + "error", response.Error, + ) + response.Response, response.Error, statusCode = common.ServeError(0, "", "API disabled", http.StatusForbidden) } else { if common.RPCMethods["GET"][config.QueryBlockByHeightOrHash].CachingEnabled { @@ -117,7 +153,13 @@ func QueryBlockByHeightOrHashRequest(gwCosmosmux *runtime.ServeMux, rpcAddr stri response.Response, response.Error, statusCode = cacheResponse, cacheError, cacheStatus common.WrapResponse(w, request, *response, statusCode, false) - common.GetLogger().Info("[query-blocks-by-height] Returning from the cache: ", height) + log.CustomLogger().Info("Cache hit for `QueryBlockByHeightOrHashRequest` request.", + "method", request.Method, + "endpoint", request.Endpoint, + "params", request.Params, + "error", response.Error, + ) + return } } @@ -125,13 +167,25 @@ func QueryBlockByHeightOrHashRequest(gwCosmosmux *runtime.ServeMux, rpcAddr stri response.Response, response.Error, statusCode = queryBlockByHeightOrHashHandle(rpcAddr, height) } + log.CustomLogger().Info("Processed `QueryBlockByHeightOrHashRequest` request.", + "method", request.Method, + "endpoint", request.Endpoint, + "params", request.Params, + "error", response.Error, + ) + common.WrapResponse(w, request, *response, statusCode, common.RPCMethods["GET"][config.QueryBlockByHeightOrHash].CachingEnabled) + + log.CustomLogger().Info("Finished `QueryBlockByHeightOrHashRequest` request.") + } } func getTransactionsFromLog(attributes []abciTypes.EventAttribute) []sdk.Coin { feeTxs := []sdk.Coin{} + log.CustomLogger().Info("Starting `getTransactionsFromLog` request...") + var evMap = make(map[string]string) for _, attribute := range attributes { key := string(attribute.GetKey()) @@ -164,15 +218,26 @@ func getTransactionsFromLog(attributes []abciTypes.EventAttribute) []sdk.Coin { } } + log.CustomLogger().Info("Finished `getTransactionsFromLog` request.") + return feeTxs } func parseTransaction(rpcAddr string, transaction tmTypes.ResultTx) (types.TransactionResult, error) { txResult := types.TransactionResult{} + log.CustomLogger().Info("Starting `parseTransaction` request...") + tx, err := config.EncodingCg.TxConfig.TxDecoder()(transaction.Tx) if err != nil { - common.GetLogger().Error("[query-transactions] Failed to decode transaction: ", err) + + log.CustomLogger().Error("Failed to decode transaction.", + "method", "TxDecoder", + "transaction", tx, + "error", err, + "result", txResult, + ) + return txResult, err } @@ -185,8 +250,14 @@ func parseTransaction(rpcAddr string, transaction tmTypes.ResultTx) (types.Trans txResult.BlockHeight = transaction.Height txResult.BlockTimestamp, err = common.GetBlockTime(rpcAddr, transaction.Height) if err != nil { - common.GetLogger().Error("[query-transactions] Block not found: ", transaction.Height) - return txResult, fmt.Errorf("block not found: %d", transaction.Height) + + log.CustomLogger().Error("Failed to find block.", + "method", "GetBlockTime", + "RPC", rpcAddr, + "height", transaction.Height, + "error", err, + ) + } txResult.Confirmation = common.NodeStatus.Block - transaction.Height + 1 txResult.GasWanted = transaction.TxResult.GetGasWanted() @@ -205,6 +276,11 @@ func parseTransaction(rpcAddr string, transaction tmTypes.ResultTx) (types.Trans }) } + log.CustomLogger().Info("Signing tx successfully done.", + "method", "signing.Tx", + "signed tx", txSigning, + ) + txResult.Transactions = []types.Transaction{} txResult.Fees = []sdk.Coin{} @@ -413,8 +489,11 @@ func parseTransaction(rpcAddr string, transaction tmTypes.ResultTx) (types.Trans } txResult.Transactions = append(txResult.Transactions, transfers...) + } + log.CustomLogger().Info("Finished `parseTransaction` request.") + return txResult, nil } @@ -422,7 +501,13 @@ func parseTransaction(rpcAddr string, transaction tmTypes.ResultTx) (types.Trans func QueryBlockTransactionsHandle(rpcAddr string, height string) (interface{}, interface{}, int) { blockHeight, _ := strconv.Atoi(height) response, err := SearchTxHashHandle(rpcAddr, "", "", "", 0, 0, int64(blockHeight), int64(blockHeight), "") + if err != nil { + log.CustomLogger().Error("`QueryBlockTransactionsHandle` failed to execute.", + "block_height", height, + "error", err, + "response_data", response, + ) return common.ServeError(0, "transaction query failed", "", http.StatusBadRequest) } @@ -449,12 +534,27 @@ func QueryBlockTransactionsRequest(gwCosmosmux *runtime.ServeMux, rpcAddr string var statusCode int queries := mux.Vars(r) height := queries["height"] + + log.CustomLogger().Info("Starting `QueryBlockTransactionsRequest`.", + "block_height", height, + ) + request := common.GetInterxRequest(r) response := common.GetResponseFormat(request, rpcAddr) - common.GetLogger().Info("[query-block-transactions-by-height] Entering Block query by height: ", height) + log.CustomLogger().Info("Attempting to fetch transactions from block.", + "block_height", height, + "method", request.Method, + "endpoint", request.Endpoint, + "params", request.Params, + ) if !common.RPCMethods["GET"][config.QueryBlockTransactions].Enabled { + + log.CustomLogger().Error("`QueryBlockTransactionsRequest` is disabled.", + "block_height", height, + ) + response.Response, response.Error, statusCode = common.ServeError(0, "", "API disabled", http.StatusForbidden) } else { if common.RPCMethods["GET"][config.QueryBlockTransactions].CachingEnabled { @@ -463,7 +563,10 @@ func QueryBlockTransactionsRequest(gwCosmosmux *runtime.ServeMux, rpcAddr string response.Response, response.Error, statusCode = cacheResponse, cacheError, cacheStatus common.WrapResponse(w, request, *response, statusCode, false) - common.GetLogger().Info("[query-block-transactions-by-height] Returning from the cache: %s", height) + log.CustomLogger().Info("Cache hit for `QueryBlockTransactionsRequest`.", + "block_height", height, + ) + return } } @@ -471,7 +574,17 @@ func QueryBlockTransactionsRequest(gwCosmosmux *runtime.ServeMux, rpcAddr string response.Response, response.Error, statusCode = QueryBlockTransactionsHandle(rpcAddr, height) } + log.CustomLogger().Info("Fetched transaction from block (no cache used).", + "block_height", height, + "transaction_response", response.Response, + "error_details", response.Error, + ) common.WrapResponse(w, request, *response, statusCode, common.RPCMethods["GET"][config.QueryBlockTransactions].CachingEnabled) + + log.CustomLogger().Info("Completed `QueryBlockTransactionsRequest`.", + "block_height", height, + "status_code", statusCode, + ) } } @@ -481,6 +594,11 @@ func QueryTransactionResultHandle(rpcAddr string, txHash string) (interface{}, i response, err := SearchTxHashHandle(rpcAddr, "", "", "", 0, 0, 0, 0, txHash) if err != nil { + log.CustomLogger().Error("`QueryTransactionResultHandle` failed to execute.", + "tx_Hash", txHash, + "error", err, + "response_data", response, + ) return common.ServeError(0, "transaction query failed", "", http.StatusBadRequest) } @@ -489,6 +607,11 @@ func QueryTransactionResultHandle(rpcAddr string, txHash string) (interface{}, i for _, transaction := range response.Txs { txResult, err = parseTransaction(rpcAddr, *transaction) if err != nil { + log.CustomLogger().Error("`parseTransaction` failed to execute.", + "tx_Result", txResult, + "error", err, + "response_data", response.Txs, + ) return common.ServeError(0, "", err.Error(), http.StatusBadRequest) } } @@ -502,12 +625,24 @@ func QueryTransactionResultRequest(gwCosmosmux *runtime.ServeMux, rpcAddr string var statusCode int queries := mux.Vars(r) txHash := queries["txHash"] + + log.CustomLogger().Info("Starting `QueryTransactionResultRequest` request...") + request := common.GetInterxRequest(r) response := common.GetResponseFormat(request, rpcAddr) - common.GetLogger().Info("[query-transaction-by-hash] Entering transaction query by hash: %s", txHash) + log.CustomLogger().Info("Attempting to fetch transactions by hash.", + "tx_Hash", txHash, + "method", request.Method, + "endpoint", request.Endpoint, + "params", request.Params, + ) if !common.RPCMethods["GET"][config.QueryTransactionResult].Enabled { + + log.CustomLogger().Error("`QueryTransactionResultRequest` is disabled.", + "tx_Hash", txHash, + ) response.Response, response.Error, statusCode = common.ServeError(0, "", "API disabled", http.StatusForbidden) } else { if common.RPCMethods["GET"][config.QueryTransactionResult].CachingEnabled { @@ -516,14 +651,26 @@ func QueryTransactionResultRequest(gwCosmosmux *runtime.ServeMux, rpcAddr string response.Response, response.Error, statusCode = cacheResponse, cacheError, cacheStatus common.WrapResponse(w, request, *response, statusCode, false) - common.GetLogger().Info("[query-transaction-by-hash] Returning from the cache: %s", txHash) + log.CustomLogger().Info("Cache hit for `QueryTransactionResultRequest`.", + "tx_Hash", txHash, + ) return } } response.Response, response.Error, statusCode = QueryTransactionResultHandle(rpcAddr, txHash) + log.CustomLogger().Info("Fetched transaction by hash (no cache used).", + "tx_Hash", txHash, + "transaction_response", response.Response, + "error_details", response.Error, + ) } common.WrapResponse(w, request, *response, statusCode, common.RPCMethods["GET"][config.QueryTransactionResult].CachingEnabled) + + log.CustomLogger().Info("Completed `QueryBlockTransactionsRequest`.", + "tx_Hash", txHash, + "status_code", statusCode, + ) } } diff --git a/log/logger.go b/log/logger.go new file mode 100755 index 0000000..a78beb9 --- /dev/null +++ b/log/logger.go @@ -0,0 +1,31 @@ +package log + +import ( + "fmt" + "os" + "strconv" + "time" + + cosmosLog "cosmossdk.io/log" +) + +func CustomLogger() cosmosLog.Logger { + + printLogs, err := strconv.ParseBool(os.Getenv("PrintLogs")) + if err != nil { + fmt.Println("[CustomLogger] Error parsing PrintLogs environment variable:", err) + } + + if !printLogs { + return cosmosLog.NewNopLogger() + } + + // Initialize a new `cosmosLog` logger instance + logger := cosmosLog.NewLogger(os.Stderr) + + logger = logger.With( + "timestamp", time.Now().UTC().Format(time.RFC3339), + ) + + return logger +} diff --git a/log/monitor.go b/log/monitor.go new file mode 100755 index 0000000..68288d0 --- /dev/null +++ b/log/monitor.go @@ -0,0 +1,54 @@ +package log + +import ( + "fmt" + "os" + "runtime" + "strconv" + "time" + + "github.com/shirou/gopsutil/cpu" + "github.com/shirou/gopsutil/load" + "github.com/shirou/gopsutil/mem" + "github.com/sirupsen/logrus" +) + +var log = logrus.New() + +// Monitor will continuously collect system information +func Monitor(interval time.Duration) { + + printLogs, err := strconv.ParseBool(os.Getenv("PrintLogs")) + + if err != nil { + fmt.Println("[CustomLogger] Error parsing PrintLogs environment variable:", err) + } + + if printLogs { + for { + var memStats runtime.MemStats + runtime.ReadMemStats(&memStats) + + v, _ := mem.VirtualMemory() + cpuPercent, _ := cpu.Percent(0, true) + loadAvg, _ := load.Avg() + + log.Printf("Timestamp: %v", time.Now().Format(time.RFC3339)) + + log.Printf("Memory Total: %v, Free: %v, Used Percent: %.2f%%, Active: %v", + v.Total, v.Free, v.UsedPercent, v.Active) + + log.Printf("CPU Usage Percentage: %v%%, CPU Usage: %v", cpuPercent, runtime.NumCPU()) + + // It logs the system load average for 1, 5, and 15 minutes. + log.Printf("Load Average: 1m: %.2f, 5m: %.2f, 15m: %.2f", + loadAvg.Load1, loadAvg.Load5, loadAvg.Load15) + + log.Printf("Memory Alloc: %v, Total Memory Alloc: %v, System Memory: %v", + memStats.Alloc, memStats.TotalAlloc, memStats.Sys) + + time.Sleep(interval) + } + } + +} diff --git a/log/recover.go b/log/recover.go new file mode 100755 index 0000000..d87cd61 --- /dev/null +++ b/log/recover.go @@ -0,0 +1,14 @@ +package log + +import ( + "github.com/sirupsen/logrus" +) + +var logger = logrus.New() + +// Recovery function to handle panics +func RecoverFromPanic() { + if r := recover(); r != nil { + logger.Errorf("Application crashed: %v", r) + } +} diff --git a/main.go b/main.go old mode 100644 new mode 100755 index 9ab2f62..a2b2bd6 --- a/main.go +++ b/main.go @@ -4,15 +4,20 @@ import ( "flag" "fmt" "os" + "time" "github.com/KiraCore/interx/common" "github.com/KiraCore/interx/config" "github.com/KiraCore/interx/gateway" + "github.com/KiraCore/interx/log" _ "github.com/KiraCore/interx/statik" "github.com/tyler-smith/go-bip39" "google.golang.org/grpc/grpclog" ) +// Enable or disable logging by setting the environment variable +const ENABLE_LOGS = "true" + func printUsage() { fmt.Println("Interx Daemon (server)") fmt.Println() @@ -35,6 +40,16 @@ func printUsage() { } func main() { + // Set the "PrintLogs" environment variable based on the logging configuration + os.Setenv("PrintLogs", ENABLE_LOGS) + + log.CustomLogger().Info("Starting INTERX server.") + + defer log.RecoverFromPanic() // Ensure we recover from any panic + + // Monitor system resources + go log.Monitor(25 * time.Second) + initCommand := flag.NewFlagSet("init", flag.ExitOnError) startCommand := flag.NewFlagSet("start", flag.ExitOnError) versionCommand := flag.NewFlagSet("version", flag.ExitOnError) @@ -98,8 +113,12 @@ func main() { // os.Args[2:] will be all arguments starting after the subcommand at os.Args[1] switch os.Args[1] { case "init": + + log.CustomLogger().Info("Initializing server with 'interxd init' command.") + err := initCommand.Parse(os.Args[2:]) if err != nil { + log.CustomLogger().Error("Failed to initialize server with 'interxd init' command.") panic(err) } @@ -159,12 +178,16 @@ func main() { *initSnapShotInterval, ) - fmt.Printf("Created interx configuration file: %s\n", *initHomePtr+"/config.json") + log.CustomLogger().Info("Created interx configuration file.", "Config File Path", *initHomePtr+"/config.json") return } case "start": + + log.CustomLogger().Info("Starting server with 'interxd start' command.") + err := startCommand.Parse(os.Args[2:]) if err != nil { + log.CustomLogger().Error("Failed to start INTERX server.") panic(err) } @@ -172,7 +195,7 @@ func main() { // Check which subcommand was Parsed using the FlagSet.Parsed() function. Handle each case accordingly. // FlagSet.Parse() will evaluate to false if no flags were parsed (i.e. the user did not provide any flags) configFilePath := *startHomePtr + "/config.json" - fmt.Println("configFilePath", configFilePath) + log.CustomLogger().Info("Config Path", configFilePath) // Adds gRPC internal logs. This is quite verbose, so adjust as desired! log := common.GetLogger() @@ -184,17 +207,21 @@ func main() { return } case "version": + + log.CustomLogger().Info("Fetching version with 'interxd version' command.") + err := versionCommand.Parse(os.Args[2:]) if err != nil { + log.CustomLogger().Error("Error executing 'interxd version' command: Failed to fetch version information.") panic(err) } if versionCommand.Parsed() { - fmt.Println(config.InterxVersion) + log.CustomLogger().Info("Interx Version", config.InterxVersion) return } default: - fmt.Println("init or start command is available.") + log.CustomLogger().Error("Server did not initialized and started.") os.Exit(1) } }