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