github.com/cnboonhan/delve@v0.0.0-20230908061759-363f2388c2fb/pkg/logflags/logflags.go (about) 1 package logflags 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "log" 10 "net" 11 "os" 12 "sort" 13 "strconv" 14 "strings" 15 "time" 16 17 "github.com/sirupsen/logrus" 18 ) 19 20 var any = false 21 var debugger = false 22 var gdbWire = false 23 var lldbServerOutput = false 24 var debugLineErrors = false 25 var rpc = false 26 var dap = false 27 var fnCall = false 28 var minidump = false 29 var stack = false 30 31 var logOut io.WriteCloser 32 33 func makeLogger(flag bool, fields Fields) Logger { 34 if lf := loggerFactory; lf != nil { 35 return lf(flag, fields, logOut) 36 } 37 logger := logrus.New().WithFields(logrus.Fields(fields)) 38 logger.Logger.Formatter = DefaultFormatter() 39 if logOut != nil { 40 logger.Logger.Out = logOut 41 } 42 logger.Logger.Level = logrus.ErrorLevel 43 if flag { 44 logger.Logger.Level = logrus.DebugLevel 45 } 46 return &logrusLogger{logger} 47 } 48 49 // Any returns true if any logging is enabled. 50 func Any() bool { 51 return any 52 } 53 54 // GdbWire returns true if the gdbserial package should log all the packets 55 // exchanged with the stub. 56 func GdbWire() bool { 57 return gdbWire 58 } 59 60 // GdbWireLogger returns a configured logger for the gdbserial wire protocol. 61 func GdbWireLogger() Logger { 62 return makeLogger(gdbWire, Fields{"layer": "gdbconn"}) 63 } 64 65 // Debugger returns true if the debugger package should log. 66 func Debugger() bool { 67 return debugger 68 } 69 70 // DebuggerLogger returns a logger for the debugger package. 71 func DebuggerLogger() Logger { 72 return makeLogger(debugger, Fields{"layer": "debugger"}) 73 } 74 75 // LLDBServerOutput returns true if the output of the LLDB server should be 76 // redirected to standard output instead of suppressed. 77 func LLDBServerOutput() bool { 78 return lldbServerOutput 79 } 80 81 // DebugLineErrors returns true if pkg/dwarf/line should log its recoverable 82 // errors. 83 func DebugLineErrors() bool { 84 return debugLineErrors 85 } 86 87 // DebugLineLogger returns a logger for the dwarf/line package. 88 func DebugLineLogger() Logger { 89 return makeLogger(debugLineErrors, Fields{"layer": "dwarf-line"}) 90 } 91 92 // RPC returns true if RPC messages should be logged. 93 func RPC() bool { 94 return rpc 95 } 96 97 // RPCLogger returns a logger for RPC messages. 98 func RPCLogger() Logger { 99 return rpcLogger(rpc) 100 } 101 102 // rpcLogger returns a logger for RPC messages set to a specific minimal log level. 103 func rpcLogger(flag bool) Logger { 104 return makeLogger(flag, Fields{"layer": "rpc"}) 105 } 106 107 // DAP returns true if dap package should log. 108 func DAP() bool { 109 return dap 110 } 111 112 // DAPLogger returns a logger for dap package. 113 func DAPLogger() Logger { 114 return makeLogger(dap, Fields{"layer": "dap"}) 115 } 116 117 // FnCall returns true if the function call protocol should be logged. 118 func FnCall() bool { 119 return fnCall 120 } 121 122 func FnCallLogger() Logger { 123 return makeLogger(fnCall, Fields{"layer": "proc", "kind": "fncall"}) 124 } 125 126 // Minidump returns true if the minidump loader should be logged. 127 func Minidump() bool { 128 return minidump 129 } 130 131 func MinidumpLogger() Logger { 132 return makeLogger(minidump, Fields{"layer": "core", "kind": "minidump"}) 133 } 134 135 // Stack returns true if the stacktracer should be logged. 136 func Stack() bool { 137 return stack 138 } 139 140 func StackLogger() Logger { 141 return makeLogger(stack, Fields{"layer": "core", "kind": "stack"}) 142 } 143 144 // WriteDAPListeningMessage writes the "DAP server listening" message in dap mode. 145 func WriteDAPListeningMessage(addr net.Addr) { 146 writeListeningMessage("DAP", addr) 147 } 148 149 // WriteAPIListeningMessage writes the "API server listening" message in headless mode. 150 func WriteAPIListeningMessage(addr net.Addr) { 151 writeListeningMessage("API", addr) 152 } 153 154 func writeListeningMessage(server string, addr net.Addr) { 155 msg := fmt.Sprintf("%s server listening at: %s", server, addr) 156 if logOut != nil { 157 fmt.Fprintln(logOut, msg) 158 } else { 159 fmt.Println(msg) 160 } 161 tcpAddr, _ := addr.(*net.TCPAddr) 162 if tcpAddr == nil || tcpAddr.IP.IsLoopback() { 163 return 164 } 165 logger := rpcLogger(true) 166 logger.Warn("Listening for remote connections (connections are not authenticated nor encrypted)") 167 } 168 169 func WriteError(msg string) { 170 if logOut != nil { 171 fmt.Fprintln(logOut, msg) 172 } else { 173 fmt.Fprintln(os.Stderr, msg) 174 } 175 } 176 177 var errLogstrWithoutLog = errors.New("--log-output specified without --log") 178 179 // Setup sets debugger flags based on the contents of logstr. 180 // If logDest is not empty logs will be redirected to the file descriptor or 181 // file path specified by logDest. 182 func Setup(logFlag bool, logstr, logDest string) error { 183 if logDest != "" { 184 n, err := strconv.Atoi(logDest) 185 if err == nil { 186 logOut = os.NewFile(uintptr(n), "delve-logs") 187 } else { 188 fh, err := os.Create(logDest) 189 if err != nil { 190 return fmt.Errorf("could not create log file: %v", err) 191 } 192 logOut = fh 193 } 194 } 195 log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) 196 if !logFlag { 197 log.SetOutput(ioutil.Discard) 198 if logstr != "" { 199 return errLogstrWithoutLog 200 } 201 return nil 202 } 203 if logstr == "" { 204 logstr = "debugger" 205 } 206 any = true 207 v := strings.Split(logstr, ",") 208 for _, logcmd := range v { 209 // If adding another value, do make sure to 210 // update "Help about logging flags" in commands.go. 211 switch logcmd { 212 case "debugger": 213 debugger = true 214 case "gdbwire": 215 gdbWire = true 216 case "lldbout": 217 lldbServerOutput = true 218 case "debuglineerr": 219 debugLineErrors = true 220 case "rpc": 221 rpc = true 222 case "dap": 223 dap = true 224 case "fncall": 225 fnCall = true 226 case "minidump": 227 minidump = true 228 case "stack": 229 stack = true 230 default: 231 fmt.Fprintf(os.Stderr, "Warning: unknown log output value %q, run 'dlv help log' for usage.\n", logcmd) 232 } 233 } 234 return nil 235 } 236 237 // Close closes the logger output. 238 func Close() { 239 if logOut != nil { 240 logOut.Close() 241 } 242 } 243 244 // DefaultFormatter provides a simplified version of logrus.TextFormatter that 245 // doesn't make logs unreadable when they are output to a text file or to a 246 // terminal that doesn't support colors. 247 func DefaultFormatter() logrus.Formatter { 248 return textFormatterInstance 249 } 250 251 type textFormatter struct{} 252 253 var textFormatterInstance = &textFormatter{} 254 255 func (f *textFormatter) Format(entry *logrus.Entry) ([]byte, error) { 256 var b *bytes.Buffer 257 if entry.Buffer != nil { 258 b = entry.Buffer 259 } else { 260 b = &bytes.Buffer{} 261 } 262 263 keys := make([]string, 0, len(entry.Data)) 264 for k := range entry.Data { 265 keys = append(keys, k) 266 } 267 sort.Strings(keys) 268 269 b.WriteString(entry.Time.Format(time.RFC3339)) 270 b.WriteByte(' ') 271 b.WriteString(entry.Level.String()) 272 b.WriteByte(' ') 273 for i, key := range keys { 274 b.WriteString(key) 275 b.WriteByte('=') 276 stringVal, ok := entry.Data[key].(string) 277 if !ok { 278 stringVal = fmt.Sprint(entry.Data[key]) 279 } 280 if f.needsQuoting(stringVal) { 281 fmt.Fprintf(b, "%q", stringVal) 282 } else { 283 b.WriteString(stringVal) 284 } 285 if i != len(keys)-1 { 286 b.WriteByte(',') 287 } else { 288 b.WriteByte(' ') 289 } 290 } 291 b.WriteString(entry.Message) 292 b.WriteByte('\n') 293 return b.Bytes(), nil 294 } 295 296 func (f *textFormatter) needsQuoting(text string) bool { 297 for _, ch := range text { 298 if !((ch >= 'a' && ch <= 'z') || 299 (ch >= 'A' && ch <= 'Z') || 300 (ch >= '0' && ch <= '9') || 301 ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '@' || ch == '^' || ch == '+') { 302 return true 303 } 304 } 305 return false 306 }