gogo-cobraklog

How to combine cobra and klog


I have a project that needs to use cobra and klog to generate executable and print logs and keep.

Firstly, I tested that using the following klog can output the log to the terminal and file at the same time.

package main
import (
    "flag"
    "k8s.io/klog"
)
func init() {
    var fs flag.FlagSet
    klog.InitFlags(&fs)
    fs.Set("logtostderr", "false")
    fs.Set("log_file_max_size", "100")
    fs.Set("log_file", "/home/test/workspace/klogfile/test.log")
    fs.Set("alsologtostderr", "true")
}
func main() {
    defer klog.Flush()
    klog.Info("info")
    klog.Warning("warning")
    klog.Error("error")
}

The content of file /home/test/workspace/klogfile/test.log is as follow

Log file created at: 2023/04/06 16:46:07
Running on machine: s52
Binary: Built with gc go1.16.12 for linux/amd64
Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg
I0406 16:46:07.751183   13512 main.go:20] info
W0406 16:46:07.751594   13512 main.go:21] warning
E0406 16:46:07.751629   13512 main.go:22] error

Then I try to add it to cobra based on k8s style

package main
import (
    "flag"
    "github.com/spf13/cobra"
    "k8s.io/klog"
)
var (
    str = "hello world"
)
func NewCommand() *cobra.Command {
    cmd := &cobra.Command{
        Use:   "echo",
        Short: "use klog with cobra",
        Long:  "Use klog together with cobra.",
        Run: func(cmd *cobra.Command, args []string) {
            Run()
        },
    }
    klog.InitFlags(flag.CommandLine)
    cmd.Flags().AddGoFlagSet(flag.CommandLine)
    cmd.Flags().SortFlags = false
    cmd.Flags().StringVar(&str, "str", str, "string to print")
    return cmd
}
func Run() {
    defer klog.Flush()
    klog.Infof("Running, str:%s", str)
}

func main() {
    if err := NewCommand().Execute(); err != nil {
        klog.Fatalf("root cmd execute failed, err=%v", err)
    }
}

I can print out the required log options through --help, but when I override them to achieve the save to file in the above example, only the output is output to the terminal and not to the file

# go run main2.go --logtostderr false --log_file_max_size "100" --alsologtostderr true --log_file "/home/test/workspace/klogfile/test2.log"
I0406 16:52:57.479455   15217 cobra_klog.go:34] Running, str:hello world

May I ask where I am missing the necessary code, if so, please let me know how to modify it, thank you!


Solution

  • There is nothing wrong in your code. The only issue is that you must use the -flag=false form to turn off a boolean flag (see Command line flag syntax).

    Try this command (repalce --logtostderr false with --logtostderr=false):

    go run main2.go --logtostderr=false --log_file_max_size "100" --alsologtostderr true --log_file "/home/test/workspace/klogfile/test2.log"
    

    BTW, I think --log_file_max_size "100" could be simplified to --log_file_max_size 100.


    Update:

    To reduce the exposed flags of klog:

     package main
     
     import (
        "flag"
        "github.com/spf13/cobra"
        "k8s.io/klog"
     )
     
     var (
        str     = "hello world"
    +   logFile string
     )
     
     func NewCommand() *cobra.Command {
    +   var fs flag.FlagSet
    +   klog.InitFlags(&fs)
    
        cmd := &cobra.Command{
            Use:   "echo",
            Short: "use klog with cobra",
            Long:  "Use klog together with cobra.",
            Run: func(cmd *cobra.Command, args []string) {
    +           fs.Set("logtostderr", "false")
    +           fs.Set("log_file", logFile)
    
                Run()
            },
        }
    -   klog.InitFlags(flag.CommandLine)
    -   cmd.Flags().AddGoFlagSet(flag.CommandLine)
        cmd.Flags().SortFlags = false
        cmd.Flags().StringVar(&str, "str", str, "string to print")
    +   cmd.Flags().StringVar(&logFile, "log_file", "", "If non-empty, use this log file")
        return cmd
     }
     func Run() {
        defer klog.Flush()
        klog.Infof("Running, str:%s", str)
     }
     
     func main() {
        if err := NewCommand().Execute(); err != nil {
            klog.Fatalf("root cmd execute failed, err=%v", err)
        }
     }