github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/internal/debug/flags.go (about) 1 // Copyright 2016 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package debug 18 19 import ( 20 "fmt" 21 "io" 22 "net/http" 23 _ "net/http/pprof" 24 "os" 25 "runtime" 26 27 "github.com/ethereum/go-ethereum/log" 28 "github.com/ethereum/go-ethereum/metrics" 29 "github.com/ethereum/go-ethereum/metrics/exp" 30 "github.com/fjl/memsize/memsizeui" 31 colorable "github.com/mattn/go-colorable" 32 "github.com/mattn/go-isatty" 33 "gopkg.in/urfave/cli.v1" 34 ) 35 36 var Memsize memsizeui.Handler 37 38 var ( 39 verbosityFlag = cli.IntFlag{ 40 Name: "verbosity", 41 Usage: "Logging verbosity: 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail", 42 Value: 3, 43 } 44 vmoduleFlag = cli.StringFlag{ 45 Name: "vmodule", 46 Usage: "Per-module verbosity: comma-separated list of <pattern>=<level> (e.g. eth/*=5,p2p=4)", 47 Value: "", 48 } 49 backtraceAtFlag = cli.StringFlag{ 50 Name: "backtrace", 51 Usage: "Request a stack trace at a specific logging statement (e.g. \"block.go:271\")", 52 Value: "", 53 } 54 debugFlag = cli.BoolFlag{ 55 Name: "debug", 56 Usage: "Prepends log messages with call-site location (file and line number)", 57 } 58 pprofFlag = cli.BoolFlag{ 59 Name: "pprof", 60 Usage: "Enable the pprof HTTP server", 61 } 62 pprofPortFlag = cli.IntFlag{ 63 Name: "pprofport", 64 Usage: "pprof HTTP server listening port", 65 Value: 6060, 66 } 67 pprofAddrFlag = cli.StringFlag{ 68 Name: "pprofaddr", 69 Usage: "pprof HTTP server listening interface", 70 Value: "127.0.0.1", 71 } 72 memprofilerateFlag = cli.IntFlag{ 73 Name: "memprofilerate", 74 Usage: "Turn on memory profiling with the given rate", 75 Value: runtime.MemProfileRate, 76 } 77 blockprofilerateFlag = cli.IntFlag{ 78 Name: "blockprofilerate", 79 Usage: "Turn on block profiling with the given rate", 80 } 81 cpuprofileFlag = cli.StringFlag{ 82 Name: "cpuprofile", 83 Usage: "Write CPU profile to the given file", 84 } 85 traceFlag = cli.StringFlag{ 86 Name: "trace", 87 Usage: "Write execution trace to the given file", 88 } 89 consoleFormatFlag = cli.StringFlag{ 90 Name: "consoleformat", 91 Usage: "Write console logs as 'json' or 'term'", 92 } 93 consoleOutputFlag = cli.StringFlag{ 94 Name: "consoleoutput", 95 Usage: "(stderr|stdout|split) By default, console output goes to stderr. " + 96 "In stdout mode, write console logs to stdout (not stderr). " + 97 "In split mode, write critical(warning, error, and critical) console logs to stderr " + 98 "and non-critical (info, debug, and trace) to stdout", 99 } 100 ) 101 102 // Flags holds all command-line flags required for debugging. 103 var Flags = []cli.Flag{ 104 verbosityFlag, vmoduleFlag, backtraceAtFlag, debugFlag, 105 pprofFlag, pprofAddrFlag, pprofPortFlag, 106 memprofilerateFlag, blockprofilerateFlag, cpuprofileFlag, traceFlag, 107 consoleFormatFlag, consoleOutputFlag, 108 } 109 110 var ( 111 ostream log.Handler 112 glogger *log.GlogHandler 113 ) 114 115 type StdoutStderrHandler struct { 116 stdoutHandler log.Handler 117 stderrHandler log.Handler 118 } 119 120 func (this StdoutStderrHandler) Log(r *log.Record) error { 121 switch r.Lvl { 122 case log.LvlCrit: 123 fallthrough 124 case log.LvlError: 125 fallthrough 126 case log.LvlWarn: 127 return this.stderrHandler.Log(r) 128 129 case log.LvlInfo: 130 fallthrough 131 case log.LvlDebug: 132 fallthrough 133 case log.LvlTrace: 134 return this.stdoutHandler.Log(r) 135 136 default: 137 return this.stdoutHandler.Log(r) 138 } 139 } 140 141 func init() { 142 ostream = log.StreamHandler(io.Writer(os.Stderr), log.TerminalFormat(false)) 143 glogger = log.NewGlogHandler(ostream) 144 } 145 146 // Setup initializes profiling and logging based on the CLI flags. 147 // It should be called as early as possible in the program. 148 func Setup(ctx *cli.Context, logdir string) error { 149 // logging 150 151 consoleFormat := ctx.GlobalString(consoleFormatFlag.Name) 152 consoleOutputMode := ctx.GlobalString(consoleOutputFlag.Name) 153 154 ostream := CreateStreamHandler(consoleFormat, consoleOutputMode) 155 glogger = log.NewGlogHandler(ostream) 156 157 log.PrintOrigins(ctx.GlobalBool(debugFlag.Name)) 158 if logdir != "" { 159 rfh, err := log.RotatingFileHandler( 160 logdir, 161 262144, 162 log.JSONFormatOrderedEx(false, true), 163 ) 164 if err != nil { 165 return err 166 } 167 glogger.SetHandler(log.MultiHandler(ostream, rfh)) 168 } 169 glogger.Verbosity(log.Lvl(ctx.GlobalInt(verbosityFlag.Name))) 170 glogger.Vmodule(ctx.GlobalString(vmoduleFlag.Name)) 171 glogger.BacktraceAt(ctx.GlobalString(backtraceAtFlag.Name)) 172 log.Root().SetHandler(glogger) 173 174 // profiling, tracing 175 runtime.MemProfileRate = ctx.GlobalInt(memprofilerateFlag.Name) 176 Handler.SetBlockProfileRate(ctx.GlobalInt(blockprofilerateFlag.Name)) 177 if traceFile := ctx.GlobalString(traceFlag.Name); traceFile != "" { 178 if err := Handler.StartGoTrace(traceFile); err != nil { 179 return err 180 } 181 } 182 if cpuFile := ctx.GlobalString(cpuprofileFlag.Name); cpuFile != "" { 183 if err := Handler.StartCPUProfile(cpuFile); err != nil { 184 return err 185 } 186 } 187 188 // pprof server 189 if ctx.GlobalBool(pprofFlag.Name) { 190 address := fmt.Sprintf("%s:%d", ctx.GlobalString(pprofAddrFlag.Name), ctx.GlobalInt(pprofPortFlag.Name)) 191 StartPProf(address) 192 } 193 return nil 194 } 195 196 func CreateStreamHandler(consoleFormat string, consoleOutputMode string) log.Handler { 197 if consoleOutputMode == "stdout" { 198 usecolor := useColor(os.Stdout) 199 var output io.Writer 200 if usecolor { 201 output = colorable.NewColorableStdout() 202 } else { 203 output = io.Writer(os.Stdout) 204 } 205 return log.StreamHandler(output, getConsoleLogFormat(consoleFormat, usecolor)) 206 } 207 208 // This is the default mode to maintain backward-compatibility with the geth command-line 209 if consoleOutputMode == "stderr" || len(consoleOutputMode) == 0 { 210 usecolor := useColor(os.Stderr) 211 var output io.Writer 212 if usecolor { 213 output = colorable.NewColorableStderr() 214 } else { 215 output = io.Writer(os.Stderr) 216 } 217 return log.StreamHandler(output, getConsoleLogFormat(consoleFormat, usecolor)) 218 } 219 220 if consoleOutputMode == "split" { 221 usecolorStdout := useColor(os.Stdout) 222 usecolorStderr := useColor(os.Stderr) 223 224 var outputStdout io.Writer 225 var outputStderr io.Writer 226 227 if usecolorStdout { 228 outputStdout = colorable.NewColorableStdout() 229 } else { 230 outputStdout = io.Writer(os.Stdout) 231 } 232 233 if usecolorStderr { 234 outputStderr = colorable.NewColorableStderr() 235 } else { 236 outputStderr = io.Writer(os.Stderr) 237 } 238 239 return StdoutStderrHandler{ 240 stdoutHandler: log.StreamHandler(outputStdout, getConsoleLogFormat(consoleFormat, usecolorStdout)), 241 stderrHandler: log.StreamHandler(outputStderr, getConsoleLogFormat(consoleFormat, usecolorStderr))} 242 } 243 244 panic(fmt.Sprintf("Unexpected value for \"%s\" flag: \"%s\"", consoleOutputFlag.Name, consoleOutputMode)) 245 } 246 247 func useColor(file *os.File) bool { 248 return (isatty.IsTerminal(file.Fd()) || isatty.IsCygwinTerminal(file.Fd())) && os.Getenv("TERM") != "dumb" 249 } 250 251 func getConsoleLogFormat(consoleFormat string, usecolor bool) log.Format { 252 if consoleFormat == "json" { 253 return log.JSONFormat() 254 } 255 if consoleFormat == "term" || len(consoleFormat) == 0 /* No explicit format specified */ { 256 return log.TerminalFormat(usecolor) 257 } 258 panic(fmt.Sprintf("Unexpected value for \"%s\" flag: \"%s\"", consoleFormatFlag.Name, consoleFormat)) 259 } 260 261 func StartPProf(address string) { 262 // Hook go-metrics into expvar on any /debug/metrics request, load all vars 263 // from the registry into expvar, and execute regular expvar handler. 264 exp.Exp(metrics.DefaultRegistry) 265 http.Handle("/memsize/", http.StripPrefix("/memsize", &Memsize)) 266 log.Info("Starting pprof server", "addr", fmt.Sprintf("http://%s/debug/pprof", address)) 267 go func() { 268 if err := http.ListenAndServe(address, nil); err != nil { 269 log.Error("Failure in running pprof server", "err", err) 270 } 271 }() 272 } 273 274 // Exit stops all running profiles, flushing their output to the 275 // respective file. 276 func Exit() { 277 Handler.StopCPUProfile() 278 Handler.StopGoTrace() 279 }