Using glog together with cobra in golang

2017-12-01

Cobra · Glog · Golang

2 minutes

If you have been programming with golang, you’ve probably heard of cobra. I use it extensively at work and also in my personal projects.

Recently though, I’ve been using glog more and more. And I quite like it. The thing is, it has a couple of flag definitions in its init() function using golang’s builtin flag library. And I wanted to include those flags into cobra’s flag definitions. This is how I did it.

package main
import (
goflag "flag"
"github.com/golang/glog"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
)
var (
rootCmd = &cobra.Command{
Long: "Use glog with cobra.",
Run: echo,
}
str string
)
func init() {
rootCmd.PersistentFlags().StringVar(&str, "echo", "hello", "echo string")
flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
}
func echo(cmd *cobra.Command, args []string) {
goflag.Parse()
glog.Info("echo (info): ", str)
glog.Warning("echo (warn): ", str)
glog.Error("echo (error): ", str)
}
func main() {
err := rootCmd.Execute()
if err != nil {
glog.Error(err)
}
}

Generated help information will now look something like this.

# run the help command
$ ./cobraglog -h
Use glog with cobra.

Usage:
   [flags]

Flags:
      --alsologtostderr                  log to standard error as well as files
      --echo string                      echo string (default "hello")
      --log_backtrace_at traceLocation   when logging hits line file:N, emit a stack trace (default :0)
      --log_dir string                   If non-empty, write log files in this directory
      --logtostderr                      log to standard error instead of files
      --stderrthreshold severity         logs at or above this threshold go to stderr (default 2)
  -v, --v Level                          log level for V logs
      --vmodule moduleSpec               comma-separated list of pattern=N settings for file-filtered logging
  -h, --help                             help for this command

Note that our cobra-defined flag --echo is also there. The rest are defined by glog internally. Finally, run the application.

# run the binary, providing the logtostderr flag defined by glog
$ ./cobraglog --logtostderr
I1129 13:49:34.166660    2138 main.go:28] echo (info): hello
W1129 13:49:34.166718    2138 main.go:29] echo (warn): hello
E1129 13:49:34.166722    2138 main.go:30] echo (error): hello

Here’s another example using subcommands.

package main
import (
goflag "flag"
"github.com/golang/glog"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
)
var (
rootCmd = &cobra.Command{
Long: "Use glog with cobra.",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
// For cobra + glog flags. Available to all subcommands.
goflag.Parse()
},
}
s1 string
s2 string
)
func init() {
rootCmd.AddCommand(
subCmd1(),
subCmd2(),
)
flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
}
func subCmd1() *cobra.Command {
c := &cobra.Command{
Use: "sub1",
Short: "test subcmd1",
Long: "Test sub command 1.",
Run: func(cmd *cobra.Command, args []string) {
glog.Infof("hello %v from subCmd1", s1)
},
}
c.Flags().SortFlags = false
c.Flags().StringVar(&s1, "target", "world", "say hello to")
return c
}
func subCmd2() *cobra.Command {
c := &cobra.Command{
Use: "sub2",
Short: "test subcmd2",
Long: "Test sub command 2.",
Run: func(cmd *cobra.Command, args []string) {
glog.Infof("hello %v from subCmd2", s2)
},
}
c.Flags().SortFlags = false
c.Flags().StringVar(&s2, "target", "parallel world", "say hello to")
return c
}
func main() {
err := rootCmd.Execute()
if err != nil {
glog.Error(err)
}
}