From 8ee590794f6b1e14e3b2ee151044df12e8a46248 Mon Sep 17 00:00:00 2001
From: Anton Nikiforov <an.nikiforov@yadro.com>
Date: Tue, 27 Dec 2022 12:36:30 +0300
Subject: [PATCH] [#1962] cli: `common.PrintVerbose` prints via
 `cobra.Command.Printf`

Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
---
 CHANGELOG.md                                  |  2 +
 cmd/frostfs-cli/internal/client/sdk.go        | 14 ++--
 cmd/frostfs-cli/internal/common/eacl.go       |  6 +-
 cmd/frostfs-cli/internal/common/json.go       |  4 +-
 cmd/frostfs-cli/internal/common/token.go      | 16 ++---
 cmd/frostfs-cli/internal/common/verbose.go    |  5 +-
 cmd/frostfs-cli/internal/key/key_test.go      | 13 +++-
 cmd/frostfs-cli/internal/key/raw.go           | 12 ++--
 cmd/frostfs-cli/internal/key/wallet.go        | 21 +++---
 cmd/frostfs-cli/modules/bearer/create.go      |  2 +-
 cmd/frostfs-cli/modules/container/create.go   | 10 +--
 cmd/frostfs-cli/modules/container/delete.go   | 12 ++--
 cmd/frostfs-cli/modules/container/get.go      | 15 +---
 cmd/frostfs-cli/modules/container/util.go     | 10 +--
 .../modules/control/set_netmap_status.go      |  4 +-
 cmd/frostfs-cli/modules/object/lock.go        |  4 +-
 cmd/frostfs-cli/modules/object/util.go        | 72 ++++++++++---------
 cmd/frostfs-cli/modules/root.go               |  2 +-
 cmd/frostfs-cli/modules/session/create.go     |  2 +-
 cmd/frostfs-cli/modules/tree/add_by_path.go   |  2 +-
 cmd/frostfs-cli/modules/tree/get_by_path.go   |  2 +-
 cmd/frostfs-cli/modules/util/convert_eacl.go  | 13 +---
 cmd/frostfs-cli/modules/util/sign_bearer.go   |  3 +-
 cmd/frostfs-cli/modules/util/sign_session.go  |  4 +-
 24 files changed, 116 insertions(+), 134 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2d464585ac..01692e4d0d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,8 @@ Changelog for FrostFS Node
 - Doc for extended headers (#2128)
 
 ### Changed
+- `common.PrintVerbose` prints via `cobra.Command.Printf` (#1962)
+
 ### Fixed
 - Big object removal with non-local parts (#1978)
 - Disable pilorama when moving to degraded mode (#2197)
diff --git a/cmd/frostfs-cli/internal/client/sdk.go b/cmd/frostfs-cli/internal/client/sdk.go
index 853bc979ad..b81a0837b6 100644
--- a/cmd/frostfs-cli/internal/client/sdk.go
+++ b/cmd/frostfs-cli/internal/client/sdk.go
@@ -21,25 +21,25 @@ var errInvalidEndpoint = errors.New("provided RPC endpoint is incorrect")
 // GetSDKClientByFlag returns default frostfs-sdk-go client using the specified flag for the address.
 // On error, outputs to stderr of cmd and exits with non-zero code.
 func GetSDKClientByFlag(cmd *cobra.Command, key *ecdsa.PrivateKey, endpointFlag string) *client.Client {
-	cli, err := getSDKClientByFlag(key, endpointFlag)
+	cli, err := getSDKClientByFlag(cmd, key, endpointFlag)
 	if err != nil {
 		common.ExitOnErr(cmd, "can't create API client: %w", err)
 	}
 	return cli
 }
 
-func getSDKClientByFlag(key *ecdsa.PrivateKey, endpointFlag string) (*client.Client, error) {
+func getSDKClientByFlag(cmd *cobra.Command, key *ecdsa.PrivateKey, endpointFlag string) (*client.Client, error) {
 	var addr network.Address
 
 	err := addr.FromString(viper.GetString(endpointFlag))
 	if err != nil {
 		return nil, fmt.Errorf("%v: %w", errInvalidEndpoint, err)
 	}
-	return GetSDKClient(key, addr)
+	return GetSDKClient(cmd, key, addr)
 }
 
 // GetSDKClient returns default frostfs-sdk-go client.
-func GetSDKClient(key *ecdsa.PrivateKey, addr network.Address) (*client.Client, error) {
+func GetSDKClient(cmd *cobra.Command, key *ecdsa.PrivateKey, addr network.Address) (*client.Client, error) {
 	var (
 		c       client.Client
 		prmInit client.PrmInit
@@ -56,7 +56,7 @@ func GetSDKClient(key *ecdsa.PrivateKey, addr network.Address) (*client.Client,
 		prmDial.SetTimeout(timeout)
 		prmDial.SetStreamTimeout(timeout)
 
-		common.PrintVerbose("Set request timeout to %s.", timeout)
+		common.PrintVerbose(cmd, "Set request timeout to %s.", timeout)
 	}
 
 	c.Init(prmInit)
@@ -69,7 +69,7 @@ func GetSDKClient(key *ecdsa.PrivateKey, addr network.Address) (*client.Client,
 }
 
 // GetCurrentEpoch returns current epoch.
-func GetCurrentEpoch(ctx context.Context, endpoint string) (uint64, error) {
+func GetCurrentEpoch(ctx context.Context, cmd *cobra.Command, endpoint string) (uint64, error) {
 	var addr network.Address
 
 	if err := addr.FromString(endpoint); err != nil {
@@ -81,7 +81,7 @@ func GetCurrentEpoch(ctx context.Context, endpoint string) (uint64, error) {
 		return 0, fmt.Errorf("can't generate key to sign query: %w", err)
 	}
 
-	c, err := GetSDKClient(key, addr)
+	c, err := GetSDKClient(cmd, key, addr)
 	if err != nil {
 		return 0, err
 	}
diff --git a/cmd/frostfs-cli/internal/common/eacl.go b/cmd/frostfs-cli/internal/common/eacl.go
index 6573742e4a..77f296374a 100644
--- a/cmd/frostfs-cli/internal/common/eacl.go
+++ b/cmd/frostfs-cli/internal/common/eacl.go
@@ -19,7 +19,7 @@ func ReadEACL(cmd *cobra.Command, eaclPath string) *eacl.Table {
 		ExitOnErr(cmd, "", errors.New("incorrect path to file with EACL"))
 	}
 
-	PrintVerbose("Reading EACL from file: %s", eaclPath)
+	PrintVerbose(cmd, "Reading EACL from file: %s", eaclPath)
 
 	data, err := os.ReadFile(eaclPath)
 	ExitOnErr(cmd, "can't read file with EACL: %w", err)
@@ -28,13 +28,13 @@ func ReadEACL(cmd *cobra.Command, eaclPath string) *eacl.Table {
 
 	if err = table.UnmarshalJSON(data); err == nil {
 		validateAndFixEACLVersion(table)
-		PrintVerbose("Parsed JSON encoded EACL table")
+		PrintVerbose(cmd, "Parsed JSON encoded EACL table")
 		return table
 	}
 
 	if err = table.Unmarshal(data); err == nil {
 		validateAndFixEACLVersion(table)
-		PrintVerbose("Parsed binary encoded EACL table")
+		PrintVerbose(cmd, "Parsed binary encoded EACL table")
 		return table
 	}
 
diff --git a/cmd/frostfs-cli/internal/common/json.go b/cmd/frostfs-cli/internal/common/json.go
index 5664b185df..02b7db60a3 100644
--- a/cmd/frostfs-cli/internal/common/json.go
+++ b/cmd/frostfs-cli/internal/common/json.go
@@ -11,12 +11,12 @@ import (
 func PrettyPrintJSON(cmd *cobra.Command, m json.Marshaler, entity string) {
 	data, err := m.MarshalJSON()
 	if err != nil {
-		PrintVerbose("Can't convert %s to json: %w", entity, err)
+		PrintVerbose(cmd, "Can't convert %s to json: %w", entity, err)
 		return
 	}
 	buf := new(bytes.Buffer)
 	if err := json.Indent(buf, data, "", "  "); err != nil {
-		PrintVerbose("Can't pretty print json: %w", err)
+		PrintVerbose(cmd, "Can't pretty print json: %w", err)
 		return
 	}
 	cmd.Println(buf)
diff --git a/cmd/frostfs-cli/internal/common/token.go b/cmd/frostfs-cli/internal/common/token.go
index d2f238636b..5401e512bc 100644
--- a/cmd/frostfs-cli/internal/common/token.go
+++ b/cmd/frostfs-cli/internal/common/token.go
@@ -19,11 +19,11 @@ func ReadBearerToken(cmd *cobra.Command, flagname string) *bearer.Token {
 		return nil
 	}
 
-	PrintVerbose("Reading bearer token from file [%s]...", path)
+	PrintVerbose(cmd, "Reading bearer token from file [%s]...", path)
 
 	var tok bearer.Token
 
-	err = ReadBinaryOrJSON(&tok, path)
+	err = ReadBinaryOrJSON(cmd, &tok, path)
 	ExitOnErr(cmd, "invalid bearer token: %v", err)
 
 	return &tok
@@ -38,8 +38,8 @@ type BinaryOrJSON interface {
 
 // ReadBinaryOrJSON reads file data using provided path and decodes
 // BinaryOrJSON from the data.
-func ReadBinaryOrJSON(dst BinaryOrJSON, fPath string) error {
-	PrintVerbose("Reading file [%s]...", fPath)
+func ReadBinaryOrJSON(cmd *cobra.Command, dst BinaryOrJSON, fPath string) error {
+	PrintVerbose(cmd, "Reading file [%s]...", fPath)
 
 	// try to read session token from file
 	data, err := os.ReadFile(fPath)
@@ -47,17 +47,17 @@ func ReadBinaryOrJSON(dst BinaryOrJSON, fPath string) error {
 		return fmt.Errorf("read file <%s>: %w", fPath, err)
 	}
 
-	PrintVerbose("Trying to decode binary...")
+	PrintVerbose(cmd, "Trying to decode binary...")
 
 	err = dst.Unmarshal(data)
 	if err != nil {
-		PrintVerbose("Failed to decode binary: %v", err)
+		PrintVerbose(cmd, "Failed to decode binary: %v", err)
 
-		PrintVerbose("Trying to decode JSON...")
+		PrintVerbose(cmd, "Trying to decode JSON...")
 
 		err = dst.UnmarshalJSON(data)
 		if err != nil {
-			PrintVerbose("Failed to decode JSON: %v", err)
+			PrintVerbose(cmd, "Failed to decode JSON: %v", err)
 			return errors.New("invalid format")
 		}
 	}
diff --git a/cmd/frostfs-cli/internal/common/verbose.go b/cmd/frostfs-cli/internal/common/verbose.go
index 92d9651dc0..39e99d2d9d 100644
--- a/cmd/frostfs-cli/internal/common/verbose.go
+++ b/cmd/frostfs-cli/internal/common/verbose.go
@@ -2,7 +2,6 @@ package common
 
 import (
 	"encoding/hex"
-	"fmt"
 	"strconv"
 	"time"
 
@@ -13,9 +12,9 @@ import (
 )
 
 // PrintVerbose prints to the stdout if the commonflags.Verbose flag is on.
-func PrintVerbose(format string, a ...interface{}) {
+func PrintVerbose(cmd *cobra.Command, format string, a ...interface{}) {
 	if viper.GetBool(commonflags.Verbose) {
-		fmt.Printf(format+"\n", a...)
+		cmd.Printf(format+"\n", a...)
 	}
 }
 
diff --git a/cmd/frostfs-cli/internal/key/key_test.go b/cmd/frostfs-cli/internal/key/key_test.go
index f145150568..563cd95457 100644
--- a/cmd/frostfs-cli/internal/key/key_test.go
+++ b/cmd/frostfs-cli/internal/key/key_test.go
@@ -11,11 +11,18 @@ import (
 	"github.com/nspcc-dev/neo-go/cli/input"
 	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
 	"github.com/nspcc-dev/neo-go/pkg/wallet"
+	"github.com/spf13/cobra"
 	"github.com/spf13/viper"
 	"github.com/stretchr/testify/require"
 	"golang.org/x/term"
 )
 
+var testCmd = &cobra.Command{
+	Use:   "test",
+	Short: "test",
+	Run:   func(cmd *cobra.Command, args []string) {},
+}
+
 func Test_getOrGenerate(t *testing.T) {
 	dir := t.TempDir()
 
@@ -96,7 +103,7 @@ func Test_getOrGenerate(t *testing.T) {
 
 	t.Run("generate", func(t *testing.T) {
 		viper.Set(commonflags.GenerateKey, true)
-		actual, err := getOrGenerate()
+		actual, err := getOrGenerate(testCmd)
 		require.NoError(t, err)
 		require.NotNil(t, actual)
 		for _, p := range []*keys.PrivateKey{nep2Key, rawKey, wifKey, acc1.PrivateKey(), acc2.PrivateKey()} {
@@ -107,13 +114,13 @@ func Test_getOrGenerate(t *testing.T) {
 
 func checkKeyError(t *testing.T, desc string, err error) {
 	viper.Set(commonflags.WalletPath, desc)
-	_, actualErr := getOrGenerate()
+	_, actualErr := getOrGenerate(testCmd)
 	require.ErrorIs(t, actualErr, err)
 }
 
 func checkKey(t *testing.T, desc string, expected *keys.PrivateKey) {
 	viper.Set(commonflags.WalletPath, desc)
-	actual, err := getOrGenerate()
+	actual, err := getOrGenerate(testCmd)
 	require.NoError(t, err)
 	require.Equal(t, &expected.PrivateKey, actual)
 }
diff --git a/cmd/frostfs-cli/internal/key/raw.go b/cmd/frostfs-cli/internal/key/raw.go
index 74d373b8a6..f467ca95f6 100644
--- a/cmd/frostfs-cli/internal/key/raw.go
+++ b/cmd/frostfs-cli/internal/key/raw.go
@@ -20,12 +20,12 @@ var errCantGenerateKey = errors.New("can't generate new private key")
 // Ideally we want to touch file-system on the last step.
 // This function assumes that all flags were bind to viper in a `PersistentPreRun`.
 func Get(cmd *cobra.Command) *ecdsa.PrivateKey {
-	pk, err := get()
+	pk, err := get(cmd)
 	common.ExitOnErr(cmd, "can't fetch private key: %w", err)
 	return pk
 }
 
-func get() (*ecdsa.PrivateKey, error) {
+func get(cmd *cobra.Command) (*ecdsa.PrivateKey, error) {
 	keyDesc := viper.GetString(commonflags.WalletPath)
 	data, err := os.ReadFile(keyDesc)
 	if err != nil {
@@ -36,7 +36,7 @@ func get() (*ecdsa.PrivateKey, error) {
 	if err != nil {
 		w, err := wallet.NewWalletFromFile(keyDesc)
 		if err == nil {
-			return FromWallet(w, viper.GetString(commonflags.Account))
+			return FromWallet(cmd, w, viper.GetString(commonflags.Account))
 		}
 		return nil, fmt.Errorf("%w: %v", ErrInvalidKey, err)
 	}
@@ -45,12 +45,12 @@ func get() (*ecdsa.PrivateKey, error) {
 
 // GetOrGenerate is similar to get but generates a new key if commonflags.GenerateKey is set.
 func GetOrGenerate(cmd *cobra.Command) *ecdsa.PrivateKey {
-	pk, err := getOrGenerate()
+	pk, err := getOrGenerate(cmd)
 	common.ExitOnErr(cmd, "can't fetch private key: %w", err)
 	return pk
 }
 
-func getOrGenerate() (*ecdsa.PrivateKey, error) {
+func getOrGenerate(cmd *cobra.Command) (*ecdsa.PrivateKey, error) {
 	if viper.GetBool(commonflags.GenerateKey) {
 		priv, err := keys.NewPrivateKey()
 		if err != nil {
@@ -58,5 +58,5 @@ func getOrGenerate() (*ecdsa.PrivateKey, error) {
 		}
 		return &priv.PrivateKey, nil
 	}
-	return get()
+	return get(cmd)
 }
diff --git a/cmd/frostfs-cli/internal/key/wallet.go b/cmd/frostfs-cli/internal/key/wallet.go
index ace54e4f66..26178c27d8 100644
--- a/cmd/frostfs-cli/internal/key/wallet.go
+++ b/cmd/frostfs-cli/internal/key/wallet.go
@@ -3,13 +3,14 @@ package key
 import (
 	"crypto/ecdsa"
 	"errors"
-	"fmt"
 
+	"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
 	"github.com/nspcc-dev/neo-go/cli/flags"
 	"github.com/nspcc-dev/neo-go/cli/input"
 	"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
 	"github.com/nspcc-dev/neo-go/pkg/util"
 	"github.com/nspcc-dev/neo-go/pkg/wallet"
+	"github.com/spf13/cobra"
 	"github.com/spf13/viper"
 )
 
@@ -22,37 +23,37 @@ var (
 )
 
 // FromWallet returns private key of the wallet account.
-func FromWallet(w *wallet.Wallet, addrStr string) (*ecdsa.PrivateKey, error) {
+func FromWallet(cmd *cobra.Command, w *wallet.Wallet, addrStr string) (*ecdsa.PrivateKey, error) {
 	var (
 		addr util.Uint160
 		err  error
 	)
 
 	if addrStr == "" {
-		printVerbose("Using default wallet address")
+		common.PrintVerbose(cmd, "Using default wallet address")
 		addr = w.GetChangeAddress()
 	} else {
 		addr, err = flags.ParseAddress(addrStr)
 		if err != nil {
-			printVerbose("Can't parse address: %s", addrStr)
+			common.PrintVerbose(cmd, "Can't parse address: %s", addrStr)
 			return nil, ErrInvalidAddress
 		}
 	}
 
 	acc := w.GetAccount(addr)
 	if acc == nil {
-		printVerbose("Can't find wallet account for %s", addrStr)
+		common.PrintVerbose(cmd, "Can't find wallet account for %s", addrStr)
 		return nil, ErrInvalidAddress
 	}
 
 	pass, err := getPassword()
 	if err != nil {
-		printVerbose("Can't read password: %v", err)
+		common.PrintVerbose(cmd, "Can't read password: %v", err)
 		return nil, ErrInvalidPassword
 	}
 
 	if err := acc.Decrypt(pass, keys.NEP2ScryptParams()); err != nil {
-		printVerbose("Can't decrypt account: %v", err)
+		common.PrintVerbose(cmd, "Can't decrypt account: %v", err)
 		return nil, ErrInvalidPassword
 	}
 
@@ -67,9 +68,3 @@ func getPassword() (string, error) {
 
 	return input.ReadPassword("Enter password > ")
 }
-
-func printVerbose(format string, a ...interface{}) {
-	if viper.GetBool("verbose") {
-		fmt.Printf(format+"\n", a...)
-	}
-}
diff --git a/cmd/frostfs-cli/modules/bearer/create.go b/cmd/frostfs-cli/modules/bearer/create.go
index 5626f0eb6d..0fd83e36a6 100644
--- a/cmd/frostfs-cli/modules/bearer/create.go
+++ b/cmd/frostfs-cli/modules/bearer/create.go
@@ -71,7 +71,7 @@ func createToken(cmd *cobra.Command, _ []string) {
 		defer cancel()
 
 		endpoint, _ := cmd.Flags().GetString(commonflags.RPC)
-		currEpoch, err := internalclient.GetCurrentEpoch(ctx, endpoint)
+		currEpoch, err := internalclient.GetCurrentEpoch(ctx, cmd, endpoint)
 		common.ExitOnErr(cmd, "can't fetch current epoch: %w", err)
 
 		if iatRelative {
diff --git a/cmd/frostfs-cli/modules/container/create.go b/cmd/frostfs-cli/modules/container/create.go
index 5eaa808bbf..91333757a0 100644
--- a/cmd/frostfs-cli/modules/container/create.go
+++ b/cmd/frostfs-cli/modules/container/create.go
@@ -36,7 +36,7 @@ var createContainerCmd = &cobra.Command{
 	Long: `Create new container and register it in the NeoFS. 
 It will be stored in sidechain when inner ring will accepts it.`,
 	Run: func(cmd *cobra.Command, args []string) {
-		placementPolicy, err := parseContainerPolicy(containerPolicy)
+		placementPolicy, err := parseContainerPolicy(cmd, containerPolicy)
 		common.ExitOnErr(cmd, "", err)
 
 		key := key.Get(cmd)
@@ -165,10 +165,10 @@ func initContainerCreateCmd() {
 		"Skip placement validity check")
 }
 
-func parseContainerPolicy(policyString string) (*netmap.PlacementPolicy, error) {
+func parseContainerPolicy(cmd *cobra.Command, policyString string) (*netmap.PlacementPolicy, error) {
 	_, err := os.Stat(policyString) // check if `policyString` is a path to file with placement policy
 	if err == nil {
-		common.PrintVerbose("Reading placement policy from file: %s", policyString)
+		common.PrintVerbose(cmd, "Reading placement policy from file: %s", policyString)
 
 		data, err := os.ReadFile(policyString)
 		if err != nil {
@@ -182,12 +182,12 @@ func parseContainerPolicy(policyString string) (*netmap.PlacementPolicy, error)
 
 	err = result.DecodeString(policyString)
 	if err == nil {
-		common.PrintVerbose("Parsed QL encoded policy")
+		common.PrintVerbose(cmd, "Parsed QL encoded policy")
 		return &result, nil
 	}
 
 	if err = result.UnmarshalJSON([]byte(policyString)); err == nil {
-		common.PrintVerbose("Parsed JSON encoded policy")
+		common.PrintVerbose(cmd, "Parsed JSON encoded policy")
 		return &result, nil
 	}
 
diff --git a/cmd/frostfs-cli/modules/container/delete.go b/cmd/frostfs-cli/modules/container/delete.go
index 1190b48d5a..4769a4d7dd 100644
--- a/cmd/frostfs-cli/modules/container/delete.go
+++ b/cmd/frostfs-cli/modules/container/delete.go
@@ -27,7 +27,7 @@ Only owner of the container has a permission to remove container.`,
 		cli := internalclient.GetSDKClientByFlag(cmd, pk, commonflags.RPC)
 
 		if force, _ := cmd.Flags().GetBool(commonflags.ForceFlag); !force {
-			common.PrintVerbose("Reading the container to check ownership...")
+			common.PrintVerbose(cmd, "Reading the container to check ownership...")
 
 			var getPrm internalclient.GetContainerPrm
 			getPrm.SetClient(cli)
@@ -39,13 +39,13 @@ Only owner of the container has a permission to remove container.`,
 			owner := resGet.Container().Owner()
 
 			if tok != nil {
-				common.PrintVerbose("Checking session issuer...")
+				common.PrintVerbose(cmd, "Checking session issuer...")
 
 				if !tok.Issuer().Equals(owner) {
 					common.ExitOnErr(cmd, "", fmt.Errorf("session issuer differs with the container owner: expected %s, has %s", owner, tok.Issuer()))
 				}
 			} else {
-				common.PrintVerbose("Checking provided account...")
+				common.PrintVerbose(cmd, "Checking provided account...")
 
 				var acc user.ID
 				user.IDFromKey(&acc, pk.PublicKey)
@@ -55,10 +55,10 @@ Only owner of the container has a permission to remove container.`,
 				}
 			}
 
-			common.PrintVerbose("Account matches the container owner.")
+			common.PrintVerbose(cmd, "Account matches the container owner.")
 
 			if tok != nil {
-				common.PrintVerbose("Skip searching for LOCK objects - session provided.")
+				common.PrintVerbose(cmd, "Skip searching for LOCK objects - session provided.")
 			} else {
 				fs := objectSDK.NewSearchFilters()
 				fs.AddTypeFilter(objectSDK.MatchStringEqual, objectSDK.TypeLock)
@@ -69,7 +69,7 @@ Only owner of the container has a permission to remove container.`,
 				searchPrm.SetFilters(fs)
 				searchPrm.SetTTL(2)
 
-				common.PrintVerbose("Searching for LOCK objects...")
+				common.PrintVerbose(cmd, "Searching for LOCK objects...")
 
 				res, err := internalclient.SearchObjects(searchPrm)
 				common.ExitOnErr(cmd, "can't search for LOCK objects: %w", err)
diff --git a/cmd/frostfs-cli/modules/container/get.go b/cmd/frostfs-cli/modules/container/get.go
index 37dcb25bd6..e14e5e7691 100644
--- a/cmd/frostfs-cli/modules/container/get.go
+++ b/cmd/frostfs-cli/modules/container/get.go
@@ -1,9 +1,7 @@
 package container
 
 import (
-	"bytes"
 	"crypto/ecdsa"
-	"encoding/json"
 	"os"
 
 	internalclient "github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client"
@@ -77,18 +75,7 @@ func (x *stringWriter) WriteString(s string) (n int, err error) {
 
 func prettyPrintContainer(cmd *cobra.Command, cnr container.Container, jsonEncoding bool) {
 	if jsonEncoding {
-		data, err := cnr.MarshalJSON()
-		if err != nil {
-			common.PrintVerbose("Can't convert container to json: %w", err)
-			return
-		}
-		buf := new(bytes.Buffer)
-		if err := json.Indent(buf, data, "", "  "); err != nil {
-			common.PrintVerbose("Can't pretty print json: %w", err)
-		}
-
-		cmd.Println(buf)
-
+		common.PrettyPrintJSON(cmd, cnr, "container")
 		return
 	}
 
diff --git a/cmd/frostfs-cli/modules/container/util.go b/cmd/frostfs-cli/modules/container/util.go
index d8ec9dc991..5c3927b26c 100644
--- a/cmd/frostfs-cli/modules/container/util.go
+++ b/cmd/frostfs-cli/modules/container/util.go
@@ -36,22 +36,22 @@ func parseContainerID(cmd *cobra.Command) cid.ID {
 // decodes session.Container from the file by path provided in
 // commonflags.SessionToken flag. Returns nil if the path is not specified.
 func getSession(cmd *cobra.Command) *session.Container {
-	common.PrintVerbose("Reading container session...")
+	common.PrintVerbose(cmd, "Reading container session...")
 
 	path, _ := cmd.Flags().GetString(commonflags.SessionToken)
 	if path == "" {
-		common.PrintVerbose("Session not provided.")
+		common.PrintVerbose(cmd, "Session not provided.")
 		return nil
 	}
 
-	common.PrintVerbose("Reading container session from the file [%s]...", path)
+	common.PrintVerbose(cmd, "Reading container session from the file [%s]...", path)
 
 	var res session.Container
 
-	err := common.ReadBinaryOrJSON(&res, path)
+	err := common.ReadBinaryOrJSON(cmd, &res, path)
 	common.ExitOnErr(cmd, "read container session: %v", err)
 
-	common.PrintVerbose("Session successfully read.")
+	common.PrintVerbose(cmd, "Session successfully read.")
 
 	return &res
 }
diff --git a/cmd/frostfs-cli/modules/control/set_netmap_status.go b/cmd/frostfs-cli/modules/control/set_netmap_status.go
index 13429d0448..28d784ca33 100644
--- a/cmd/frostfs-cli/modules/control/set_netmap_status.go
+++ b/cmd/frostfs-cli/modules/control/set_netmap_status.go
@@ -51,7 +51,7 @@ func setNetmapStatus(cmd *cobra.Command, _ []string) {
 
 	printIgnoreForce := func(st control.NetmapStatus) {
 		if force {
-			common.PrintVerbose("Ignore --%s flag for %s state.", commonflags.ForceFlag, st)
+			common.PrintVerbose(cmd, "Ignore --%s flag for %s state.", commonflags.ForceFlag, st)
 		}
 	}
 
@@ -69,7 +69,7 @@ func setNetmapStatus(cmd *cobra.Command, _ []string) {
 
 		if force {
 			body.SetForceMaintenance()
-			common.PrintVerbose("Local maintenance will be forced.")
+			common.PrintVerbose(cmd, "Local maintenance will be forced.")
 		}
 	}
 
diff --git a/cmd/frostfs-cli/modules/object/lock.go b/cmd/frostfs-cli/modules/object/lock.go
index 574ffbd9c6..5894316767 100644
--- a/cmd/frostfs-cli/modules/object/lock.go
+++ b/cmd/frostfs-cli/modules/object/lock.go
@@ -60,13 +60,13 @@ var objectLockCmd = &cobra.Command{
 
 			endpoint, _ := cmd.Flags().GetString(commonflags.RPC)
 
-			currEpoch, err := internalclient.GetCurrentEpoch(ctx, endpoint)
+			currEpoch, err := internalclient.GetCurrentEpoch(ctx, cmd, endpoint)
 			common.ExitOnErr(cmd, "Request current epoch: %w", err)
 
 			exp = currEpoch + lifetime
 		}
 
-		common.PrintVerbose("Lock object will expire after %d epoch", exp)
+		common.PrintVerbose(cmd, "Lock object will expire after %d epoch", exp)
 
 		var expirationAttr objectSDK.Attribute
 		expirationAttr.SetKey(objectV2.SysAttributeExpEpoch)
diff --git a/cmd/frostfs-cli/modules/object/util.go b/cmd/frostfs-cli/modules/object/util.go
index 6146965258..4b036923f2 100644
--- a/cmd/frostfs-cli/modules/object/util.go
+++ b/cmd/frostfs-cli/modules/object/util.go
@@ -46,7 +46,7 @@ func InitBearer(cmd *cobra.Command) {
 // Prepare prepares object-related parameters for a command.
 func Prepare(cmd *cobra.Command, prms ...RPCParameters) {
 	ttl := viper.GetUint32(commonflags.TTL)
-	common.PrintVerbose("TTL: %d", ttl)
+	common.PrintVerbose(cmd, "TTL: %d", ttl)
 
 	for i := range prms {
 		btok := common.ReadBearerToken(cmd, bearerTokenFlag)
@@ -127,19 +127,19 @@ func readSession(cmd *cobra.Command, dst SessionPrm, key *ecdsa.PrivateKey, cnr
 // decodes session.Object from the file by path specified in the
 // commonflags.SessionToken flag. Returns nil if flag is not set.
 func getSession(cmd *cobra.Command) *session.Object {
-	common.PrintVerbose("Trying to read session from the file...")
+	common.PrintVerbose(cmd, "Trying to read session from the file...")
 
 	path, _ := cmd.Flags().GetString(commonflags.SessionToken)
 	if path == "" {
-		common.PrintVerbose("File with session token is not provided.")
+		common.PrintVerbose(cmd, "File with session token is not provided.")
 		return nil
 	}
 
-	common.PrintVerbose("Reading session from the file [%s]...", path)
+	common.PrintVerbose(cmd, "Reading session from the file [%s]...", path)
 
 	var tok session.Object
 
-	err := common.ReadBinaryOrJSON(&tok, path)
+	err := common.ReadBinaryOrJSON(cmd, &tok, path)
 	common.ExitOnErr(cmd, "read session: %v", err)
 
 	return &tok
@@ -185,7 +185,7 @@ func _readVerifiedSession(cmd *cobra.Command, dst SessionPrm, key *ecdsa.Private
 		return
 	}
 
-	common.PrintVerbose("Checking session correctness...")
+	common.PrintVerbose(cmd, "Checking session correctness...")
 
 	switch false {
 	case tok.AssertContainer(cnr):
@@ -200,7 +200,7 @@ func _readVerifiedSession(cmd *cobra.Command, dst SessionPrm, key *ecdsa.Private
 		common.ExitOnErr(cmd, "", errors.New("invalid signature of the session data"))
 	}
 
-	common.PrintVerbose("Session is correct.")
+	common.PrintVerbose(cmd, "Session is correct.")
 
 	dst.SetSessionToken(tok)
 }
@@ -227,7 +227,7 @@ func ReadOrOpenSessionViaClient(cmd *cobra.Command, dst SessionPrm, cli *client.
 		objs = []oid.ID{*obj}
 
 		if _, ok := dst.(*internal.DeleteObjectPrm); ok {
-			common.PrintVerbose("Collecting relatives of the removal object...")
+			common.PrintVerbose(cmd, "Collecting relatives of the removal object...")
 
 			objs = append(objs, collectObjectRelatives(cmd, cli, cnr, *obj)...)
 		}
@@ -259,7 +259,7 @@ func OpenSessionViaClient(cmd *cobra.Command, dst SessionPrm, cli *client.Client
 
 	if obj != nil {
 		if _, ok := dst.(*internal.DeleteObjectPrm); ok {
-			common.PrintVerbose("Collecting relatives of the removal object...")
+			common.PrintVerbose(cmd, "Collecting relatives of the removal object...")
 
 			rels := collectObjectRelatives(cmd, cli, cnr, *obj)
 
@@ -275,12 +275,12 @@ func OpenSessionViaClient(cmd *cobra.Command, dst SessionPrm, cli *client.Client
 
 	const sessionLifetime = 10 // in NeoFS epochs
 
-	common.PrintVerbose("Opening remote session with the node...")
+	common.PrintVerbose(cmd, "Opening remote session with the node...")
 
 	err := sessionCli.CreateSession(&tok, cli, sessionLifetime)
 	common.ExitOnErr(cmd, "open remote session: %w", err)
 
-	common.PrintVerbose("Session successfully opened.")
+	common.PrintVerbose(cmd, "Session successfully opened.")
 
 	finalizeSession(cmd, dst, &tok, key, cnr, objs...)
 
@@ -297,33 +297,33 @@ func OpenSessionViaClient(cmd *cobra.Command, dst SessionPrm, cli *client.Client
 //	*internal.PutObjectPrm
 //	*internal.DeleteObjectPrm
 func finalizeSession(cmd *cobra.Command, dst SessionPrm, tok *session.Object, key *ecdsa.PrivateKey, cnr cid.ID, objs ...oid.ID) {
-	common.PrintVerbose("Finalizing session token...")
+	common.PrintVerbose(cmd, "Finalizing session token...")
 
 	switch dst.(type) {
 	default:
 		panic(fmt.Sprintf("unsupported op parameters %T", dst))
 	case *internal.PutObjectPrm:
-		common.PrintVerbose("Binding session to object PUT...")
+		common.PrintVerbose(cmd, "Binding session to object PUT...")
 		tok.ForVerb(session.VerbObjectPut)
 	case *internal.DeleteObjectPrm:
-		common.PrintVerbose("Binding session to object DELETE...")
+		common.PrintVerbose(cmd, "Binding session to object DELETE...")
 		tok.ForVerb(session.VerbObjectDelete)
 	}
 
-	common.PrintVerbose("Binding session to container %s...", cnr)
+	common.PrintVerbose(cmd, "Binding session to container %s...", cnr)
 
 	tok.BindContainer(cnr)
 	if len(objs) > 0 {
-		common.PrintVerbose("Limiting session by the objects %v...", objs)
+		common.PrintVerbose(cmd, "Limiting session by the objects %v...", objs)
 		tok.LimitByObjects(objs...)
 	}
 
-	common.PrintVerbose("Signing session...")
+	common.PrintVerbose(cmd, "Signing session...")
 
 	err := tok.Sign(*key)
 	common.ExitOnErr(cmd, "sign session: %w", err)
 
-	common.PrintVerbose("Session token successfully formed and attached to the request.")
+	common.PrintVerbose(cmd, "Session token successfully formed and attached to the request.")
 
 	dst.SetSessionToken(tok)
 }
@@ -339,7 +339,7 @@ func initFlagSession(cmd *cobra.Command, verb string) {
 //
 // The object itself is not included in the result.
 func collectObjectRelatives(cmd *cobra.Command, cli *client.Client, cnr cid.ID, obj oid.ID) []oid.ID {
-	common.PrintVerbose("Fetching raw object header...")
+	common.PrintVerbose(cmd, "Fetching raw object header...")
 
 	// request raw header first
 	var addrObj oid.Address
@@ -361,10 +361,10 @@ func collectObjectRelatives(cmd *cobra.Command, cli *client.Client, cnr cid.ID,
 	default:
 		common.ExitOnErr(cmd, "failed to get raw object header: %w", err)
 	case err == nil:
-		common.PrintVerbose("Raw header received - object is singular.")
+		common.PrintVerbose(cmd, "Raw header received - object is singular.")
 		return nil
 	case errors.As(err, &errSplit):
-		common.PrintVerbose("Split information received - object is virtual.")
+		common.PrintVerbose(cmd, "Split information received - object is virtual.")
 	}
 
 	splitInfo := errSplit.SplitInfo()
@@ -373,7 +373,7 @@ func collectObjectRelatives(cmd *cobra.Command, cli *client.Client, cnr cid.ID,
 	// If any approach fails, we don't try the next since we assume that it will fail too.
 
 	if idLinking, ok := splitInfo.Link(); ok {
-		common.PrintVerbose("Collecting split members using linking object %s...", idLinking)
+		common.PrintVerbose(cmd, "Collecting split members using linking object %s...", idLinking)
 
 		addrObj.SetObject(idLinking)
 		prmHead.SetAddress(addrObj)
@@ -381,18 +381,22 @@ func collectObjectRelatives(cmd *cobra.Command, cli *client.Client, cnr cid.ID,
 		// client is already set
 
 		res, err := internal.HeadObject(prmHead)
-		common.ExitOnErr(cmd, "failed to get linking object's header: %w", err)
+		if err == nil {
+			children := res.Header().Children()
 
-		children := res.Header().Children()
+			common.PrintVerbose(cmd, "Received split members from the linking object: %v", children)
 
-		common.PrintVerbose("Received split members from the linking object: %v", children)
+			// include linking object
+			return append(children, idLinking)
+		}
 
-		// include linking object
-		return append(children, idLinking)
+		// linking object is not required for
+		// object collecting
+		common.PrintVerbose(cmd, "failed to get linking object's header: %w", err)
 	}
 
 	if idSplit := splitInfo.SplitID(); idSplit != nil {
-		common.PrintVerbose("Collecting split members by split ID...")
+		common.PrintVerbose(cmd, "Collecting split members by split ID...")
 
 		var query object.SearchFilters
 		query.AddSplitIDFilter(object.MatchStringEqual, idSplit)
@@ -407,7 +411,7 @@ func collectObjectRelatives(cmd *cobra.Command, cli *client.Client, cnr cid.ID,
 
 		members := res.IDList()
 
-		common.PrintVerbose("Found objects by split ID: %v", res.IDList())
+		common.PrintVerbose(cmd, "Found objects by split ID: %v", res.IDList())
 
 		return members
 	}
@@ -417,7 +421,7 @@ func collectObjectRelatives(cmd *cobra.Command, cli *client.Client, cnr cid.ID,
 		common.ExitOnErr(cmd, "", errors.New("missing any data in received object split information"))
 	}
 
-	common.PrintVerbose("Traverse the object split chain in reverse...", idMember)
+	common.PrintVerbose(cmd, "Traverse the object split chain in reverse...", idMember)
 
 	var res *internal.HeadObjectRes
 	chain := []oid.ID{idMember}
@@ -427,7 +431,7 @@ func collectObjectRelatives(cmd *cobra.Command, cli *client.Client, cnr cid.ID,
 	// split members are almost definitely singular, but don't get hung up on it
 
 	for {
-		common.PrintVerbose("Reading previous element of the split chain member %s...", idMember)
+		common.PrintVerbose(cmd, "Reading previous element of the split chain member %s...", idMember)
 
 		addrObj.SetObject(idMember)
 
@@ -436,7 +440,7 @@ func collectObjectRelatives(cmd *cobra.Command, cli *client.Client, cnr cid.ID,
 
 		idMember, ok = res.Header().PreviousID()
 		if !ok {
-			common.PrintVerbose("Chain ended.")
+			common.PrintVerbose(cmd, "Chain ended.")
 			break
 		}
 
@@ -448,7 +452,7 @@ func collectObjectRelatives(cmd *cobra.Command, cli *client.Client, cnr cid.ID,
 		chainSet[idMember] = struct{}{}
 	}
 
-	common.PrintVerbose("Looking for a linking object...")
+	common.PrintVerbose(cmd, "Looking for a linking object...")
 
 	var query object.SearchFilters
 	query.AddParentIDFilter(object.MatchStringEqual, obj)
@@ -465,7 +469,7 @@ func collectObjectRelatives(cmd *cobra.Command, cli *client.Client, cnr cid.ID,
 
 	for i := range list {
 		if _, ok = chainSet[list[i]]; !ok {
-			common.PrintVerbose("Found one more related object %s.", list[i])
+			common.PrintVerbose(cmd, "Found one more related object %s.", list[i])
 			chain = append(chain, list[i])
 		}
 	}
diff --git a/cmd/frostfs-cli/modules/root.go b/cmd/frostfs-cli/modules/root.go
index 9650f819c1..156db4bee4 100644
--- a/cmd/frostfs-cli/modules/root.go
+++ b/cmd/frostfs-cli/modules/root.go
@@ -118,6 +118,6 @@ func initConfig() {
 
 	// If a config file is found, read it in.
 	if err := viper.ReadInConfig(); err == nil {
-		common.PrintVerbose("Using config file: %s", viper.ConfigFileUsed())
+		common.PrintVerbose(rootCmd, "Using config file: %s", viper.ConfigFileUsed())
 	}
 }
diff --git a/cmd/frostfs-cli/modules/session/create.go b/cmd/frostfs-cli/modules/session/create.go
index 48e677fc26..498eec5489 100644
--- a/cmd/frostfs-cli/modules/session/create.go
+++ b/cmd/frostfs-cli/modules/session/create.go
@@ -54,7 +54,7 @@ func createSession(cmd *cobra.Command, _ []string) {
 	addrStr, _ := cmd.Flags().GetString(commonflags.RPC)
 	common.ExitOnErr(cmd, "can't parse endpoint: %w", netAddr.FromString(addrStr))
 
-	c, err := internalclient.GetSDKClient(privKey, netAddr)
+	c, err := internalclient.GetSDKClient(cmd, privKey, netAddr)
 	common.ExitOnErr(cmd, "can't create client: %w", err)
 
 	lifetime := uint64(defaultLifetime)
diff --git a/cmd/frostfs-cli/modules/tree/add_by_path.go b/cmd/frostfs-cli/modules/tree/add_by_path.go
index 1f15279b3a..52c1cab70f 100644
--- a/cmd/frostfs-cli/modules/tree/add_by_path.go
+++ b/cmd/frostfs-cli/modules/tree/add_by_path.go
@@ -83,7 +83,7 @@ func addByPath(cmd *cobra.Command, _ []string) {
 
 	nn := resp.GetBody().GetNodes()
 	if len(nn) == 0 {
-		common.PrintVerbose("No new nodes were created")
+		common.PrintVerbose(cmd, "No new nodes were created")
 		return
 	}
 
diff --git a/cmd/frostfs-cli/modules/tree/get_by_path.go b/cmd/frostfs-cli/modules/tree/get_by_path.go
index cf94d198f7..2a3488f48b 100644
--- a/cmd/frostfs-cli/modules/tree/get_by_path.go
+++ b/cmd/frostfs-cli/modules/tree/get_by_path.go
@@ -80,7 +80,7 @@ func getByPath(cmd *cobra.Command, _ []string) {
 
 	nn := resp.GetBody().GetNodes()
 	if len(nn) == 0 {
-		common.PrintVerbose("The node is not found")
+		common.PrintVerbose(cmd, "The node is not found")
 		return
 	}
 
diff --git a/cmd/frostfs-cli/modules/util/convert_eacl.go b/cmd/frostfs-cli/modules/util/convert_eacl.go
index 667194324e..dc30fe9f93 100644
--- a/cmd/frostfs-cli/modules/util/convert_eacl.go
+++ b/cmd/frostfs-cli/modules/util/convert_eacl.go
@@ -1,8 +1,6 @@
 package util
 
 import (
-	"bytes"
-	"encoding/json"
 	"os"
 
 	"github.com/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common"
@@ -45,7 +43,7 @@ func convertEACLTable(cmd *cobra.Command, _ []string) {
 	}
 
 	if len(to) == 0 {
-		prettyPrintJSON(cmd, data)
+		common.PrettyPrintJSON(cmd, table, "eACL")
 		return
 	}
 
@@ -54,12 +52,3 @@ func convertEACLTable(cmd *cobra.Command, _ []string) {
 
 	cmd.Printf("extended ACL table was successfully dumped to %s\n", to)
 }
-
-func prettyPrintJSON(cmd *cobra.Command, data []byte) {
-	buf := new(bytes.Buffer)
-	if err := json.Indent(buf, data, "", "  "); err != nil {
-		common.PrintVerbose("Can't pretty print json: %w", err)
-	}
-
-	cmd.Println(buf)
-}
diff --git a/cmd/frostfs-cli/modules/util/sign_bearer.go b/cmd/frostfs-cli/modules/util/sign_bearer.go
index 515e325d06..cdd3fd0bc9 100644
--- a/cmd/frostfs-cli/modules/util/sign_bearer.go
+++ b/cmd/frostfs-cli/modules/util/sign_bearer.go
@@ -51,8 +51,7 @@ func signBearerToken(cmd *cobra.Command, _ []string) {
 	}
 
 	if len(to) == 0 {
-		prettyPrintJSON(cmd, data)
-
+		common.PrettyPrintJSON(cmd, btok, "bearer token")
 		return
 	}
 
diff --git a/cmd/frostfs-cli/modules/util/sign_session.go b/cmd/frostfs-cli/modules/util/sign_session.go
index 610ca8af6f..f9350c6719 100644
--- a/cmd/frostfs-cli/modules/util/sign_session.go
+++ b/cmd/frostfs-cli/modules/util/sign_session.go
@@ -52,7 +52,7 @@ func signSessionToken(cmd *cobra.Command, _ []string) {
 		new(session.Object),
 		new(session.Container),
 	} {
-		errLast = common.ReadBinaryOrJSON(el, fPath)
+		errLast = common.ReadBinaryOrJSON(cmd, el, fPath)
 		if errLast == nil {
 			stok = el
 			break
@@ -71,7 +71,7 @@ func signSessionToken(cmd *cobra.Command, _ []string) {
 
 	to := cmd.Flag(signToFlag).Value.String()
 	if len(to) == 0 {
-		prettyPrintJSON(cmd, data)
+		common.PrettyPrintJSON(cmd, stok, "session token")
 		return
 	}