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  }