github.com/klaytn/klaytn@v1.12.1/api/debug/flags.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2016 The go-ethereum Authors 3 // This file is part of the go-ethereum library. 4 // 5 // The go-ethereum library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Lesser General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // The go-ethereum library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Lesser General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public License 16 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // This file is derived from internal/debug/flags.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package debug 22 23 import ( 24 "fmt" 25 "io" 26 _ "net/http/pprof" 27 "os" 28 "path/filepath" 29 "runtime" 30 31 "github.com/fjl/memsize/memsizeui" 32 "github.com/klaytn/klaytn/log" 33 "github.com/mattn/go-colorable" 34 "github.com/mattn/go-isatty" 35 "github.com/urfave/cli/v2" 36 "github.com/urfave/cli/v2/altsrc" 37 "gopkg.in/natefinch/lumberjack.v2" 38 ) 39 40 var Memsize memsizeui.Handler 41 42 var ( 43 verbosityFlag = &cli.IntFlag{ 44 Name: "verbosity", 45 Usage: "Logging verbosity: 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=detail", 46 Value: 3, 47 Aliases: []string{"debug-profile.verbosity"}, 48 EnvVars: []string{"KLAYTN_VERBOSITY"}, 49 Category: "LOGGING AND DEBUGGING", 50 } 51 vmoduleFlag = &cli.StringFlag{ 52 Name: "vmodule", 53 Usage: "Per-module verbosity: comma-separated list of <pattern>=<level> (e.g. klay/*=5,p2p=4)", 54 Value: "", 55 Aliases: []string{"debug-profile.vmodule"}, 56 EnvVars: []string{"KLAYTN_VMODULE"}, 57 Category: "LOGGING AND DEBUGGING", 58 } 59 backtraceAtFlag = &cli.StringFlag{ 60 Name: "backtrace", 61 Usage: "Request a stack trace at a specific logging statement (e.g. \"block.go:271\")", 62 Value: "", 63 Aliases: []string{"debug-profile.backtrace"}, 64 EnvVars: []string{"KLAYTN_BACKTRACE"}, 65 Category: "LOGGING AND DEBUGGING", 66 } 67 debugFlag = &cli.BoolFlag{ 68 Name: "debug", 69 Usage: "Prepends log messages with call-site location (file and line number)", 70 Aliases: []string{"debug-profile.print-site"}, 71 EnvVars: []string{"KLAYTN_DEBUG"}, 72 Category: "LOGGING AND DEBUGGING", 73 } 74 logFormatFlag = &cli.StringFlag{ 75 Name: "log.format", 76 Usage: "Log format to use (json|logfmt|terminal)", 77 Value: "terminal", 78 Aliases: []string{}, 79 EnvVars: []string{"KLAYTN_LOGFORMAT"}, 80 Category: "LOGGING AND DEBUGGING", 81 } 82 logFileFlag = &cli.StringFlag{ 83 Name: "log.file", 84 Usage: "Write logs to a file", 85 Aliases: []string{}, 86 EnvVars: []string{"KLAYTN_LOGFILE"}, 87 Category: "LOGGING AND DEBUGGING", 88 } 89 logRotateFlag = &cli.BoolFlag{ 90 Name: "log.rotate", 91 Usage: "Enables log file rotation", 92 Aliases: []string{}, 93 EnvVars: []string{"KLAYTN_LOGROTATE"}, 94 Category: "LOGGING AND DEBUGGING", 95 } 96 logMaxSizeMBsFlag = &cli.IntFlag{ 97 Name: "log.maxsize", 98 Usage: "Maximum size in MBs of a single log file (use with --log.rotate flag)", 99 Value: 100, 100 Aliases: []string{}, 101 EnvVars: []string{"KLAYTN_LOGMAXSIZE"}, 102 Category: "LOGGING AND DEBUGGING", 103 } 104 logMaxBackupsFlag = &cli.IntFlag{ 105 Name: "log.maxbackups", 106 Usage: "Maximum number of log files to retain (use with --log.rotate flag)", 107 Value: 10, 108 Aliases: []string{}, 109 EnvVars: []string{"KLAYTN_LOGMAXBACKUPS"}, 110 Category: "LOGGING AND DEBUGGING", 111 } 112 logMaxAgeFlag = &cli.IntFlag{ 113 Name: "log.maxage", 114 Usage: "Maximum number of days to retain a log file (use with --log.rotate flag)", 115 Value: 30, 116 Aliases: []string{}, 117 EnvVars: []string{"KLAYTN_LOGMAXAGE"}, 118 Category: "LOGGING AND DEBUGGING", 119 } 120 logCompressFlag = &cli.BoolFlag{ 121 Name: "log.compress", 122 Usage: "Compress the log files (use with --log.rotate flag)", 123 Aliases: []string{}, 124 EnvVars: []string{"KLAYTN_LOGCOMPRESS"}, 125 Category: "LOGGING AND DEBUGGING", 126 } 127 pprofFlag = &cli.BoolFlag{ 128 Name: "pprof", 129 Usage: "Enable the pprof HTTP server", 130 Aliases: []string{"debug-profile.pprof.enable"}, 131 EnvVars: []string{"KLAYTN_PPROF"}, 132 Category: "LOGGING AND DEBUGGING", 133 } 134 pprofPortFlag = &cli.IntFlag{ 135 Name: "pprofport", 136 Usage: "pprof HTTP server listening port", 137 Value: 6060, 138 Aliases: []string{"debug-profile.pprof.port"}, 139 EnvVars: []string{"KLAYTN_PPROFPORT"}, 140 Category: "LOGGING AND DEBUGGING", 141 } 142 pprofAddrFlag = &cli.StringFlag{ 143 Name: "pprofaddr", 144 Usage: "pprof HTTP server listening interface", 145 Value: "127.0.0.1", 146 Aliases: []string{"debug-profile.pprof.addr"}, 147 EnvVars: []string{"KLAYTN_PPROFADDR"}, 148 Category: "LOGGING AND DEBUGGING", 149 } 150 memprofileFlag = &cli.StringFlag{ 151 Name: "memprofile", 152 Usage: "Write memory profile to the given file", 153 Aliases: []string{"debug-profile.mem-profile.file-name"}, 154 EnvVars: []string{"KLAYTN_MEMPROFILE"}, 155 Category: "LOGGING AND DEBUGGING", 156 } 157 memprofilerateFlag = &cli.IntFlag{ 158 Name: "memprofilerate", 159 Usage: "Turn on memory profiling with the given rate", 160 Value: runtime.MemProfileRate, 161 Aliases: []string{"debug-profile.mem-profile.rate"}, 162 EnvVars: []string{"KLAYTN_MEMPROFILERATE"}, 163 Category: "LOGGING AND DEBUGGING", 164 } 165 blockprofilerateFlag = &cli.IntFlag{ 166 Name: "blockprofilerate", 167 Usage: "Turn on block profiling with the given rate", 168 Aliases: []string{"debug-profile.block-profile.rate"}, 169 EnvVars: []string{"KLAYTN_BLOCKPROFILERATE"}, 170 Category: "LOGGING AND DEBUGGING", 171 } 172 cpuprofileFlag = &cli.StringFlag{ 173 Name: "cpuprofile", 174 Usage: "Write CPU profile to the given file", 175 Aliases: []string{"debug-profile.cpu-profile.file-name"}, 176 EnvVars: []string{"KLAYTN_CPUPROFILE"}, 177 Category: "LOGGING AND DEBUGGING", 178 } 179 traceFlag = &cli.StringFlag{ 180 Name: "trace", 181 Usage: "Write execution trace to the given file", 182 Aliases: []string{"debug-profile.trace.file-name"}, 183 EnvVars: []string{"KLAYTN_TRACE"}, 184 Category: "LOGGING AND DEBUGGING", 185 } 186 ) 187 188 // Flags holds all command-line flags required for debugging. 189 var Flags = []cli.Flag{ 190 altsrc.NewIntFlag(verbosityFlag), 191 altsrc.NewStringFlag(vmoduleFlag), 192 altsrc.NewStringFlag(backtraceAtFlag), 193 altsrc.NewBoolFlag(debugFlag), 194 altsrc.NewStringFlag(logFormatFlag), 195 altsrc.NewStringFlag(logFileFlag), 196 altsrc.NewBoolFlag(logRotateFlag), 197 altsrc.NewIntFlag(logMaxSizeMBsFlag), 198 altsrc.NewIntFlag(logMaxBackupsFlag), 199 altsrc.NewIntFlag(logMaxAgeFlag), 200 altsrc.NewBoolFlag(logCompressFlag), 201 altsrc.NewBoolFlag(pprofFlag), 202 altsrc.NewStringFlag(pprofAddrFlag), 203 altsrc.NewIntFlag(pprofPortFlag), 204 altsrc.NewStringFlag(memprofileFlag), 205 altsrc.NewIntFlag(memprofilerateFlag), 206 altsrc.NewIntFlag(blockprofilerateFlag), 207 altsrc.NewStringFlag(cpuprofileFlag), 208 altsrc.NewStringFlag(traceFlag), 209 } 210 211 var glogger *log.GlogHandler 212 213 func init() { 214 glogger = log.NewGlogHandler(log.StreamHandler(os.Stdout, log.TerminalFormat(false))) 215 glogger.Verbosity(log.LvlInfo) 216 log.Root().SetHandler(glogger) 217 } 218 219 func GetGlogger() (*log.GlogHandler, error) { 220 if glogger != nil { 221 return glogger, nil 222 } 223 return nil, fmt.Errorf("glogger is nil") 224 } 225 226 // CreateLogDir creates a directory whose path is logdir as well as empty log files. 227 func CreateLogDir(logDir string) { 228 if logDir == "" { 229 return 230 } 231 Handler.logDir = logDir 232 233 // Currently failures on directory or file creation is treated as a warning. 234 if err := os.MkdirAll(logDir, 0o700); err != nil { 235 logger.Warn("Failed to create a directory", "logDir", logDir, "err", err) 236 } 237 } 238 239 // Setup initializes profiling and logging based on the CLI flags. 240 // It should be called as early as possible in the program. 241 func Setup(ctx *cli.Context) error { 242 var ( 243 output = io.Writer(os.Stderr) 244 logFmtFlag = ctx.String(logFormatFlag.Name) 245 logfmt log.Format 246 ) 247 248 switch logFmtFlag { 249 case "json": 250 logfmt = log.JsonFormat() 251 case "logfmt": 252 logfmt = log.LogfmtFormat() 253 case "terminal": 254 useColor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb" 255 if useColor { 256 output = colorable.NewColorableStdout() 257 } 258 logfmt = log.TerminalFormat(useColor) 259 default: 260 // Unknown log format specified 261 return fmt.Errorf("unknown log format: %v", ctx.String(logFormatFlag.Name)) 262 } 263 264 var ( 265 ostream = log.StreamHandler(output, logfmt) 266 logFile = ctx.String(logFileFlag.Name) 267 rotation = ctx.Bool(logRotateFlag.Name) 268 context = []interface{}{"rotate", rotation} 269 ) 270 271 // if logFile is not set when rotation, set default log name 272 if rotation && len(logFile) == 0 { 273 logFile = filepath.Join(os.TempDir(), "klaytn-lumberjack.log") 274 } 275 if len(logFile) > 0 { 276 context = append(context, "format", logFmtFlag, "location", logFile) 277 if err := validateLogLocation(logFile); err != nil { 278 return fmt.Errorf("tried to create a temporary file to verify that the log path is writable, but it failed: %v", err) 279 } 280 if rotation { 281 ostream = log.StreamHandler(&lumberjack.Logger{ 282 Filename: logFile, 283 MaxSize: ctx.Int(logMaxSizeMBsFlag.Name), 284 MaxBackups: ctx.Int(logMaxBackupsFlag.Name), 285 MaxAge: ctx.Int(logMaxAgeFlag.Name), 286 Compress: ctx.Bool(logCompressFlag.Name), 287 }, logfmt) 288 } else { 289 logOutputStream, err := log.FileHandler(logFile, logfmt) 290 if err != nil { 291 return err 292 } 293 ostream = logOutputStream 294 } 295 } 296 glogger = log.NewGlogHandler(ostream) 297 298 // logging 299 log.PrintOrigins(ctx.Bool(debugFlag.Name)) 300 if err := log.ChangeGlobalLogLevel(glogger, log.Lvl(ctx.Int(verbosityFlag.Name))); err != nil { 301 return err 302 } 303 if err := glogger.Vmodule(ctx.String(vmoduleFlag.Name)); err != nil { 304 return err 305 } 306 if len(ctx.String(backtraceAtFlag.Name)) != 0 { 307 if err := glogger.BacktraceAt(ctx.String(backtraceAtFlag.Name)); err != nil { 308 return err 309 } 310 } 311 log.Root().SetHandler(glogger) 312 313 // profiling, tracing 314 runtime.MemProfileRate = ctx.Int(memprofilerateFlag.Name) 315 Handler.SetBlockProfileRate(ctx.Int(blockprofilerateFlag.Name)) 316 if traceFile := ctx.String(traceFlag.Name); traceFile != "" { 317 if err := Handler.StartGoTrace(traceFile); err != nil { 318 return err 319 } 320 } 321 if cpuFile := ctx.String(cpuprofileFlag.Name); cpuFile != "" { 322 if err := Handler.StartCPUProfile(cpuFile); err != nil { 323 return err 324 } 325 } 326 Handler.memFile = ctx.String(memprofileFlag.Name) 327 328 // pprof server 329 if ctx.Bool(pprofFlag.Name) { 330 addr := ctx.String(pprofAddrFlag.Name) 331 port := ctx.Int(pprofPortFlag.Name) 332 Handler.StartPProf(&addr, &port) 333 } 334 if len(logFile) > 0 { 335 logger.Info("Logging configured", context...) 336 } 337 return nil 338 } 339 340 // Exit stops all running profiles, flushing their output to the 341 // respective file. 342 func Exit() { 343 if Handler.vmLogFile != nil { 344 Handler.vmLogFile.Close() 345 Handler.vmLogFile = nil 346 } 347 if Handler.memFile != "" { 348 Handler.WriteMemProfile(Handler.memFile) 349 } 350 Handler.StopCPUProfile() 351 Handler.StopGoTrace() 352 Handler.StopPProf() 353 } 354 355 func validateLogLocation(path string) error { 356 if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil { 357 return fmt.Errorf("error creating the directory: %w", err) 358 } 359 // Check if the path is writable by trying to create a temporary file 360 tmp := path + ".temp" 361 if f, err := os.Create(tmp); err != nil { 362 return err 363 } else { 364 f.Close() 365 } 366 return os.Remove(tmp) 367 }