This is an automated email from the ASF dual-hosted git repository.

hanahmily pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/skywalking-banyandb.git


The following commit(s) were added to refs/heads/main by this push:
     new 4206e0ee6 test(bydbctl): avoid race-prone stdout capture in command 
tests (#1105)
4206e0ee6 is described below

commit 4206e0ee6c77e08bc458bcd58303e66955527559
Author: Tanay Paul <[email protected]>
AuthorDate: Thu May 7 04:17:12 2026 +0530

    test(bydbctl): avoid race-prone stdout capture in command tests (#1105)
---
 CHANGES.md                                         |   1 +
 bydbctl/internal/cmd/analyze.go                    |  12 +-
 bydbctl/internal/cmd/analyze_test.go               |  13 +-
 bydbctl/internal/cmd/auth_test.go                  |  24 ++-
 bydbctl/internal/cmd/group.go                      |  37 ++--
 bydbctl/internal/cmd/group_test.go                 |  74 ++++---
 bydbctl/internal/cmd/health_check.go               |   6 +-
 bydbctl/internal/cmd/health_check_test.go          |  68 +++---
 bydbctl/internal/cmd/index_rule.go                 |  40 ++--
 bydbctl/internal/cmd/index_rule_binding.go         |  40 ++--
 bydbctl/internal/cmd/index_rule_binding_test.go    | 100 +++++----
 bydbctl/internal/cmd/index_rule_test.go            | 100 +++++----
 bydbctl/internal/cmd/measure.go                    |  34 ++-
 bydbctl/internal/cmd/measure_test.go               | 122 +++++++----
 bydbctl/internal/cmd/property.go                   |  40 ++--
 bydbctl/internal/cmd/property_test.go              | 236 +++++++++++++--------
 bydbctl/internal/cmd/rest.go                       |  14 +-
 bydbctl/internal/cmd/root.go                       |   5 +-
 bydbctl/internal/cmd/stream.go                     |  34 ++-
 bydbctl/internal/cmd/stream_test.go                | 122 +++++++----
 bydbctl/internal/cmd/topn.go                       |  34 ++-
 bydbctl/internal/cmd/topn_test.go                  | 148 ++++++++-----
 bydbctl/internal/cmd/trace.go                      |  42 ++--
 bydbctl/internal/cmd/trace_test.go                 | 122 +++++++----
 bydbctl/internal/cmd/use.go                        |   4 +-
 bydbctl/internal/cmd/{use.go => use_test.go}       |  50 +++--
 dist/LICENSE                                       |   1 -
 .../license-github.com-zenizh-go-capturer.txt      |  21 --
 go.mod                                             |   1 -
 go.sum                                             |   2 -
 30 files changed, 889 insertions(+), 658 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index daad0e5da..3195f1d79 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -38,6 +38,7 @@ Release Notes.
 
 ### Bug Fixes
 
+- Fix bydbctl command tests using global stdout capture, which caused 
race-enabled runs to corrupt captured command output.
 - Use `topic` instead of `session_id` as the Prometheus label on liaison 
`queue_sub` chunk-ordering counters to avoid unbounded metric cardinality.
 - Fix flaky trace query filtering caused by non-deterministic sidx tag 
ordering and add consistency checks for integration query cases.
 - Fix index-mode measure queries returning documents outside the requested 
time range when a widened segment overlaps the query window.
diff --git a/bydbctl/internal/cmd/analyze.go b/bydbctl/internal/cmd/analyze.go
index 04f85207d..2b25dd8d1 100644
--- a/bydbctl/internal/cmd/analyze.go
+++ b/bydbctl/internal/cmd/analyze.go
@@ -44,7 +44,7 @@ func newAnalyzeCmd() *cobra.Command {
                Use:     "series",
                Version: version.Build(),
                Short:   "Analyze series cardinality and distribution",
-               RunE: func(_ *cobra.Command, args []string) (err error) {
+               RunE: func(cmd *cobra.Command, args []string) (err error) {
                        if len(args) == 0 {
                                return errors.New("series index directory is 
required, its name should be 'sidx' in a segment 'seg-xxxxxx'")
                        }
@@ -74,9 +74,9 @@ func newAnalyzeCmd() *cobra.Command {
                                        if s.Subject == subjectName {
                                                found = true
                                                for i := range s.EntityValues {
-                                                       fmt.Printf("%s,", 
pbv1.MustTagValueToStr(s.EntityValues[i]))
+                                                       
fmt.Fprintf(cmd.OutOrStdout(), "%s,", pbv1.MustTagValueToStr(s.EntityValues[i]))
                                                }
-                                               fmt.Println()
+                                               fmt.Fprintln(cmd.OutOrStdout())
                                                continue
                                        }
                                        if found {
@@ -95,7 +95,7 @@ func newAnalyzeCmd() *cobra.Command {
                                }
                                if s.Subject != subject {
                                        if subject != "" {
-                                               fmt.Printf("%s, %d\n", subject, 
count)
+                                               fmt.Fprintf(cmd.OutOrStdout(), 
"%s, %d\n", subject, count)
                                        }
                                        subject = s.Subject
                                        count = 1
@@ -104,9 +104,9 @@ func newAnalyzeCmd() *cobra.Command {
                                }
                        }
                        if subject != "" {
-                               fmt.Printf("%s, %d\n", subject, count)
+                               fmt.Fprintf(cmd.OutOrStdout(), "%s, %d\n", 
subject, count)
                        }
-                       fmt.Printf("total, %d\n", total)
+                       fmt.Fprintf(cmd.OutOrStdout(), "total, %d\n", total)
                        return nil
                },
        }
diff --git a/bydbctl/internal/cmd/analyze_test.go 
b/bydbctl/internal/cmd/analyze_test.go
index d7e164b4b..06c66e3b6 100644
--- a/bydbctl/internal/cmd/analyze_test.go
+++ b/bydbctl/internal/cmd/analyze_test.go
@@ -18,13 +18,13 @@
 package cmd_test
 
 import (
+       "bytes"
        "path"
        "time"
 
        . "github.com/onsi/ginkgo/v2"
        . "github.com/onsi/gomega"
        "github.com/spf13/cobra"
-       "github.com/zenizh/go-capturer"
        grpclib "google.golang.org/grpc"
        "google.golang.org/grpc/credentials/insecure"
 
@@ -64,10 +64,13 @@ var _ = Describe("Measure Data Query", func() {
                serverDeferFunc()
 
                rootCmd.SetArgs([]string{"analyze", "series", 
path.Join(directory, "measure/sw_metric/seg-20210901/sidx")})
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err = rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                GinkgoWriter.Println(out)
                Expect(out).To(ContainSubstring("total"))
        })
diff --git a/bydbctl/internal/cmd/auth_test.go 
b/bydbctl/internal/cmd/auth_test.go
index f59cf6034..4b070b981 100644
--- a/bydbctl/internal/cmd/auth_test.go
+++ b/bydbctl/internal/cmd/auth_test.go
@@ -18,6 +18,7 @@
 package cmd_test
 
 import (
+       "bytes"
        "embed"
        "fmt"
        "os"
@@ -28,7 +29,6 @@ import (
        g "github.com/onsi/ginkgo/v2"
        gm "github.com/onsi/gomega"
        "github.com/spf13/cobra"
-       "github.com/zenizh/go-capturer"
        "sigs.k8s.io/yaml"
 
        databasev1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/database/v1"
@@ -119,17 +119,23 @@ resource_opts:
   ttl:
     unit: UNIT_DAY
     num: 7`))
-               out := capturer.CaptureStdout(func() {
-                       err = rootCmd.Execute()
-                       gm.Expect(err).NotTo(gm.HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err = rootCmd.Execute()
+               gm.Expect(err).NotTo(gm.HaveOccurred())
+               out := buf.String()
                gm.Expect(out).To(gm.ContainSubstring("group group1 is 
created"))
 
                rootCmd.SetArgs([]string{"--config", bydbctlCfgFile, "group", 
"-a", httpAddr, "get", "-g", "group1"})
-               out = capturer.CaptureStdout(func() {
-                       err = rootCmd.Execute()
-                       gm.Expect(err).NotTo(gm.HaveOccurred())
-               })
+               buf.Reset()
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err = rootCmd.Execute()
+               gm.Expect(err).NotTo(gm.HaveOccurred())
+               out = buf.String()
                resp := new(databasev1.GroupRegistryServiceGetResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
                gm.Expect(resp.Group.Metadata.Name).To(gm.Equal("group1"))
diff --git a/bydbctl/internal/cmd/group.go b/bydbctl/internal/cmd/group.go
index c83366f4c..e00305b1b 100644
--- a/bydbctl/internal/cmd/group.go
+++ b/bydbctl/internal/cmd/group.go
@@ -19,6 +19,7 @@ package cmd
 
 import (
        "fmt"
+       "io"
 
        "github.com/go-resty/resty/v2"
        "github.com/spf13/cobra"
@@ -41,7 +42,7 @@ func newGroupCmd() *cobra.Command {
                Version: version.Build(),
                Short:   "Create groups from files",
                RunE: func(cmd *cobra.Command, _ []string) (err error) {
-                       return rest(func() ([]reqBody, error) { return 
parseNameFromYAML(cmd.InOrStdin()) },
+                       return rest(cmd.OutOrStdout(), func() ([]reqBody, 
error) { return parseNameFromYAML(cmd.InOrStdin()) },
                                func(request request) (*resty.Response, error) {
                                        g := new(commonv1.Group)
                                        err := 
protojson.Unmarshal(request.data, g)
@@ -57,9 +58,8 @@ func newGroupCmd() *cobra.Command {
                                        }
                                        return 
request.req.SetBody(b).Post(getPath("/api/v1/group/schema"))
                                },
-                               func(_ int, reqBody reqBody, _ []byte) error {
-                                       fmt.Printf("group %s is created", 
reqBody.name)
-                                       fmt.Println()
+                               func(w io.Writer, _ int, reqBody reqBody, _ 
[]byte) error {
+                                       fmt.Fprintf(w, "group %s is created\n", 
reqBody.name)
                                        return nil
                                }, enableTLS, insecure, cert)
                },
@@ -70,7 +70,7 @@ func newGroupCmd() *cobra.Command {
                Version: version.Build(),
                Short:   "Update groups from files",
                RunE: func(cmd *cobra.Command, _ []string) (err error) {
-                       return rest(func() ([]reqBody, error) { return 
parseNameFromYAML(cmd.InOrStdin()) },
+                       return rest(cmd.OutOrStdout(), func() ([]reqBody, 
error) { return parseNameFromYAML(cmd.InOrStdin()) },
                                func(request request) (*resty.Response, error) {
                                        g := new(commonv1.Group)
                                        err := 
protojson.Unmarshal(request.data, g)
@@ -86,9 +86,8 @@ func newGroupCmd() *cobra.Command {
                                        }
                                        return 
request.req.SetBody(b).SetPathParam("group", 
request.name).Put(getPath("/api/v1/group/schema/{group}"))
                                },
-                               func(_ int, reqBody reqBody, _ []byte) error {
-                                       fmt.Printf("group %s is updated", 
reqBody.name)
-                                       fmt.Println()
+                               func(w io.Writer, _ int, reqBody reqBody, _ 
[]byte) error {
+                                       fmt.Fprintf(w, "group %s is updated\n", 
reqBody.name)
                                        return nil
                                }, enableTLS, insecure, cert)
                },
@@ -99,8 +98,8 @@ func newGroupCmd() *cobra.Command {
                Use:     "get [-g group]",
                Version: version.Build(),
                Short:   "Get a group",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.SetPathParam("group", 
request.group).Get(getPath("/api/v1/group/schema/{group}"))
                        }, yamlPrinter, enableTLS, insecure, cert)
                },
@@ -111,8 +110,8 @@ func newGroupCmd() *cobra.Command {
                Use:     "delete [-g group] [--force] [--dry-run] 
[--data-only]",
                Version: version.Build(),
                Short:   "Delete a group",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.
                                        SetPathParam("group", request.group).
                                        SetQueryParam("force", 
fmt.Sprintf("%v", force)).
@@ -120,14 +119,12 @@ func newGroupCmd() *cobra.Command {
                                        SetQueryParam("data_only", 
fmt.Sprintf("%v", dataOnly)).
                                        
Delete(getPath("/api/v1/group/schema/{group}"))
                        },
-                               func(_ int, reqBody reqBody, respBody []byte) 
error {
+                               func(w io.Writer, _ int, reqBody reqBody, 
respBody []byte) error {
                                        if dryRun {
-                                               fmt.Printf("group %s dry-run 
deletion result:", reqBody.group)
-                                               fmt.Println()
-                                               return yamlPrinter(0, reqBody, 
respBody)
+                                               fmt.Fprintf(w, "group %s 
dry-run deletion result:\n", reqBody.group)
+                                               return yamlPrinter(w, 0, 
reqBody, respBody)
                                        }
-                                       fmt.Printf("group %s is deleted", 
reqBody.group)
-                                       fmt.Println()
+                                       fmt.Fprintf(w, "group %s is deleted\n", 
reqBody.group)
                                        return nil
                                }, enableTLS, insecure, cert)
                },
@@ -140,8 +137,8 @@ func newGroupCmd() *cobra.Command {
                Use:     "list",
                Version: version.Build(),
                Short:   "List all groups",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(nil, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), nil, func(request 
request) (*resty.Response, error) {
                                return 
request.req.Get(getPath("/api/v1/group/schema/lists"))
                        }, yamlPrinter, enableTLS, insecure, cert)
                },
diff --git a/bydbctl/internal/cmd/group_test.go 
b/bydbctl/internal/cmd/group_test.go
index 34344d708..c2939d9ca 100644
--- a/bydbctl/internal/cmd/group_test.go
+++ b/bydbctl/internal/cmd/group_test.go
@@ -18,12 +18,12 @@
 package cmd_test
 
 import (
+       "bytes"
        "strings"
 
        . "github.com/onsi/ginkgo/v2"
        . "github.com/onsi/gomega"
        "github.com/spf13/cobra"
-       "github.com/zenizh/go-capturer"
 
        databasev1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/database/v1"
        "github.com/apache/skywalking-banyandb/bydbctl/internal/cmd"
@@ -56,19 +56,22 @@ resource_opts:
   ttl:
     unit: UNIT_DAY
     num: 7`))
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
-               Expect(out).To(ContainSubstring("group group1 is created"))
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               Expect(buf.String()).To(ContainSubstring("group group1 is 
created"))
        })
 
        It("get group", func() {
                rootCmd.SetArgs([]string{"group", "get", "-g", "group1"})
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                resp := new(databasev1.GroupRegistryServiceGetResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
                Expect(resp.Group.Metadata.Name).To(Equal("group1"))
@@ -88,29 +91,33 @@ resource_opts:
   ttl:
     unit: UNIT_DAY
     num: 7`))
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
-               Expect(out).To(ContainSubstring("group group1 is updated"))
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               Expect(buf.String()).To(ContainSubstring("group group1 is 
updated"))
        })
 
        It("delete group", func() {
                Eventually(func(g Gomega) {
                        rootCmd.SetArgs([]string{"group", "delete", "-g", 
"group1"})
-                       out := capturer.CaptureStdout(func() {
-                               
g.Expect(rootCmd.Execute()).NotTo(HaveOccurred())
-                       })
-                       g.Expect(out).To(ContainSubstring("group group1 is 
deleted"))
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+                       g.Expect(rootCmd.Execute()).NotTo(HaveOccurred())
+                       g.Expect(buf.String()).To(ContainSubstring("group 
group1 is deleted"))
                }).Should(Succeed())
        })
 
        It("delete group with dry-run flag", func() {
                rootCmd.SetArgs([]string{"group", "delete", "-g", "group1", 
"--dry-run"})
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("group group1 dry-run deletion 
result:"))
                resp := new(databasev1.GroupRegistryServiceDeleteResponse)
                helpers.UnmarshalYAML([]byte(out[strings.Index(out, "\n")+1:]), 
resp)
@@ -132,17 +139,20 @@ resource_opts:
   ttl:
     unit: UNIT_DAY
     num: 7`))
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
-               Expect(out).To(ContainSubstring("group group2 is created"))
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               Expect(buf.String()).To(ContainSubstring("group group2 is 
created"))
                // list
                rootCmd.SetArgs([]string{"group", "list"})
-               out = capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               buf.Reset()
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+               err = rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                resp := new(databasev1.GroupRegistryServiceListResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
                Expect(resp.Group).To(HaveLen(3)) // group1, group2, 
_deletion_task (internal group)
diff --git a/bydbctl/internal/cmd/health_check.go 
b/bydbctl/internal/cmd/health_check.go
index de63956f2..2d1e0ffed 100644
--- a/bydbctl/internal/cmd/health_check.go
+++ b/bydbctl/internal/cmd/health_check.go
@@ -40,10 +40,10 @@ func newHealthCheckCmd() *cobra.Command {
                Short:         "Health check",
                SilenceErrors: true,
                SilenceUsage:  true,
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
                        opts := make([]grpc.DialOption, 0, 1)
                        if grpcAddr == "" {
-                               return rest(nil, func(request request) 
(*resty.Response, error) {
+                               return rest(cmd.OutOrStdout(), nil, 
func(request request) (*resty.Response, error) {
                                        return 
request.req.Get(getPath("/api/healthz"))
                                }, yamlPrinter, enableTLS, insecure, cert)
                        }
@@ -53,7 +53,7 @@ func newHealthCheckCmd() *cobra.Command {
                        }
                        err = healthCheck(grpcAddr, 10*time.Second, 
10*time.Second, username, password, opts...)
                        if err == nil {
-                               fmt.Println("connected")
+                               fmt.Fprintln(cmd.OutOrStdout(), "connected")
                        }
                        return err
                },
diff --git a/bydbctl/internal/cmd/health_check_test.go 
b/bydbctl/internal/cmd/health_check_test.go
index 74c8c4e17..4ad0cbcde 100644
--- a/bydbctl/internal/cmd/health_check_test.go
+++ b/bydbctl/internal/cmd/health_check_test.go
@@ -18,13 +18,13 @@
 package cmd_test
 
 import (
+       "bytes"
        "path/filepath"
        "runtime"
 
        . "github.com/onsi/ginkgo/v2"
        . "github.com/onsi/gomega"
        "github.com/spf13/cobra"
-       "github.com/zenizh/go-capturer"
 
        "github.com/apache/skywalking-banyandb/bydbctl/internal/cmd"
        "github.com/apache/skywalking-banyandb/pkg/test/setup"
@@ -48,19 +48,25 @@ var _ = Describe("health check after launching banyandb 
server with gRPC and HTT
 
        It("http health check should pass", func() {
                rootCmd.SetArgs([]string{"health", "--addr", "https://"; + 
httpAddr, "--cert", certFile, "--enable-tls", "true"})
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("SERVING"))
        })
 
        It("http health check should pass with insecure flag set", func() {
                rootCmd.SetArgs([]string{"health", "--addr", "https://"; + 
httpAddr, "--insecure", "true", "--enable-tls", "true"})
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("SERVING"))
        })
 
@@ -72,19 +78,25 @@ var _ = Describe("health check after launching banyandb 
server with gRPC and HTT
 
        It("grpc health check should pass", func() {
                rootCmd.SetArgs([]string{"health", "--grpc-addr", grpcAddr, 
"--cert", certFile, "--enable-tls", "true"})
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("connected"))
        })
 
        It("grpc health check should pass with insecure flag set", func() {
                rootCmd.SetArgs([]string{"health", "--grpc-addr", grpcAddr, 
"--insecure", "true", "--enable-tls", "true"})
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("connected"))
        })
 
@@ -111,19 +123,25 @@ var _ = Describe("health check after launching banyandb 
server", func() {
 
        It("http health check should pass", func() {
                rootCmd.SetArgs([]string{"health", "--addr", "http://"; + 
httpAddr})
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("SERVING"))
        })
 
        It("grpc health check should pass", func() {
                rootCmd.SetArgs([]string{"health", "--grpc-addr", grpcAddr})
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("connected"))
        })
 
diff --git a/bydbctl/internal/cmd/index_rule.go 
b/bydbctl/internal/cmd/index_rule.go
index b28c19bf0..d80679565 100644
--- a/bydbctl/internal/cmd/index_rule.go
+++ b/bydbctl/internal/cmd/index_rule.go
@@ -19,6 +19,7 @@ package cmd
 
 import (
        "fmt"
+       "io"
 
        "github.com/go-resty/resty/v2"
        "github.com/spf13/cobra"
@@ -44,25 +45,22 @@ func newIndexRuleCmd() *cobra.Command {
                Version: version.Build(),
                Short:   "Create indexRules from files",
                RunE: func(cmd *cobra.Command, _ []string) error {
-                       return rest(func() ([]reqBody, error) { return 
parseNameAndGroupFromYAML(cmd.InOrStdin()) },
+                       return rest(cmd.OutOrStdout(), func() ([]reqBody, 
error) { return parseNameAndGroupFromYAML(cmd.InOrStdin()) },
                                func(request request) (*resty.Response, error) {
                                        s := new(databasev1.IndexRule)
                                        err := 
protojson.Unmarshal(request.data, s)
                                        if err != nil {
                                                return nil, err
                                        }
-                                       cr := 
&databasev1.IndexRuleRegistryServiceCreateRequest{
-                                               IndexRule: s,
-                                       }
+                                       cr := 
&databasev1.IndexRuleRegistryServiceCreateRequest{IndexRule: s}
                                        b, err := protojson.Marshal(cr)
                                        if err != nil {
                                                return nil, err
                                        }
                                        return 
request.req.SetBody(b).Post(getPath(indexRuleSchemaPath))
                                },
-                               func(_ int, reqBody reqBody, _ []byte) error {
-                                       fmt.Printf("indexRule %s.%s is 
created", reqBody.group, reqBody.name)
-                                       fmt.Println()
+                               func(w io.Writer, _ int, reqBody reqBody, _ 
[]byte) error {
+                                       fmt.Fprintf(w, "indexRule %s.%s is 
created\n", reqBody.group, reqBody.name)
                                        return nil
                                }, enableTLS, insecure, cert)
                },
@@ -73,16 +71,14 @@ func newIndexRuleCmd() *cobra.Command {
                Version: version.Build(),
                Short:   "Update indexRules from files",
                RunE: func(cmd *cobra.Command, _ []string) (err error) {
-                       return rest(func() ([]reqBody, error) { return 
parseNameAndGroupFromYAML(cmd.InOrStdin()) },
+                       return rest(cmd.OutOrStdout(), func() ([]reqBody, 
error) { return parseNameAndGroupFromYAML(cmd.InOrStdin()) },
                                func(request request) (*resty.Response, error) {
                                        s := new(databasev1.IndexRule)
                                        err := 
protojson.Unmarshal(request.data, s)
                                        if err != nil {
                                                return nil, err
                                        }
-                                       cr := 
&databasev1.IndexRuleRegistryServiceUpdateRequest{
-                                               IndexRule: s,
-                                       }
+                                       cr := 
&databasev1.IndexRuleRegistryServiceUpdateRequest{IndexRule: s}
                                        b, err := protojson.Marshal(cr)
                                        if err != nil {
                                                return nil, err
@@ -91,9 +87,8 @@ func newIndexRuleCmd() *cobra.Command {
                                                SetPathParam("name", 
request.name).SetPathParam("group", request.group).
                                                
Put(getPath(indexRuleSchemaPathWithParams))
                                },
-                               func(_ int, reqBody reqBody, _ []byte) error {
-                                       fmt.Printf("indexRule %s.%s is 
updated", reqBody.group, reqBody.name)
-                                       fmt.Println()
+                               func(w io.Writer, _ int, reqBody reqBody, _ 
[]byte) error {
+                                       fmt.Fprintf(w, "indexRule %s.%s is 
updated\n", reqBody.group, reqBody.name)
                                        return nil
                                }, enableTLS, insecure, cert)
                },
@@ -103,8 +98,8 @@ func newIndexRuleCmd() *cobra.Command {
                Use:     "get [-g group] -n name",
                Version: version.Build(),
                Short:   "Get a indexRule",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.SetPathParam("name", 
request.name).SetPathParam("group", 
request.group).Get(getPath(indexRuleSchemaPathWithParams))
                        }, yamlPrinter, enableTLS, insecure, cert)
                },
@@ -114,12 +109,11 @@ func newIndexRuleCmd() *cobra.Command {
                Use:     "delete [-g group] -n name",
                Version: version.Build(),
                Short:   "Delete a indexRule",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.SetPathParam("name", 
request.name).SetPathParam("group", 
request.group).Delete(getPath(indexRuleSchemaPathWithParams))
-                       }, func(_ int, reqBody reqBody, _ []byte) error {
-                               fmt.Printf("indexRule %s.%s is deleted", 
reqBody.group, reqBody.name)
-                               fmt.Println()
+                       }, func(w io.Writer, _ int, reqBody reqBody, _ []byte) 
error {
+                               fmt.Fprintf(w, "indexRule %s.%s is deleted\n", 
reqBody.group, reqBody.name)
                                return nil
                        }, enableTLS, insecure, cert)
                },
@@ -130,8 +124,8 @@ func newIndexRuleCmd() *cobra.Command {
                Use:     "list [-g group]",
                Version: version.Build(),
                Short:   "List indexRules",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.SetPathParam("group", 
request.group).Get(getPath("/api/v1/index-rule/schema/lists/{group}"))
                        }, yamlPrinter, enableTLS, insecure, cert)
                },
diff --git a/bydbctl/internal/cmd/index_rule_binding.go 
b/bydbctl/internal/cmd/index_rule_binding.go
index 34b055ac4..1e21470c3 100644
--- a/bydbctl/internal/cmd/index_rule_binding.go
+++ b/bydbctl/internal/cmd/index_rule_binding.go
@@ -19,6 +19,7 @@ package cmd
 
 import (
        "fmt"
+       "io"
 
        "github.com/go-resty/resty/v2"
        "github.com/spf13/cobra"
@@ -44,25 +45,22 @@ func newIndexRuleBindingCmd() *cobra.Command {
                Version: version.Build(),
                Short:   "Create indexRuleBindings from files",
                RunE: func(cmd *cobra.Command, _ []string) error {
-                       return rest(func() ([]reqBody, error) { return 
parseNameAndGroupFromYAML(cmd.InOrStdin()) },
+                       return rest(cmd.OutOrStdout(), func() ([]reqBody, 
error) { return parseNameAndGroupFromYAML(cmd.InOrStdin()) },
                                func(request request) (*resty.Response, error) {
                                        s := new(databasev1.IndexRuleBinding)
                                        err := 
protojson.Unmarshal(request.data, s)
                                        if err != nil {
                                                return nil, err
                                        }
-                                       cr := 
&databasev1.IndexRuleBindingRegistryServiceCreateRequest{
-                                               IndexRuleBinding: s,
-                                       }
+                                       cr := 
&databasev1.IndexRuleBindingRegistryServiceCreateRequest{IndexRuleBinding: s}
                                        b, err := protojson.Marshal(cr)
                                        if err != nil {
                                                return nil, err
                                        }
                                        return 
request.req.SetBody(b).Post(getPath(indexRuleBindingSchemaPath))
                                },
-                               func(_ int, reqBody reqBody, _ []byte) error {
-                                       fmt.Printf("indexRuleBinding %s.%s is 
created", reqBody.group, reqBody.name)
-                                       fmt.Println()
+                               func(w io.Writer, _ int, reqBody reqBody, _ 
[]byte) error {
+                                       fmt.Fprintf(w, "indexRuleBinding %s.%s 
is created\n", reqBody.group, reqBody.name)
                                        return nil
                                }, enableTLS, insecure, cert)
                },
@@ -73,16 +71,14 @@ func newIndexRuleBindingCmd() *cobra.Command {
                Version: version.Build(),
                Short:   "Update indexRuleBindings from files",
                RunE: func(cmd *cobra.Command, _ []string) (err error) {
-                       return rest(func() ([]reqBody, error) { return 
parseNameAndGroupFromYAML(cmd.InOrStdin()) },
+                       return rest(cmd.OutOrStdout(), func() ([]reqBody, 
error) { return parseNameAndGroupFromYAML(cmd.InOrStdin()) },
                                func(request request) (*resty.Response, error) {
                                        s := new(databasev1.IndexRuleBinding)
                                        err := 
protojson.Unmarshal(request.data, s)
                                        if err != nil {
                                                return nil, err
                                        }
-                                       cr := 
&databasev1.IndexRuleBindingRegistryServiceUpdateRequest{
-                                               IndexRuleBinding: s,
-                                       }
+                                       cr := 
&databasev1.IndexRuleBindingRegistryServiceUpdateRequest{IndexRuleBinding: s}
                                        b, err := protojson.Marshal(cr)
                                        if err != nil {
                                                return nil, err
@@ -91,9 +87,8 @@ func newIndexRuleBindingCmd() *cobra.Command {
                                                SetPathParam("name", 
request.name).SetPathParam("group", request.group).
                                                
Put(getPath(indexRuleBindingSchemaPathWithParams))
                                },
-                               func(_ int, reqBody reqBody, _ []byte) error {
-                                       fmt.Printf("indexRuleBinding %s.%s is 
updated", reqBody.group, reqBody.name)
-                                       fmt.Println()
+                               func(w io.Writer, _ int, reqBody reqBody, _ 
[]byte) error {
+                                       fmt.Fprintf(w, "indexRuleBinding %s.%s 
is updated\n", reqBody.group, reqBody.name)
                                        return nil
                                }, enableTLS, insecure, cert)
                },
@@ -103,8 +98,8 @@ func newIndexRuleBindingCmd() *cobra.Command {
                Use:     "get [-g group] -n name",
                Version: version.Build(),
                Short:   "Get a indexRuleBinding",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.SetPathParam("name", 
request.name).SetPathParam("group", 
request.group).Get(getPath(indexRuleBindingSchemaPathWithParams))
                        }, yamlPrinter, enableTLS, insecure, cert)
                },
@@ -114,12 +109,11 @@ func newIndexRuleBindingCmd() *cobra.Command {
                Use:     "delete [-g group] -n name",
                Version: version.Build(),
                Short:   "Delete a indexRuleBinding",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.SetPathParam("name", 
request.name).SetPathParam("group", 
request.group).Delete(getPath(indexRuleBindingSchemaPathWithParams))
-                       }, func(_ int, reqBody reqBody, _ []byte) error {
-                               fmt.Printf("indexRuleBinding %s.%s is deleted", 
reqBody.group, reqBody.name)
-                               fmt.Println()
+                       }, func(w io.Writer, _ int, reqBody reqBody, _ []byte) 
error {
+                               fmt.Fprintf(w, "indexRuleBinding %s.%s is 
deleted\n", reqBody.group, reqBody.name)
                                return nil
                        }, enableTLS, insecure, cert)
                },
@@ -130,8 +124,8 @@ func newIndexRuleBindingCmd() *cobra.Command {
                Use:     "list [-g group]",
                Version: version.Build(),
                Short:   "List indexRuleBindings",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.SetPathParam("group", 
request.group).Get(getPath("/api/v1/index-rule-binding/schema/lists/{group}"))
                        }, yamlPrinter, enableTLS, insecure, cert)
                },
diff --git a/bydbctl/internal/cmd/index_rule_binding_test.go 
b/bydbctl/internal/cmd/index_rule_binding_test.go
index a58011fbe..966670a13 100644
--- a/bydbctl/internal/cmd/index_rule_binding_test.go
+++ b/bydbctl/internal/cmd/index_rule_binding_test.go
@@ -18,12 +18,12 @@
 package cmd_test
 
 import (
+       "bytes"
        "strings"
 
        . "github.com/onsi/ginkgo/v2"
        . "github.com/onsi/gomega"
        "github.com/spf13/cobra"
-       "github.com/zenizh/go-capturer"
 
        databasev1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/database/v1"
        "github.com/apache/skywalking-banyandb/bydbctl/internal/cmd"
@@ -56,12 +56,15 @@ resource_opts:
   ttl:
     unit: UNIT_DAY
     num: 7`))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               if err != nil {
-                                       GinkgoWriter.Printf("execution 
fails:%v", err)
-                               }
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       if err != nil {
+                               GinkgoWriter.Printf("execution fails:%v", err)
+                       }
+                       return buf.String()
                }
                Eventually(createGroup, 
flags.EventuallyTimeout).Should(ContainSubstring("group group1 is created"))
                rootCmd.SetArgs([]string{"indexRuleBinding", "create", "-a", 
addr, "-f", "-"})
@@ -79,22 +82,28 @@ subject:
   name: stream1
 begin_at: 2021-04-15T01:30:15.01Z
 expire_at: 2121-04-15T01:30:15.01Z`))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               if err != nil {
-                                       GinkgoWriter.Printf("execution 
fails:%v", err)
-                               }
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       if err != nil {
+                               GinkgoWriter.Printf("execution fails:%v", err)
+                       }
+                       return buf.String()
                }
                Eventually(createIndexRuleBinding, 
flags.EventuallyTimeout).Should(ContainSubstring("indexRuleBinding group1.name1 
is created"))
        })
 
        It("get indexRuleBinding schema", func() {
                rootCmd.SetArgs([]string{"indexRuleBinding", "get", "-g", 
"group1", "-n", "name1"})
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                GinkgoWriter.Println(out)
                resp := 
new(databasev1.IndexRuleBindingRegistryServiceGetResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
@@ -118,16 +127,22 @@ subject:
   name: stream2
 begin_at: 2021-04-15T01:30:15.01Z
 expire_at: 2121-04-15T01:30:15.01Z`))
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("indexRuleBinding group1.name1 
is updated"))
                rootCmd.SetArgs([]string{"indexRuleBinding", "get", "-g", 
"group1", "-n", "name1"})
-               out = capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               buf.Reset()
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err = rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out = buf.String()
                resp := 
new(databasev1.IndexRuleBindingRegistryServiceGetResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
                Expect(resp.IndexRuleBinding.Metadata.Group).To(Equal("group1"))
@@ -138,14 +153,17 @@ expire_at: 2121-04-15T01:30:15.01Z`))
        It("delete indexRuleBinding schema", func() {
                // delete
                rootCmd.SetArgs([]string{"indexRuleBinding", "delete", "-g", 
"group1", "-n", "name1"})
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("indexRuleBinding group1.name1 
is deleted"))
                // get again
                rootCmd.SetArgs([]string{"indexRuleBinding", "get", "-g", 
"group1", "-n", "name1"})
-               err := rootCmd.Execute()
+               err = rootCmd.Execute()
                Expect(err).To(MatchError("rpc error: code = NotFound desc = 
banyandb: resource not found"))
        })
 
@@ -165,17 +183,23 @@ subject:
   name: stream2
 begin_at: 2021-04-15T01:30:15.01Z
 expire_at: 2121-04-15T01:30:15.01Z`))
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("indexRuleBinding group1.name2 
is created"))
                // list
                rootCmd.SetArgs([]string{"indexRuleBinding", "list", "-g", 
"group1"})
-               out = capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               buf.Reset()
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err = rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out = buf.String()
                resp := 
new(databasev1.IndexRuleBindingRegistryServiceListResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
                Expect(resp.IndexRuleBinding).To(HaveLen(2))
diff --git a/bydbctl/internal/cmd/index_rule_test.go 
b/bydbctl/internal/cmd/index_rule_test.go
index f12224624..b919c31b7 100644
--- a/bydbctl/internal/cmd/index_rule_test.go
+++ b/bydbctl/internal/cmd/index_rule_test.go
@@ -18,12 +18,12 @@
 package cmd_test
 
 import (
+       "bytes"
        "strings"
 
        . "github.com/onsi/ginkgo/v2"
        . "github.com/onsi/gomega"
        "github.com/spf13/cobra"
-       "github.com/zenizh/go-capturer"
 
        databasev1 
"github.com/apache/skywalking-banyandb/api/proto/banyandb/database/v1"
        "github.com/apache/skywalking-banyandb/bydbctl/internal/cmd"
@@ -56,12 +56,15 @@ resource_opts:
   ttl:
     unit: UNIT_DAY
     num: 7`))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               if err != nil {
-                                       GinkgoWriter.Printf("execution 
fails:%v", err)
-                               }
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       if err != nil {
+                               GinkgoWriter.Printf("execution fails:%v", err)
+                       }
+                       return buf.String()
                }
                Eventually(createGroup, 
flags.EventuallyTimeout).Should(ContainSubstring("group group1 is created"))
                rootCmd.SetArgs([]string{"indexRule", "create", "-a", addr, 
"-f", "-"})
@@ -72,22 +75,28 @@ metadata:
   group: group1
 tags: ["layer"]
 type: TYPE_INVERTED`))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               if err != nil {
-                                       GinkgoWriter.Printf("execution 
fails:%v", err)
-                               }
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       if err != nil {
+                               GinkgoWriter.Printf("execution fails:%v", err)
+                       }
+                       return buf.String()
                }
                Eventually(createIndexRule, 
flags.EventuallyTimeout).Should(ContainSubstring("indexRule group1.name1 is 
created"))
        })
 
        It("get indexRule schema", func() {
                rootCmd.SetArgs([]string{"indexRule", "get", "-g", "group1", 
"-n", "name1"})
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                GinkgoWriter.Println(out)
                resp := new(databasev1.IndexRuleRegistryServiceGetResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
@@ -103,16 +112,22 @@ metadata:
   group: group1
 tags: ["layer"]
 type: TYPE_INVERTED`))
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("indexRule group1.name1 is 
updated"))
                rootCmd.SetArgs([]string{"indexRule", "get", "-g", "group1", 
"-n", "name1"})
-               out = capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               buf.Reset()
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err = rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out = buf.String()
                resp := new(databasev1.IndexRuleRegistryServiceGetResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
                Expect(resp.IndexRule.Metadata.Group).To(Equal("group1"))
@@ -122,14 +137,17 @@ type: TYPE_INVERTED`))
        It("delete indexRule schema", func() {
                // delete
                rootCmd.SetArgs([]string{"indexRule", "delete", "-g", "group1", 
"-n", "name1"})
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("indexRule group1.name1 is 
deleted"))
                // get again
                rootCmd.SetArgs([]string{"indexRule", "get", "-g", "group1", 
"-n", "name1"})
-               err := rootCmd.Execute()
+               err = rootCmd.Execute()
                Expect(err).To(MatchError("rpc error: code = NotFound desc = 
banyandb: resource not found"))
        })
 
@@ -142,17 +160,23 @@ metadata:
   group: group1
 tags: ["layer"]
 type: TYPE_INVERTED`))
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("indexRule group1.name2 is 
created"))
                // list
                rootCmd.SetArgs([]string{"indexRule", "list", "-g", "group1"})
-               out = capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               buf.Reset()
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err = rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out = buf.String()
                resp := new(databasev1.IndexRuleRegistryServiceListResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
                Expect(resp.IndexRule).To(HaveLen(2))
diff --git a/bydbctl/internal/cmd/measure.go b/bydbctl/internal/cmd/measure.go
index e8b9b799e..5626490a7 100644
--- a/bydbctl/internal/cmd/measure.go
+++ b/bydbctl/internal/cmd/measure.go
@@ -19,6 +19,7 @@ package cmd
 
 import (
        "fmt"
+       "io"
 
        "github.com/go-resty/resty/v2"
        "github.com/spf13/cobra"
@@ -48,7 +49,7 @@ func newMeasureCmd() *cobra.Command {
                Version: version.Build(),
                Short:   "Create measures from files",
                RunE: func(cmd *cobra.Command, _ []string) error {
-                       return rest(func() ([]reqBody, error) { return 
parseNameAndGroupFromYAML(cmd.InOrStdin()) },
+                       return rest(cmd.OutOrStdout(), func() ([]reqBody, 
error) { return parseNameAndGroupFromYAML(cmd.InOrStdin()) },
                                func(request request) (*resty.Response, error) {
                                        s := new(databasev1.Measure)
                                        err := 
protojson.Unmarshal(request.data, s)
@@ -64,9 +65,8 @@ func newMeasureCmd() *cobra.Command {
                                        }
                                        return 
request.req.SetBody(b).Post(getPath(measureSchemaPath))
                                },
-                               func(_ int, reqBody reqBody, _ []byte) error {
-                                       fmt.Printf("measure %s.%s is created", 
reqBody.group, reqBody.name)
-                                       fmt.Println()
+                               func(w io.Writer, _ int, reqBody reqBody, _ 
[]byte) error {
+                                       fmt.Fprintf(w, "measure %s.%s is 
created\n", reqBody.group, reqBody.name)
                                        return nil
                                }, enableTLS, insecure, cert)
                },
@@ -77,7 +77,7 @@ func newMeasureCmd() *cobra.Command {
                Version: version.Build(),
                Short:   "Update measures from files",
                RunE: func(cmd *cobra.Command, _ []string) (err error) {
-                       return rest(func() ([]reqBody, error) { return 
parseNameAndGroupFromYAML(cmd.InOrStdin()) },
+                       return rest(cmd.OutOrStdout(), func() ([]reqBody, 
error) { return parseNameAndGroupFromYAML(cmd.InOrStdin()) },
                                func(request request) (*resty.Response, error) {
                                        s := new(databasev1.Measure)
                                        err := 
protojson.Unmarshal(request.data, s)
@@ -95,9 +95,8 @@ func newMeasureCmd() *cobra.Command {
                                                SetPathParam("name", 
request.name).SetPathParam("group", request.group).
                                                
Put(getPath(measureSchemaPathWithParams))
                                },
-                               func(_ int, reqBody reqBody, _ []byte) error {
-                                       fmt.Printf("measure %s.%s is updated", 
reqBody.group, reqBody.name)
-                                       fmt.Println()
+                               func(w io.Writer, _ int, reqBody reqBody, _ 
[]byte) error {
+                                       fmt.Fprintf(w, "measure %s.%s is 
updated\n", reqBody.group, reqBody.name)
                                        return nil
                                }, enableTLS, insecure, cert)
                },
@@ -107,8 +106,8 @@ func newMeasureCmd() *cobra.Command {
                Use:     "get [-g group] -n name",
                Version: version.Build(),
                Short:   "Get a measure",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.SetPathParam("name", 
request.name).SetPathParam("group", 
request.group).Get(getPath(measureSchemaPathWithParams))
                        }, yamlPrinter, enableTLS, insecure, cert)
                },
@@ -118,12 +117,11 @@ func newMeasureCmd() *cobra.Command {
                Use:     "delete [-g group] -n name",
                Version: version.Build(),
                Short:   "Delete a measure",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.SetPathParam("name", 
request.name).SetPathParam("group", 
request.group).Delete(getPath(measureSchemaPathWithParams))
-                       }, func(_ int, reqBody reqBody, _ []byte) error {
-                               fmt.Printf("measure %s.%s is deleted", 
reqBody.group, reqBody.name)
-                               fmt.Println()
+                       }, func(w io.Writer, _ int, reqBody reqBody, _ []byte) 
error {
+                               fmt.Fprintf(w, "measure %s.%s is deleted\n", 
reqBody.group, reqBody.name)
                                return nil
                        }, enableTLS, insecure, cert)
                },
@@ -134,8 +132,8 @@ func newMeasureCmd() *cobra.Command {
                Use:     "list [-g group]",
                Version: version.Build(),
                Short:   "List measures",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.SetPathParam("group", 
request.group).Get(getPath(measureListPath))
                        }, yamlPrinter, enableTLS, insecure, cert)
                },
@@ -147,7 +145,7 @@ func newMeasureCmd() *cobra.Command {
                Short:   "Query data in a measure",
                Long:    timeRangeUsage,
                RunE: func(cmd *cobra.Command, _ []string) (err error) {
-                       return rest(func() ([]reqBody, error) { return 
parseTimeRangeFromFlagAndYAML(cmd.InOrStdin()) },
+                       return rest(cmd.OutOrStdout(), func() ([]reqBody, 
error) { return parseTimeRangeFromFlagAndYAML(cmd.InOrStdin()) },
                                func(request request) (*resty.Response, error) {
                                        return 
request.req.SetBody(request.data).Post(getPath(measureQueryPath))
                                }, yamlPrinter, enableTLS, insecure, cert)
diff --git a/bydbctl/internal/cmd/measure_test.go 
b/bydbctl/internal/cmd/measure_test.go
index 074394f48..48638d6b4 100644
--- a/bydbctl/internal/cmd/measure_test.go
+++ b/bydbctl/internal/cmd/measure_test.go
@@ -18,6 +18,7 @@
 package cmd_test
 
 import (
+       "bytes"
        "fmt"
        "strings"
        "time"
@@ -25,7 +26,6 @@ import (
        . "github.com/onsi/ginkgo/v2"
        . "github.com/onsi/gomega"
        "github.com/spf13/cobra"
-       "github.com/zenizh/go-capturer"
        grpclib "google.golang.org/grpc"
        "google.golang.org/grpc/credentials/insecure"
 
@@ -63,12 +63,15 @@ resource_opts:
   ttl:
     unit: UNIT_DAY
     num: 7`))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               if err != nil {
-                                       GinkgoWriter.Printf("execution 
fails:%v", err)
-                               }
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       if err != nil {
+                               GinkgoWriter.Printf("execution fails:%v", err)
+                       }
+                       return buf.String()
                }
                Eventually(createGroup, 
flags.EventuallyTimeout).Should(ContainSubstring("group group1 is created"))
                rootCmd.SetArgs([]string{"measure", "create", "-a", addr, "-f", 
"-"})
@@ -84,22 +87,28 @@ tag_families:
         type: TAG_TYPE_STRING
 entity:
   tagNames: ["id"]`))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               if err != nil {
-                                       GinkgoWriter.Printf("execution 
fails:%v", err)
-                               }
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       if err != nil {
+                               GinkgoWriter.Printf("execution fails:%v", err)
+                       }
+                       return buf.String()
                }
                Eventually(createMeasure, 
flags.EventuallyTimeout).Should(ContainSubstring("measure group1.name1 is 
created"))
        })
 
        It("get measure schema", func() {
                rootCmd.SetArgs([]string{"measure", "get", "-g", "group1", 
"-n", "name1"})
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                resp := new(databasev1.MeasureRegistryServiceGetResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
                Expect(resp.Measure.Metadata.Group).To(Equal("group1"))
@@ -121,16 +130,22 @@ tag_families:
         type: TAG_TYPE_STRING
 entity:
   tagNames: ["id"]`))
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("measure group1.name1 is 
updated"))
                rootCmd.SetArgs([]string{"measure", "get", "-g", "group1", 
"-n", "name1"})
-               out = capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               buf.Reset()
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err = rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out = buf.String()
                resp := new(databasev1.MeasureRegistryServiceGetResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
                Expect(resp.Measure.Metadata.Group).To(Equal("group1"))
@@ -141,14 +156,17 @@ entity:
        It("delete measure schema", func() {
                // delete
                rootCmd.SetArgs([]string{"measure", "delete", "-g", "group1", 
"-n", "name1"})
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("measure group1.name1 is 
deleted"))
                // get again
                rootCmd.SetArgs([]string{"measure", "get", "-g", "group1", 
"-n", "name1"})
-               err := rootCmd.Execute()
+               err = rootCmd.Execute()
                Expect(err).To(MatchError("rpc error: code = NotFound desc = 
banyandb: resource not found"))
        })
 
@@ -166,17 +184,23 @@ tag_families:
         type: TAG_TYPE_STRING
 entity:
   tagNames: ["tag1"]`))
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("measure group1.name2 is 
created"))
                // list
                rootCmd.SetArgs([]string{"measure", "list", "-g", "group1"})
-               out = capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               buf.Reset()
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err = rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out = buf.String()
                resp := new(databasev1.MeasureRegistryServiceListResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
                // There is a _topn_result measure created by default
@@ -228,10 +252,13 @@ tagProjection:
     - name: default
       tags:
         - id`, startStr, endStr)))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               Expect(err).NotTo(HaveOccurred())
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       Expect(err).NotTo(HaveOccurred())
+                       return buf.String()
                }
                Eventually(issue, 
flags.EventuallyTimeout).ShouldNot(ContainSubstring("code:"))
                Eventually(func() int {
@@ -266,10 +293,13 @@ tagProjection:
    - name: default
      tags:
        - id`))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               Expect(err).NotTo(HaveOccurred())
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       Expect(err).NotTo(HaveOccurred())
+                       return buf.String()
                }
                Eventually(issue, 
flags.EventuallyTimeout).ShouldNot(ContainSubstring("code:"))
                Eventually(func() int {
diff --git a/bydbctl/internal/cmd/property.go b/bydbctl/internal/cmd/property.go
index 460e04139..4605aae05 100644
--- a/bydbctl/internal/cmd/property.go
+++ b/bydbctl/internal/cmd/property.go
@@ -19,6 +19,7 @@ package cmd
 
 import (
        "fmt"
+       "io"
 
        "github.com/go-resty/resty/v2"
        "github.com/spf13/cobra"
@@ -68,7 +69,7 @@ func newPropertyCmd() *cobra.Command {
                Version: version.Build(),
                Short:   "Create property schemas from files",
                RunE: func(cmd *cobra.Command, _ []string) error {
-                       return rest(func() ([]reqBody, error) { return 
parseNameAndGroupFromYAML(cmd.InOrStdin()) },
+                       return rest(cmd.OutOrStdout(), func() ([]reqBody, 
error) { return parseNameAndGroupFromYAML(cmd.InOrStdin()) },
                                func(request request) (*resty.Response, error) {
                                        s := new(databasev1.Property)
                                        err := 
protojson.Unmarshal(request.data, s)
@@ -84,9 +85,8 @@ func newPropertyCmd() *cobra.Command {
                                        }
                                        return 
request.req.SetBody(b).Post(getPath(propertySchemaPath))
                                },
-                               func(_ int, reqBody reqBody, _ []byte) error {
-                                       fmt.Printf("property schema %s.%s is 
created", reqBody.group, reqBody.name)
-                                       fmt.Println()
+                               func(w io.Writer, _ int, reqBody reqBody, _ 
[]byte) error {
+                                       fmt.Fprintf(w, "property schema %s.%s 
is created\n", reqBody.group, reqBody.name)
                                        return nil
                                }, enableTLS, insecure, cert)
                },
@@ -97,7 +97,7 @@ func newPropertyCmd() *cobra.Command {
                Version: version.Build(),
                Short:   "Update property schemas from files",
                RunE: func(cmd *cobra.Command, _ []string) error {
-                       return rest(func() ([]reqBody, error) { return 
parseNameAndGroupFromYAML(cmd.InOrStdin()) },
+                       return rest(cmd.OutOrStdout(), func() ([]reqBody, 
error) { return parseNameAndGroupFromYAML(cmd.InOrStdin()) },
                                func(request request) (*resty.Response, error) {
                                        s := new(databasev1.Property)
                                        err := 
protojson.Unmarshal(request.data, s)
@@ -115,9 +115,8 @@ func newPropertyCmd() *cobra.Command {
                                                SetPathParam("name", 
request.name).SetPathParam("group", request.group).
                                                
Put(getPath(propertySchemaPathWithParams))
                                },
-                               func(_ int, reqBody reqBody, _ []byte) error {
-                                       fmt.Printf("property schema %s.%s is 
updated", reqBody.group, reqBody.name)
-                                       fmt.Println()
+                               func(w io.Writer, _ int, reqBody reqBody, _ 
[]byte) error {
+                                       fmt.Fprintf(w, "property schema %s.%s 
is updated\n", reqBody.group, reqBody.name)
                                        return nil
                                }, enableTLS, insecure, cert)
                },
@@ -127,8 +126,8 @@ func newPropertyCmd() *cobra.Command {
                Use:     "get [-g group] -n name",
                Version: version.Build(),
                Short:   "Get a property schema",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.SetPathParam("name", 
request.name).SetPathParam("group", 
request.group).Get(getPath(propertySchemaPathWithParams))
                        }, yamlPrinter, enableTLS, insecure, cert)
                },
@@ -138,12 +137,11 @@ func newPropertyCmd() *cobra.Command {
                Use:     "delete [-g group] -n name",
                Version: version.Build(),
                Short:   "Delete a property schema",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.SetPathParam("name", 
request.name).SetPathParam("group", 
request.group).Delete(getPath(propertySchemaPathWithParams))
-                       }, func(_ int, reqBody reqBody, _ []byte) error {
-                               fmt.Printf("property schema %s.%s is deleted", 
reqBody.group, reqBody.name)
-                               fmt.Println()
+                       }, func(w io.Writer, _ int, reqBody reqBody, _ []byte) 
error {
+                               fmt.Fprintf(w, "property schema %s.%s is 
deleted\n", reqBody.group, reqBody.name)
                                return nil
                        }, enableTLS, insecure, cert)
                },
@@ -154,8 +152,8 @@ func newPropertyCmd() *cobra.Command {
                Use:     "list [-g group]",
                Version: version.Build(),
                Short:   "List property schemas",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.SetPathParam("group", 
request.group).Get(getPath("/api/v1/property/schema/lists/{group}"))
                        }, yamlPrinter, enableTLS, insecure, cert)
                },
@@ -168,7 +166,7 @@ func newPropertyCmd() *cobra.Command {
                Version: version.Build(),
                Short:   "Apply(Create or Update) properties data from files",
                RunE: func(cmd *cobra.Command, _ []string) error {
-                       return rest(func() ([]reqBody, error) { return 
parseFromYAMLForProperty(cmd.InOrStdin()) },
+                       return rest(cmd.OutOrStdout(), func() ([]reqBody, 
error) { return parseFromYAMLForProperty(cmd.InOrStdin()) },
                                func(request request) (*resty.Response, error) {
                                        s := new(propertyv1.Property)
                                        err := 
protojson.Unmarshal(request.data, s)
@@ -193,8 +191,8 @@ func newPropertyCmd() *cobra.Command {
                Use:     "delete -g group -n name [-i id]",
                Version: version.Build(),
                Short:   "Delete property data",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.SetPathParam("name", 
request.name).SetPathParam("group", request.group).
                                        SetPathParam("id", 
request.id).Delete(getPath(propertyDataPath))
                        }, yamlPrinter, enableTLS, insecure, cert)
@@ -208,7 +206,7 @@ func newPropertyCmd() *cobra.Command {
                Short:   "Query property data from files",
                Long:    timeRangeUsage,
                RunE: func(cmd *cobra.Command, _ []string) (err error) {
-                       return rest(func() ([]reqBody, error) { return 
simpleParseFromYAML(cmd.InOrStdin()) },
+                       return rest(cmd.OutOrStdout(), func() ([]reqBody, 
error) { return simpleParseFromYAML(cmd.InOrStdin()) },
                                func(request request) (*resty.Response, error) {
                                        return 
request.req.SetBody(request.data).Post(getPath(propertyQueryPath))
                                }, yamlPrinter, enableTLS, insecure, cert)
diff --git a/bydbctl/internal/cmd/property_test.go 
b/bydbctl/internal/cmd/property_test.go
index 4e8963464..a1fb937f8 100644
--- a/bydbctl/internal/cmd/property_test.go
+++ b/bydbctl/internal/cmd/property_test.go
@@ -18,6 +18,7 @@
 package cmd_test
 
 import (
+       "bytes"
        "context"
        "errors"
        "fmt"
@@ -30,7 +31,6 @@ import (
        . "github.com/onsi/ginkgo/v2"
        . "github.com/onsi/gomega"
        "github.com/spf13/cobra"
-       "github.com/zenizh/go-capturer"
        "google.golang.org/protobuf/encoding/protojson"
 
        "github.com/apache/skywalking-banyandb/api/common"
@@ -142,10 +142,13 @@ var _ = Describe("Property Operation", func() {
        It("apply same property after delete", func() {
                // delete
                rootCmd.SetArgs([]string{"property", "data", "delete", "-g", 
"ui-template", "-n", "service", "-i", "kubernetes"})
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("deleted: true"))
 
                // apply property(created should be true)
@@ -159,10 +162,13 @@ var _ = Describe("Property Operation", func() {
        It("delete property", func() {
                // delete
                rootCmd.SetArgs([]string{"property", "data", "delete", "-g", 
"ui-template", "-n", "service", "-i", "kubernetes"})
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("deleted: true"))
 
                queryData(rootCmd, addr, propertyGroup, "", 0, nil)
@@ -191,12 +197,15 @@ catalog: CATALOG_PROPERTY
 resource_opts:
   shard_num: 2
 `))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               if err != nil {
-                                       GinkgoWriter.Printf("execution 
fails:%v", err)
-                               }
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       if err != nil {
+                               GinkgoWriter.Printf("execution fails:%v", err)
+                       }
+                       return buf.String()
                }
                Eventually(createGroup, 
flags.EventuallyTimeout).Should(ContainSubstring("group ui-template is 
created"))
        })
@@ -213,10 +222,13 @@ tags:
   - name: state
     type: TAG_TYPE_INT
 `))
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("property schema 
ui-template.service is created"))
        })
 
@@ -233,18 +245,24 @@ tags:
   - name: state
     type: TAG_TYPE_INT
 `))
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("property schema 
ui-template.service is created"))
 
                // Then get the schema
                rootCmd.SetArgs([]string{"property", "schema", "get", "-g", 
"ui-template", "-n", "service"})
-               out = capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               buf.Reset()
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err = rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out = buf.String()
                resp := new(databasev1.PropertyRegistryServiceGetResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
                Expect(resp.Property.Metadata.Group).To(Equal("ui-template"))
@@ -267,10 +285,13 @@ tags:
   - name: state
     type: TAG_TYPE_INT
 `))
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("property schema 
ui-template.service is created"))
 
                // Then update the schema
@@ -287,18 +308,24 @@ tags:
   - name: version
     type: TAG_TYPE_STRING
 `))
-               out = capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               buf.Reset()
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err = rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out = buf.String()
                Expect(out).To(ContainSubstring("property schema 
ui-template.service is updated"))
 
                // Verify the update
                rootCmd.SetArgs([]string{"property", "schema", "get", "-g", 
"ui-template", "-n", "service"})
-               out = capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               buf.Reset()
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err = rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out = buf.String()
                resp := new(databasev1.PropertyRegistryServiceGetResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
                Expect(resp.Property.Tags).To(HaveLen(3))
@@ -318,23 +345,29 @@ tags:
   - name: state
     type: TAG_TYPE_INT
 `))
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("property schema 
ui-template.service is created"))
 
                // Delete the schema
                rootCmd.SetArgs([]string{"property", "schema", "delete", "-g", 
"ui-template", "-n", "service"})
-               out = capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               buf.Reset()
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err = rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out = buf.String()
                Expect(out).To(ContainSubstring("property schema 
ui-template.service is deleted"))
 
                // Verify deletion
                rootCmd.SetArgs([]string{"property", "schema", "get", "-g", 
"ui-template", "-n", "service"})
-               err := rootCmd.Execute()
+               err = rootCmd.Execute()
                Expect(err).To(MatchError(ContainSubstring("not found")))
        })
 
@@ -351,10 +384,13 @@ tags:
   - name: state
     type: TAG_TYPE_INT
 `))
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("property schema 
ui-template.service is created"))
 
                rootCmd.SetArgs([]string{"property", "schema", "create", "-a", 
addr, "-f", "-"})
@@ -366,18 +402,24 @@ tags:
 - name: content
   type: TAG_TYPE_STRING
 `))
-               out = capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               buf.Reset()
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err = rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out = buf.String()
                Expect(out).To(ContainSubstring("property schema 
ui-template.endpoint is created"))
 
                // List all schemas
                rootCmd.SetArgs([]string{"property", "schema", "list", "-g", 
"ui-template"})
-               out = capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               buf.Reset()
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err = rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out = buf.String()
                resp := new(databasev1.PropertyRegistryServiceListResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
                Expect(resp.Properties).To(HaveLen(2))
@@ -666,10 +708,13 @@ projection:
     - name: searchable
       tags:
         - trace_id`))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               Expect(err).NotTo(HaveOccurred())
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       Expect(err).NotTo(HaveOccurred())
+                       return buf.String()
                }
                Eventually(issue, 
flags.EventuallyTimeout).ShouldNot(ContainSubstring("code:"))
                Eventually(func() int {
@@ -923,12 +968,15 @@ resource_opts:
   shard_num: %d
   replicas: %d
 `, shardCount, replicas)))
-               return capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       if err != nil {
-                               GinkgoWriter.Printf("execution fails:%v", err)
-                       }
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               if err != nil {
+                       GinkgoWriter.Printf("execution fails:%v", err)
+               }
+               return buf.String()
        }
        Eventually(createGroup, 
flags.EventuallyTimeout).Should(ContainSubstring("group ui-template is 
created"))
 
@@ -944,12 +992,15 @@ tags:
   - name: state
     type: TAG_TYPE_INT
 `))
-               return capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       if err != nil {
-                               GinkgoWriter.Printf("execution fails:%v", err)
-                       }
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               if err != nil {
+                       GinkgoWriter.Printf("execution fails:%v", err)
+               }
+               return buf.String()
        }
        Eventually(createPropertySchema, 
flags.EventuallyTimeout).Should(ContainSubstring("property schema 
ui-template.service is created"))
 }
@@ -957,10 +1008,13 @@ tags:
 func applyData(rootCmd *cobra.Command, addr, data string, created bool, 
tagsNum int) {
        rootCmd.SetArgs([]string{"property", "data", "apply", "-a", addr, "-f", 
"-"})
        rootCmd.SetIn(strings.NewReader(data))
-       out := capturer.CaptureStdout(func() {
-               err := rootCmd.Execute()
-               Expect(err).NotTo(HaveOccurred())
-       })
+       var buf bytes.Buffer
+       rootCmd.SetOut(&buf)
+       rootCmd.SetErr(&buf)
+
+       err := rootCmd.Execute()
+       Expect(err).NotTo(HaveOccurred())
+       out := buf.String()
        GinkgoWriter.Println(out)
        Expect(out).To(ContainSubstring(fmt.Sprintf("created: %t", created)))
        Expect(out).To(ContainSubstring(fmt.Sprintf("tagsNum: %d", tagsNum)))
@@ -974,10 +1028,13 @@ func queryData(rootCmd *cobra.Command, addr, group, id 
string, dataCount int, ve
                        query += fmt.Sprintf("\nids: [\"%s\"]", id)
                }
                rootCmd.SetIn(strings.NewReader(query))
-               return capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               return buf.String()
        }
        Eventually(func() error {
                out := issue()
@@ -1001,10 +1058,13 @@ func queryData(rootCmd *cobra.Command, addr, group, id 
string, dataCount int, ve
 
 func deleteData(rootCmd *cobra.Command, addr, group, name, id string, success 
bool) {
        rootCmd.SetArgs([]string{"property", "data", "delete", "-a", addr, 
"-g", group, "-n", name, "-i", id})
-       out := capturer.CaptureStdout(func() {
-               err := rootCmd.Execute()
-               Expect(err).NotTo(HaveOccurred())
-       })
+       var buf bytes.Buffer
+       rootCmd.SetOut(&buf)
+       rootCmd.SetErr(&buf)
+
+       err := rootCmd.Execute()
+       Expect(err).NotTo(HaveOccurred())
+       out := buf.String()
        Expect(out).To(ContainSubstring("deleted: %t", success))
 }
 
diff --git a/bydbctl/internal/cmd/rest.go b/bydbctl/internal/cmd/rest.go
index 2ec57448b..6031b659f 100644
--- a/bydbctl/internal/cmd/rest.go
+++ b/bydbctl/internal/cmd/rest.go
@@ -301,22 +301,22 @@ func parseFromYAMLForProperty(reader io.Reader) (requests 
[]reqBody, err error)
        return requests, nil
 }
 
-type printer func(index int, reqBody reqBody, body []byte) error
+type printer func(w io.Writer, index int, reqBody reqBody, body []byte) error
 
-func yamlPrinter(index int, _ reqBody, body []byte) error {
+func yamlPrinter(w io.Writer, index int, _ reqBody, body []byte) error {
        yamlResult, err := yaml.JSONToYAML(body)
        if err != nil {
                return err
        }
        if index > 0 {
-               fmt.Println("---")
+               fmt.Fprintln(w, "---")
        }
-       fmt.Print(string(yamlResult))
-       fmt.Println()
+       fmt.Fprint(w, string(yamlResult))
+       fmt.Fprintln(w)
        return nil
 }
 
-func rest(pfn paramsFn, fn reqFn, printer printer, enableTLS bool, insecure 
bool, cert string) (err error) {
+func rest(w io.Writer, pfn paramsFn, fn reqFn, printer printer, enableTLS 
bool, insecure bool, cert string) (err error) {
        var requests []reqBody
        if pfn == nil {
                requests = []reqBody{{}}
@@ -370,7 +370,7 @@ func rest(pfn paramsFn, fn reqFn, printer printer, 
enableTLS bool, insecure bool
                if resp.StatusCode() != http.StatusOK {
                        return fmt.Errorf("unexpected HTTP status code: %d", 
resp.StatusCode())
                }
-               err = printer(i, r, bd)
+               err = printer(w, i, r, bd)
                if err != nil {
                        return err
                }
diff --git a/bydbctl/internal/cmd/root.go b/bydbctl/internal/cmd/root.go
index c83f2752c..2117fdafe 100644
--- a/bydbctl/internal/cmd/root.go
+++ b/bydbctl/internal/cmd/root.go
@@ -118,15 +118,16 @@ func initConfig() {
                        return err
                }
                configFile := viper.ConfigFileUsed()
+               errWriter := rootCmd.ErrOrStderr()
                info, err := os.Stat(configFile)
                if err != nil {
                        return fmt.Errorf("unable to stat config file: %w", err)
                }
                if info.Mode().Perm() != 0o600 {
-                       fmt.Printf("config file %s has unsafe permissions: %o 
(expected 0600)", configFile, info.Mode().Perm())
+                       fmt.Fprintf(errWriter, "config file %s has unsafe 
permissions: %o (expected 0600)\n", configFile, info.Mode().Perm())
                }
                // Dump this to stderr in case of mixing up response yaml
-               fmt.Fprintln(os.Stderr, "Using config file:", configFile)
+               fmt.Fprintln(errWriter, "Using config file:", configFile)
                return nil
        }
 
diff --git a/bydbctl/internal/cmd/stream.go b/bydbctl/internal/cmd/stream.go
index 6525a23d6..95e23a7ed 100644
--- a/bydbctl/internal/cmd/stream.go
+++ b/bydbctl/internal/cmd/stream.go
@@ -19,6 +19,7 @@ package cmd
 
 import (
        "fmt"
+       "io"
 
        "github.com/go-resty/resty/v2"
        "github.com/spf13/cobra"
@@ -53,7 +54,7 @@ func newStreamCmd() *cobra.Command {
                Version: version.Build(),
                Short:   "Create streams from files",
                RunE: func(cmd *cobra.Command, _ []string) error {
-                       return rest(func() ([]reqBody, error) { return 
parseNameAndGroupFromYAML(cmd.InOrStdin()) },
+                       return rest(cmd.OutOrStdout(), func() ([]reqBody, 
error) { return parseNameAndGroupFromYAML(cmd.InOrStdin()) },
                                func(request request) (*resty.Response, error) {
                                        s := new(databasev1.Stream)
                                        err := 
protojson.Unmarshal(request.data, s)
@@ -69,9 +70,8 @@ func newStreamCmd() *cobra.Command {
                                        }
                                        return 
request.req.SetBody(b).Post(getPath(streamSchemaPath))
                                },
-                               func(_ int, reqBody reqBody, _ []byte) error {
-                                       fmt.Printf("stream %s.%s is created", 
reqBody.group, reqBody.name)
-                                       fmt.Println()
+                               func(w io.Writer, _ int, reqBody reqBody, _ 
[]byte) error {
+                                       fmt.Fprintf(w, "stream %s.%s is 
created\n", reqBody.group, reqBody.name)
                                        return nil
                                }, enableTLS, insecure, cert)
                },
@@ -82,7 +82,7 @@ func newStreamCmd() *cobra.Command {
                Version: version.Build(),
                Short:   "Update streams from files",
                RunE: func(cmd *cobra.Command, _ []string) (err error) {
-                       return rest(func() ([]reqBody, error) { return 
parseNameAndGroupFromYAML(cmd.InOrStdin()) },
+                       return rest(cmd.OutOrStdout(), func() ([]reqBody, 
error) { return parseNameAndGroupFromYAML(cmd.InOrStdin()) },
                                func(request request) (*resty.Response, error) {
                                        s := new(databasev1.Stream)
                                        err := 
protojson.Unmarshal(request.data, s)
@@ -100,9 +100,8 @@ func newStreamCmd() *cobra.Command {
                                                SetPathParam("name", 
request.name).SetPathParam("group", request.group).
                                                
Put(getPath(streamSchemaPathWithParams))
                                },
-                               func(_ int, reqBody reqBody, _ []byte) error {
-                                       fmt.Printf("stream %s.%s is updated", 
reqBody.group, reqBody.name)
-                                       fmt.Println()
+                               func(w io.Writer, _ int, reqBody reqBody, _ 
[]byte) error {
+                                       fmt.Fprintf(w, "stream %s.%s is 
updated\n", reqBody.group, reqBody.name)
                                        return nil
                                }, enableTLS, insecure, cert)
                },
@@ -112,8 +111,8 @@ func newStreamCmd() *cobra.Command {
                Use:     "get [-g group] -n name",
                Version: version.Build(),
                Short:   "Get a stream",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.SetPathParam("name", 
request.name).SetPathParam("group", 
request.group).Get(getPath(streamSchemaPathWithParams))
                        }, yamlPrinter, enableTLS, insecure, cert)
                },
@@ -123,12 +122,11 @@ func newStreamCmd() *cobra.Command {
                Use:     "delete [-g group] -n name",
                Version: version.Build(),
                Short:   "Delete a stream",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.SetPathParam("name", 
request.name).SetPathParam("group", 
request.group).Delete(getPath(streamSchemaPathWithParams))
-                       }, func(_ int, reqBody reqBody, _ []byte) error {
-                               fmt.Printf("stream %s.%s is deleted", 
reqBody.group, reqBody.name)
-                               fmt.Println()
+                       }, func(w io.Writer, _ int, reqBody reqBody, _ []byte) 
error {
+                               fmt.Fprintf(w, "stream %s.%s is deleted\n", 
reqBody.group, reqBody.name)
                                return nil
                        }, enableTLS, insecure, cert)
                },
@@ -139,8 +137,8 @@ func newStreamCmd() *cobra.Command {
                Use:     "list [-g group]",
                Version: version.Build(),
                Short:   "List streams",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.SetPathParam("group", 
request.group).Get(getPath(streamListPath))
                        }, yamlPrinter, enableTLS, insecure, cert)
                },
@@ -152,7 +150,7 @@ func newStreamCmd() *cobra.Command {
                Short:   "Query data in a stream",
                Long:    timeRangeUsage,
                RunE: func(cmd *cobra.Command, _ []string) (err error) {
-                       return rest(func() ([]reqBody, error) { return 
parseTimeRangeFromFlagAndYAML(cmd.InOrStdin()) },
+                       return rest(cmd.OutOrStdout(), func() ([]reqBody, 
error) { return parseTimeRangeFromFlagAndYAML(cmd.InOrStdin()) },
                                func(request request) (*resty.Response, error) {
                                        return 
request.req.SetBody(request.data).Post(getPath(streamQueryPath))
                                }, yamlPrinter, enableTLS, insecure, cert)
diff --git a/bydbctl/internal/cmd/stream_test.go 
b/bydbctl/internal/cmd/stream_test.go
index 8c028354c..ff3ac9976 100644
--- a/bydbctl/internal/cmd/stream_test.go
+++ b/bydbctl/internal/cmd/stream_test.go
@@ -18,6 +18,7 @@
 package cmd_test
 
 import (
+       "bytes"
        "fmt"
        "strings"
        "time"
@@ -25,7 +26,6 @@ import (
        . "github.com/onsi/ginkgo/v2"
        . "github.com/onsi/gomega"
        "github.com/spf13/cobra"
-       "github.com/zenizh/go-capturer"
        grpclib "google.golang.org/grpc"
        "google.golang.org/grpc/credentials/insecure"
 
@@ -63,12 +63,15 @@ resource_opts:
   ttl:
     unit: UNIT_DAY
     num: 7`))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               if err != nil {
-                                       GinkgoWriter.Printf("execution 
fails:%v", err)
-                               }
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       if err != nil {
+                               GinkgoWriter.Printf("execution fails:%v", err)
+                       }
+                       return buf.String()
                }
                Eventually(createGroup, 
flags.EventuallyTimeout).Should(ContainSubstring("group group1 is created"))
                rootCmd.SetArgs([]string{"stream", "create", "-a", addr, "-f", 
"-"})
@@ -84,22 +87,28 @@ tagFamilies:
         type: TAG_TYPE_STRING
 entity:
   tagNames: ["tag1"]`))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               if err != nil {
-                                       GinkgoWriter.Printf("execution 
fails:%v", err)
-                               }
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       if err != nil {
+                               GinkgoWriter.Printf("execution fails:%v", err)
+                       }
+                       return buf.String()
                }
                Eventually(createStream, 
flags.EventuallyTimeout).Should(ContainSubstring("stream group1.name1 is 
created"))
        })
 
        It("get stream schema", func() {
                rootCmd.SetArgs([]string{"stream", "get", "-g", "group1", "-n", 
"name1"})
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                GinkgoWriter.Println(out)
                resp := new(databasev1.StreamRegistryServiceGetResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
@@ -122,16 +131,22 @@ tagFamilies:
         type: TAG_TYPE_STRING
 entity:
   tagNames: ["tag1"]`))
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("stream group1.name1 is 
updated"))
                rootCmd.SetArgs([]string{"stream", "get", "-g", "group1", "-n", 
"name1"})
-               out = capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               buf.Reset()
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err = rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out = buf.String()
                resp := new(databasev1.StreamRegistryServiceGetResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
                Expect(resp.Stream.Metadata.Group).To(Equal("group1"))
@@ -142,14 +157,17 @@ entity:
        It("delete stream schema", func() {
                // delete
                rootCmd.SetArgs([]string{"stream", "delete", "-g", "group1", 
"-n", "name1"})
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("stream group1.name1 is 
deleted"))
                // get again
                rootCmd.SetArgs([]string{"stream", "get", "-g", "group1", "-n", 
"name1"})
-               err := rootCmd.Execute()
+               err = rootCmd.Execute()
                Expect(err).To(MatchError("rpc error: code = NotFound desc = 
banyandb: resource not found"))
        })
 
@@ -167,17 +185,23 @@ tagFamilies:
         type: TAG_TYPE_STRING
 entity:
   tagNames: ["tag1"]`))
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("stream group1.name2 is 
created"))
                // list
                rootCmd.SetArgs([]string{"stream", "list", "-g", "group1"})
-               out = capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               buf.Reset()
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err = rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out = buf.String()
                resp := new(databasev1.StreamRegistryServiceListResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
                Expect(resp.Stream).To(HaveLen(2))
@@ -229,10 +253,13 @@ projection:
     - name: searchable
       tags:
         - trace_id`, nowStr, endStr)))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               Expect(err).NotTo(HaveOccurred())
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       Expect(err).NotTo(HaveOccurred())
+                       return buf.String()
                }
                Eventually(issue, 
flags.EventuallyTimeout).ShouldNot(ContainSubstring("code:"))
                Eventually(func() int {
@@ -266,10 +293,13 @@ projection:
     - name: searchable
       tags:
         - trace_id`))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               Expect(err).NotTo(HaveOccurred())
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       Expect(err).NotTo(HaveOccurred())
+                       return buf.String()
                }
                Eventually(issue, 
flags.EventuallyTimeout).ShouldNot(ContainSubstring("code:"))
                Eventually(func() int {
diff --git a/bydbctl/internal/cmd/topn.go b/bydbctl/internal/cmd/topn.go
index 5c30f8922..e46d09d4c 100644
--- a/bydbctl/internal/cmd/topn.go
+++ b/bydbctl/internal/cmd/topn.go
@@ -19,6 +19,7 @@ package cmd
 
 import (
        "fmt"
+       "io"
 
        "github.com/go-resty/resty/v2"
        "github.com/spf13/cobra"
@@ -49,7 +50,7 @@ func newTopnCmd() *cobra.Command {
                Version: version.Build(),
                Short:   "Create topn from files",
                RunE: func(cmd *cobra.Command, _ []string) error {
-                       return rest(func() ([]reqBody, error) { return 
parseNameAndGroupFromYAML(cmd.InOrStdin()) },
+                       return rest(cmd.OutOrStdout(), func() ([]reqBody, 
error) { return parseNameAndGroupFromYAML(cmd.InOrStdin()) },
                                func(request request) (*resty.Response, error) {
                                        s := new(databasev1.TopNAggregation)
                                        err := 
protojson.Unmarshal(request.data, s)
@@ -65,9 +66,8 @@ func newTopnCmd() *cobra.Command {
                                        }
                                        return 
request.req.SetBody(b).Post(getPath(topnSchemaPath))
                                },
-                               func(_ int, reqBody reqBody, _ []byte) error {
-                                       fmt.Printf("topn %s.%s is created", 
reqBody.group, reqBody.name)
-                                       fmt.Println()
+                               func(w io.Writer, _ int, reqBody reqBody, _ 
[]byte) error {
+                                       fmt.Fprintf(w, "topn %s.%s is 
created\n", reqBody.group, reqBody.name)
                                        return nil
                                }, enableTLS, insecure, cert)
                },
@@ -79,7 +79,7 @@ func newTopnCmd() *cobra.Command {
                Version: version.Build(),
                Short:   "Update topn from files",
                RunE: func(cmd *cobra.Command, _ []string) (err error) {
-                       return rest(func() ([]reqBody, error) { return 
parseNameAndGroupFromYAML(cmd.InOrStdin()) },
+                       return rest(cmd.OutOrStdout(), func() ([]reqBody, 
error) { return parseNameAndGroupFromYAML(cmd.InOrStdin()) },
                                func(request request) (*resty.Response, error) {
                                        s := new(databasev1.TopNAggregation)
                                        err := 
protojson.Unmarshal(request.data, s)
@@ -97,9 +97,8 @@ func newTopnCmd() *cobra.Command {
                                                SetPathParam("name", 
request.name).SetPathParam("group", request.group).
                                                
Put(getPath(topnSchemaPathWithParams))
                                },
-                               func(_ int, reqBody reqBody, _ []byte) error {
-                                       fmt.Printf("topn %s.%s is updated", 
reqBody.group, reqBody.name)
-                                       fmt.Println()
+                               func(w io.Writer, _ int, reqBody reqBody, _ 
[]byte) error {
+                                       fmt.Fprintf(w, "topn %s.%s is 
updated\n", reqBody.group, reqBody.name)
                                        return nil
                                }, enableTLS, insecure, cert)
                },
@@ -110,8 +109,8 @@ func newTopnCmd() *cobra.Command {
                Use:     "get [-g group] -n name",
                Version: version.Build(),
                Short:   "Get a topn",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.SetPathParam("name", 
request.name).SetPathParam("group", 
request.group).Get(getPath(topnSchemaPathWithParams))
                        }, yamlPrinter, enableTLS, insecure, cert)
                },
@@ -121,12 +120,11 @@ func newTopnCmd() *cobra.Command {
                Use:     "delete [-g group] -n name",
                Version: version.Build(),
                Short:   "Delete a topn",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.SetPathParam("name", 
request.name).SetPathParam("group", 
request.group).Delete(getPath(topnSchemaPathWithParams))
-                       }, func(_ int, reqBody reqBody, _ []byte) error {
-                               fmt.Printf("topn %s.%s is deleted", 
reqBody.group, reqBody.name)
-                               fmt.Println()
+                       }, func(w io.Writer, _ int, reqBody reqBody, _ []byte) 
error {
+                               fmt.Fprintf(w, "topn %s.%s is deleted\n", 
reqBody.group, reqBody.name)
                                return nil
                        }, enableTLS, insecure, cert)
                },
@@ -138,8 +136,8 @@ func newTopnCmd() *cobra.Command {
                Use:     "list [-g group]",
                Version: version.Build(),
                Short:   "List topn",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.SetPathParam("group", 
request.group).Get(getPath(topnListPath))
                        }, yamlPrinter, enableTLS, insecure, cert)
                },
@@ -152,7 +150,7 @@ func newTopnCmd() *cobra.Command {
                Short:   "Query data in a topn",
                Long:    timeRangeUsage,
                RunE: func(cmd *cobra.Command, _ []string) (err error) {
-                       return rest(func() ([]reqBody, error) { return 
parseTimeRangeFromFlagAndYAML(cmd.InOrStdin()) },
+                       return rest(cmd.OutOrStdout(), func() ([]reqBody, 
error) { return parseTimeRangeFromFlagAndYAML(cmd.InOrStdin()) },
                                func(request request) (*resty.Response, error) {
                                        return 
request.req.SetBody(request.data).Post(getPath(topnQueryPath))
                                }, yamlPrinter, enableTLS, insecure, cert)
diff --git a/bydbctl/internal/cmd/topn_test.go 
b/bydbctl/internal/cmd/topn_test.go
index b3d2c1a2c..0ef47328c 100644
--- a/bydbctl/internal/cmd/topn_test.go
+++ b/bydbctl/internal/cmd/topn_test.go
@@ -18,6 +18,7 @@
 package cmd_test
 
 import (
+       "bytes"
        "fmt"
        "strings"
        "time"
@@ -25,7 +26,6 @@ import (
        . "github.com/onsi/ginkgo/v2"
        . "github.com/onsi/gomega"
        "github.com/spf13/cobra"
-       "github.com/zenizh/go-capturer"
        grpclib "google.golang.org/grpc"
        "google.golang.org/grpc/credentials/insecure"
 
@@ -63,12 +63,15 @@ resource_opts:
   ttl:
     unit: UNIT_DAY
     num: 7`))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               if err != nil {
-                                       GinkgoWriter.Printf("execution 
fails:%v", err)
-                               }
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       if err != nil {
+                               GinkgoWriter.Printf("execution fails:%v", err)
+                       }
+                       return buf.String()
                }
                Eventually(createGroup, 
flags.EventuallyTimeout).Should(ContainSubstring("group group1 is created"))
                rootCmd.SetArgs([]string{"measure", "create", "-a", addr, "-f", 
"-"})
@@ -97,12 +100,15 @@ fields:
 entity:
   tag_names:
   - id`))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               if err != nil {
-                                       GinkgoWriter.Printf("execution 
fails:%v", err)
-                               }
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       if err != nil {
+                               GinkgoWriter.Printf("execution fails:%v", err)
+                       }
+                       return buf.String()
                }
                Eventually(createMeasure, 
flags.EventuallyTimeout).Should(ContainSubstring("measure group1.name1 is 
created"))
                rootCmd.SetArgs([]string{"topn", "create", "-a", addr, "-f", 
"-"})
@@ -120,22 +126,28 @@ group_by_tag_names:
   - id
 counters_number: 10000
 lru_size: 10`))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               if err != nil {
-                                       GinkgoWriter.Printf("execution 
fails:%v", err)
-                               }
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       if err != nil {
+                               GinkgoWriter.Printf("execution fails:%v", err)
+                       }
+                       return buf.String()
                }
                Eventually(createTopn, 
flags.EventuallyTimeout).Should(ContainSubstring("topn group1.name2 is 
created"))
        })
 
        It("get topn schema", func() {
                rootCmd.SetArgs([]string{"topn", "get", "-g", "group1", "-n", 
"name2"})
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                resp := 
new(databasev1.TopNAggregationRegistryServiceGetResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
                Expect(resp.TopNAggregation.Metadata.Group).To(Equal("group1"))
@@ -158,16 +170,22 @@ group_by_tag_names:
   - entity_id
 counters_number: 10000
 lru_size: 10`))
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("topn group1.name2 is updated"))
                rootCmd.SetArgs([]string{"topn", "get", "-g", "group1", "-n", 
"name2"})
-               out = capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               buf.Reset()
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err = rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out = buf.String()
                resp := 
new(databasev1.TopNAggregationRegistryServiceGetResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
                Expect(resp.TopNAggregation.Metadata.Group).To(Equal("group1"))
@@ -178,14 +196,17 @@ lru_size: 10`))
        It("delete topn schema", func() {
                // delete
                rootCmd.SetArgs([]string{"topn", "delete", "-g", "group1", 
"-n", "name2"})
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("topn group1.name2 is deleted"))
                // get again
                rootCmd.SetArgs([]string{"topn", "get", "-g", "group1", "-n", 
"name2"})
-               err := rootCmd.Execute()
+               err = rootCmd.Execute()
                Expect(err).To(MatchError("rpc error: code = NotFound desc = 
banyandb: resource not found"))
        })
 
@@ -205,17 +226,23 @@ group_by_tag_names:
   - id
 counters_number: 10000
 lru_size: 10`))
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("topn group1.name3 is created"))
                // list
                rootCmd.SetArgs([]string{"topn", "list", "-g", "group1"})
-               out = capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               buf.Reset()
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err = rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out = buf.String()
                resp := 
new(databasev1.TopNAggregationRegistryServiceListResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
                Expect(resp.TopNAggregation).To(HaveLen(2))
@@ -268,10 +295,13 @@ tagProjection:
         - id
         - entity_id
         - service_id`, startStr, endStr)))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               Expect(err).NotTo(HaveOccurred())
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       Expect(err).NotTo(HaveOccurred())
+                       return buf.String()
                }
                Eventually(issue, 
flags.EventuallyTimeout).ShouldNot(ContainSubstring("code:"))
                Eventually(func() int {
@@ -294,10 +324,13 @@ timeRange:
 topN: 3
 agg: 2
 fieldValueSort: 1`, startStr, endStr)))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               Expect(err).NotTo(HaveOccurred())
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       Expect(err).NotTo(HaveOccurred())
+                       return buf.String()
                }
                Eventually(issue1, 
flags.EventuallyTimeout).ShouldNot(ContainSubstring("code:"))
                Eventually(func() int {
@@ -331,10 +364,13 @@ groups: ["sw_metric"]
 topN: 3
 agg: 2
 fieldValueSort: 1`))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               Expect(err).NotTo(HaveOccurred())
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       Expect(err).NotTo(HaveOccurred())
+                       return buf.String()
                }
                Eventually(issue1, 
flags.EventuallyTimeout).ShouldNot(ContainSubstring("code:"))
                Eventually(func() int {
diff --git a/bydbctl/internal/cmd/trace.go b/bydbctl/internal/cmd/trace.go
index 53858f816..a6518c706 100644
--- a/bydbctl/internal/cmd/trace.go
+++ b/bydbctl/internal/cmd/trace.go
@@ -19,6 +19,7 @@ package cmd
 
 import (
        "fmt"
+       "io"
 
        "github.com/go-resty/resty/v2"
        "github.com/spf13/cobra"
@@ -48,25 +49,22 @@ func newTraceCmd() *cobra.Command {
                Version: version.Build(),
                Short:   "Create traces from files",
                RunE: func(cmd *cobra.Command, _ []string) error {
-                       return rest(func() ([]reqBody, error) { return 
parseNameAndGroupFromYAML(cmd.InOrStdin()) },
+                       return rest(cmd.OutOrStdout(), func() ([]reqBody, 
error) { return parseNameAndGroupFromYAML(cmd.InOrStdin()) },
                                func(request request) (*resty.Response, error) {
                                        s := new(databasev1.Trace)
                                        err := 
protojson.Unmarshal(request.data, s)
                                        if err != nil {
                                                return nil, err
                                        }
-                                       cr := 
&databasev1.TraceRegistryServiceCreateRequest{
-                                               Trace: s,
-                                       }
+                                       cr := 
&databasev1.TraceRegistryServiceCreateRequest{Trace: s}
                                        b, err := protojson.Marshal(cr)
                                        if err != nil {
                                                return nil, err
                                        }
                                        return 
request.req.SetBody(b).Post(getPath(traceSchemaPath))
                                },
-                               func(_ int, reqBody reqBody, _ []byte) error {
-                                       fmt.Printf("trace %s.%s is created", 
reqBody.group, reqBody.name)
-                                       fmt.Println()
+                               func(w io.Writer, _ int, reqBody reqBody, _ 
[]byte) error {
+                                       fmt.Fprintf(w, "trace %s.%s is 
created\n", reqBody.group, reqBody.name)
                                        return nil
                                }, enableTLS, insecure, cert)
                },
@@ -77,16 +75,14 @@ func newTraceCmd() *cobra.Command {
                Version: version.Build(),
                Short:   "Update traces from files",
                RunE: func(cmd *cobra.Command, _ []string) (err error) {
-                       return rest(func() ([]reqBody, error) { return 
parseNameAndGroupFromYAML(cmd.InOrStdin()) },
+                       return rest(cmd.OutOrStdout(), func() ([]reqBody, 
error) { return parseNameAndGroupFromYAML(cmd.InOrStdin()) },
                                func(request request) (*resty.Response, error) {
                                        s := new(databasev1.Trace)
                                        err := 
protojson.Unmarshal(request.data, s)
                                        if err != nil {
                                                return nil, err
                                        }
-                                       cr := 
&databasev1.TraceRegistryServiceUpdateRequest{
-                                               Trace: s,
-                                       }
+                                       cr := 
&databasev1.TraceRegistryServiceUpdateRequest{Trace: s}
                                        b, err := protojson.Marshal(cr)
                                        if err != nil {
                                                return nil, err
@@ -95,9 +91,8 @@ func newTraceCmd() *cobra.Command {
                                                SetPathParam("name", 
request.name).SetPathParam("group", request.group).
                                                
Put(getPath(traceSchemaPathWithParams))
                                },
-                               func(_ int, reqBody reqBody, _ []byte) error {
-                                       fmt.Printf("trace %s.%s is updated", 
reqBody.group, reqBody.name)
-                                       fmt.Println()
+                               func(w io.Writer, _ int, reqBody reqBody, _ 
[]byte) error {
+                                       fmt.Fprintf(w, "trace %s.%s is 
updated\n", reqBody.group, reqBody.name)
                                        return nil
                                }, enableTLS, insecure, cert)
                },
@@ -107,8 +102,8 @@ func newTraceCmd() *cobra.Command {
                Use:     "get [-g group] -n name",
                Version: version.Build(),
                Short:   "Get a trace",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.SetPathParam("name", 
request.name).SetPathParam("group", 
request.group).Get(getPath(traceSchemaPathWithParams))
                        }, yamlPrinter, enableTLS, insecure, cert)
                },
@@ -118,12 +113,11 @@ func newTraceCmd() *cobra.Command {
                Use:     "delete [-g group] -n name",
                Version: version.Build(),
                Short:   "Delete a trace",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.SetPathParam("name", 
request.name).SetPathParam("group", 
request.group).Delete(getPath(traceSchemaPathWithParams))
-                       }, func(_ int, reqBody reqBody, _ []byte) error {
-                               fmt.Printf("trace %s.%s is deleted", 
reqBody.group, reqBody.name)
-                               fmt.Println()
+                       }, func(w io.Writer, _ int, reqBody reqBody, _ []byte) 
error {
+                               fmt.Fprintf(w, "trace %s.%s is deleted\n", 
reqBody.group, reqBody.name)
                                return nil
                        }, enableTLS, insecure, cert)
                },
@@ -134,8 +128,8 @@ func newTraceCmd() *cobra.Command {
                Use:     "list [-g group]",
                Version: version.Build(),
                Short:   "List traces",
-               RunE: func(_ *cobra.Command, _ []string) (err error) {
-                       return rest(parseFromFlags, func(request request) 
(*resty.Response, error) {
+               RunE: func(cmd *cobra.Command, _ []string) (err error) {
+                       return rest(cmd.OutOrStdout(), parseFromFlags, 
func(request request) (*resty.Response, error) {
                                return request.req.SetPathParam("group", 
request.group).Get(getPath(traceListPath))
                        }, yamlPrinter, enableTLS, insecure, cert)
                },
@@ -147,7 +141,7 @@ func newTraceCmd() *cobra.Command {
                Short:   "Query data in a trace",
                Long:    timeRangeUsage,
                RunE: func(cmd *cobra.Command, _ []string) (err error) {
-                       return rest(func() ([]reqBody, error) { return 
parseTimeRangeFromFlagAndYAML(cmd.InOrStdin()) },
+                       return rest(cmd.OutOrStdout(), func() ([]reqBody, 
error) { return parseTimeRangeFromFlagAndYAML(cmd.InOrStdin()) },
                                func(request request) (*resty.Response, error) {
                                        return 
request.req.SetBody(request.data).Post(getPath(traceQueryPath))
                                }, yamlPrinter, enableTLS, insecure, cert)
diff --git a/bydbctl/internal/cmd/trace_test.go 
b/bydbctl/internal/cmd/trace_test.go
index cd03f83ae..ae8cf1a0c 100644
--- a/bydbctl/internal/cmd/trace_test.go
+++ b/bydbctl/internal/cmd/trace_test.go
@@ -18,6 +18,7 @@
 package cmd_test
 
 import (
+       "bytes"
        "fmt"
        "strings"
        "time"
@@ -25,7 +26,6 @@ import (
        . "github.com/onsi/ginkgo/v2"
        . "github.com/onsi/gomega"
        "github.com/spf13/cobra"
-       "github.com/zenizh/go-capturer"
        grpclib "google.golang.org/grpc"
        "google.golang.org/grpc/credentials/insecure"
 
@@ -63,12 +63,15 @@ resource_opts:
   ttl:
     unit: UNIT_DAY
     num: 7`))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               if err != nil {
-                                       GinkgoWriter.Printf("execution 
fails:%v", err)
-                               }
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       if err != nil {
+                               GinkgoWriter.Printf("execution fails:%v", err)
+                       }
+                       return buf.String()
                }
                Eventually(createGroup, 
flags.EventuallyTimeout).Should(ContainSubstring("group group1 is created"))
                rootCmd.SetArgs([]string{"trace", "create", "-a", addr, "-f", 
"-"})
@@ -87,22 +90,28 @@ tags:
 trace_id_tag_name: trace_id
 span_id_tag_name: span_id
 timestamp_tag_name: timestamp`))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               if err != nil {
-                                       GinkgoWriter.Printf("execution 
fails:%v", err)
-                               }
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       if err != nil {
+                               GinkgoWriter.Printf("execution fails:%v", err)
+                       }
+                       return buf.String()
                }
                Eventually(createTrace, 
flags.EventuallyTimeout).Should(ContainSubstring("trace group1.name1 is 
created"))
        })
 
        It("get trace schema", func() {
                rootCmd.SetArgs([]string{"trace", "get", "-g", "group1", "-n", 
"name1"})
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                GinkgoWriter.Println(out)
                resp := new(databasev1.TraceRegistryServiceGetResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
@@ -132,16 +141,22 @@ tags:
 trace_id_tag_name: trace_id
 span_id_tag_name: span_id
 timestamp_tag_name: timestamp`))
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("trace group1.name1 is 
updated"))
                rootCmd.SetArgs([]string{"trace", "get", "-g", "group1", "-n", 
"name1"})
-               out = capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               buf.Reset()
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err = rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out = buf.String()
                resp := new(databasev1.TraceRegistryServiceGetResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
                Expect(resp.Trace.Metadata.Group).To(Equal("group1"))
@@ -153,14 +168,17 @@ timestamp_tag_name: timestamp`))
        It("delete trace schema", func() {
                // delete
                rootCmd.SetArgs([]string{"trace", "delete", "-g", "group1", 
"-n", "name1"})
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("trace group1.name1 is 
deleted"))
                // get again
                rootCmd.SetArgs([]string{"trace", "get", "-g", "group1", "-n", 
"name1"})
-               err := rootCmd.Execute()
+               err = rootCmd.Execute()
                Expect(err).To(MatchError("rpc error: code = NotFound desc = 
banyandb: resource not found"))
        })
 
@@ -181,17 +199,23 @@ tags:
 trace_id_tag_name: trace_id
 span_id_tag_name: span_id
 timestamp_tag_name: timestamp`))
-               out := capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               var buf bytes.Buffer
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err := rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out := buf.String()
                Expect(out).To(ContainSubstring("trace group1.name2 is 
created"))
                // list
                rootCmd.SetArgs([]string{"trace", "list", "-g", "group1"})
-               out = capturer.CaptureStdout(func() {
-                       err := rootCmd.Execute()
-                       Expect(err).NotTo(HaveOccurred())
-               })
+               buf.Reset()
+               rootCmd.SetOut(&buf)
+               rootCmd.SetErr(&buf)
+
+               err = rootCmd.Execute()
+               Expect(err).NotTo(HaveOccurred())
+               out = buf.String()
                resp := new(databasev1.TraceRegistryServiceListResponse)
                helpers.UnmarshalYAML([]byte(out), resp)
                Expect(resp.Trace).To(HaveLen(2))
@@ -243,10 +267,13 @@ orderBy:
   indexRuleName: "timestamp"
   sort: SORT_DESC`, nowStr, endStr)
                        rootCmd.SetIn(strings.NewReader(sprintf))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               Expect(err).NotTo(HaveOccurred())
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       Expect(err).NotTo(HaveOccurred())
+                       return buf.String()
                }
                Eventually(issue, 
flags.EventuallyTimeout).ShouldNot(ContainSubstring("code:"))
                Eventually(func() int {
@@ -279,10 +306,13 @@ limit: 100
 orderBy:
   indexRuleName: "timestamp"
   sort: SORT_DESC`))
-                       return capturer.CaptureStdout(func() {
-                               err := rootCmd.Execute()
-                               Expect(err).NotTo(HaveOccurred())
-                       })
+                       var buf bytes.Buffer
+                       rootCmd.SetOut(&buf)
+                       rootCmd.SetErr(&buf)
+
+                       err := rootCmd.Execute()
+                       Expect(err).NotTo(HaveOccurred())
+                       return buf.String()
                }
                Eventually(issue, 
flags.EventuallyTimeout).ShouldNot(ContainSubstring("code:"))
                Eventually(func() int {
diff --git a/bydbctl/internal/cmd/use.go b/bydbctl/internal/cmd/use.go
index d4870c1d8..da6634dbf 100644
--- a/bydbctl/internal/cmd/use.go
+++ b/bydbctl/internal/cmd/use.go
@@ -32,13 +32,13 @@ func newUseCmd() *cobra.Command {
                Version: version.Build(),
                Short:   "Select a group",
                Args:    cobra.ExactArgs(1),
-               RunE: func(_ *cobra.Command, args []string) (err error) {
+               RunE: func(cmd *cobra.Command, args []string) (err error) {
                        viper.Set("group", args[0])
                        err = viper.WriteConfig()
                        if err != nil {
                                return err
                        }
-                       fmt.Printf("Switched to [%s]", viper.GetString("group"))
+                       fmt.Fprintf(cmd.OutOrStdout(), "Switched to [%s]", 
viper.GetString("group"))
                        return nil
                },
        }
diff --git a/bydbctl/internal/cmd/use.go b/bydbctl/internal/cmd/use_test.go
similarity index 51%
copy from bydbctl/internal/cmd/use.go
copy to bydbctl/internal/cmd/use_test.go
index d4870c1d8..ff68728db 100644
--- a/bydbctl/internal/cmd/use.go
+++ b/bydbctl/internal/cmd/use_test.go
@@ -18,28 +18,40 @@
 package cmd
 
 import (
-       "fmt"
+       "bytes"
+       "os"
+       "path/filepath"
+       "testing"
 
        "github.com/spf13/cobra"
        "github.com/spf13/viper"
-
-       "github.com/apache/skywalking-banyandb/pkg/version"
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
 )
 
-func newUseCmd() *cobra.Command {
-       return &cobra.Command{
-               Use:     "use group",
-               Version: version.Build(),
-               Short:   "Select a group",
-               Args:    cobra.ExactArgs(1),
-               RunE: func(_ *cobra.Command, args []string) (err error) {
-                       viper.Set("group", args[0])
-                       err = viper.WriteConfig()
-                       if err != nil {
-                               return err
-                       }
-                       fmt.Printf("Switched to [%s]", viper.GetString("group"))
-                       return nil
-               },
-       }
+func TestUseCommandWritesToConfiguredWriter(t *testing.T) {
+       t.Cleanup(func() {
+               cfgFile = ""
+               viper.Reset()
+       })
+
+       configPath := filepath.Join(t.TempDir(), ".bydbctl.yaml")
+       err := os.WriteFile(configPath, []byte("addr: 
http://localhost:17913\n";), 0o600)
+       require.NoError(t, err)
+
+       command := &cobra.Command{Use: "root"}
+       RootCmdFlags(command)
+       command.SetArgs([]string{"--config", configPath, "use", "sw"})
+       var outBuf bytes.Buffer
+       var errBuf bytes.Buffer
+       command.SetOut(&outBuf)
+       command.SetErr(&errBuf)
+
+       err = command.Execute()
+       require.NoError(t, err)
+       assert.Contains(t, outBuf.String(), "Switched to [sw]")
+
+       cfgBytes, err := os.ReadFile(configPath)
+       require.NoError(t, err)
+       assert.Contains(t, string(cfgBytes), "group: sw")
 }
diff --git a/dist/LICENSE b/dist/LICENSE
index 8c0cc227f..4413d5cf4 100644
--- a/dist/LICENSE
+++ b/dist/LICENSE
@@ -405,7 +405,6 @@ MIT licenses
     github.com/urfave/cli/v2 v2.27.7 MIT
     github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 MIT
     github.com/yusufpapurcu/wmi v1.2.4 MIT
-    github.com/zenizh/go-capturer v0.0.0-20211219060012-52ea6c8fed04 MIT
     go.uber.org/automaxprocs v1.6.0 MIT
     go.uber.org/multierr v1.11.0 MIT
     go.uber.org/zap v1.27.1 MIT
diff --git a/dist/licenses/license-github.com-zenizh-go-capturer.txt 
b/dist/licenses/license-github.com-zenizh-go-capturer.txt
deleted file mode 100644
index 549d46b2f..000000000
--- a/dist/licenses/license-github.com-zenizh-go-capturer.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2017 kami
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/go.mod b/go.mod
index bdc6e1f32..126c1c7ce 100644
--- a/go.mod
+++ b/go.mod
@@ -41,7 +41,6 @@ require (
        github.com/stretchr/testify v1.11.1
        github.com/urfave/cli/v2 v2.27.7
        github.com/xhit/go-str2duration/v2 v2.1.0
-       github.com/zenizh/go-capturer v0.0.0-20211219060012-52ea6c8fed04
        go.uber.org/automaxprocs v1.6.0
        go.uber.org/mock v0.6.0
        go.uber.org/multierr v1.11.0
diff --git a/go.sum b/go.sum
index a6342451d..4007af83a 100644
--- a/go.sum
+++ b/go.sum
@@ -424,8 +424,6 @@ github.com/xrash/smetrics 
v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAz
 github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod 
h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
 github.com/yusufpapurcu/wmi v1.2.4 
h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
 github.com/yusufpapurcu/wmi v1.2.4/go.mod 
h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
-github.com/zenizh/go-capturer v0.0.0-20211219060012-52ea6c8fed04 
h1:qXafrlZL1WsJW5OokjraLLRURHiw0OzKHD/RNdspp4w=
-github.com/zenizh/go-capturer v0.0.0-20211219060012-52ea6c8fed04/go.mod 
h1:FiwNQxz6hGoNFBC4nIx+CxZhI3nne5RmIOlT/MXcSD4=
 github.com/zinclabs/bluge_segment_api v1.0.0 
h1:GJvPxdzR7KjwdxmcKleQLvtIYi/J7Q7ehRlZqgGayzg=
 github.com/zinclabs/bluge_segment_api v1.0.0/go.mod 
h1:mYfPVUdXLZ4iXsicXMER+RcI/avwphjMOi8nhN9HDLA=
 go.opentelemetry.io/auto/sdk v1.2.1 
h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=

Reply via email to