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  }