github.com/telepresenceio/telepresence/v2@v2.20.0-pro.6.0.20240517030216-236ea954e789/pkg/client/logging/initcontext.go (about) 1 package logging 2 3 import ( 4 "bufio" 5 "context" 6 "fmt" 7 "log" 8 "os" 9 "path/filepath" 10 "strconv" 11 "strings" 12 13 "github.com/sirupsen/logrus" 14 15 "github.com/datawire/dlib/dlog" 16 "github.com/telepresenceio/telepresence/v2/pkg/client" 17 "github.com/telepresenceio/telepresence/v2/pkg/dos" 18 "github.com/telepresenceio/telepresence/v2/pkg/filelocation" 19 tlog "github.com/telepresenceio/telepresence/v2/pkg/log" 20 ) 21 22 // loggerForTest exposes internals to initcontext_test.go. 23 var loggerForTest *logrus.Logger //nolint:gochecknoglobals // used by unit tests only 24 25 // InitContext sets up standard Telepresence logging for a background process. 26 func InitContext(ctx context.Context, name string, strategy RotationStrategy, captureStd bool) (context.Context, error) { 27 logger := logrus.StandardLogger() 28 loggerForTest = logger 29 30 // Start with InfoLevel so that the config is read using that level 31 logger.SetLevel(logrus.InfoLevel) 32 logger.ReportCaller = false // turned on when level >= logrus.TraceLevel 33 34 if captureStd && IsTerminal(int(os.Stdout.Fd())) { 35 logger.Formatter = tlog.NewFormatter("15:04:05.0000") 36 } else { 37 logger.Formatter = tlog.NewFormatter("2006-01-02 15:04:05.0000") 38 maxFiles := uint16(5) 39 40 // TODO: Also make this a configurable setting in config.yml 41 if me := os.Getenv("TELEPRESENCE_MAX_LOGFILES"); me != "" { 42 if mx, err := strconv.Atoi(me); err == nil && mx >= 0 { 43 maxFiles = uint16(mx) 44 } 45 } 46 rf, err := OpenRotatingFile(ctx, filepath.Join(filelocation.AppUserLogDir(ctx), name+".log"), "20060102T150405", true, 0o600, strategy, maxFiles) 47 if err != nil { 48 return ctx, err 49 } 50 logger.SetOutput(rf) 51 52 if captureStd { 53 if err := dupToStdOut(rf.file.(*os.File)); err != nil { 54 return ctx, err 55 } 56 if err := dupToStdErr(rf.file.(*os.File)); err != nil { 57 return ctx, err 58 } 59 } 60 61 // Configure the standard logger to write without any fields and with prefix "stdlog" 62 log.SetOutput(logger.Writer()) 63 log.SetPrefix("stdlog : ") 64 log.SetFlags(0) 65 } 66 67 ctx = dlog.WithLogger(ctx, dlog.WrapLogrus(logger)) 68 69 // Read the config and set the configured level. 70 logLevels := client.GetConfig(ctx).LogLevels() 71 level := logLevels.UserDaemon 72 if name == "daemon" { 73 level = logLevels.RootDaemon 74 } 75 tlog.SetLogrusLevel(logger, level.String(), false) 76 ctx = tlog.WithLevelSetter(ctx, logger) 77 return ctx, nil 78 } 79 80 func SummarizeLog(ctx context.Context, name string) (string, error) { 81 filename := filepath.Join(filelocation.AppUserLogDir(ctx), name+".log") 82 file, err := dos.Open(ctx, filename) 83 if err != nil { 84 if os.IsNotExist(err) { 85 err = nil 86 } 87 return "", err 88 } 89 defer file.Close() 90 scanner := bufio.NewScanner(file) 91 92 errorCount := 0 93 for scanner.Scan() { 94 // XXX: is there a better way to detect error lines? 95 txt := scanner.Text() 96 parts := strings.Fields(txt) 97 if len(parts) < 3 { 98 continue 99 } 100 switch parts[2] { 101 case "error": 102 errorCount++ 103 case "info": 104 if strings.Contains(txt, "-- Starting new session") { 105 // Start over. No use counting errors from previous sessions 106 errorCount = 0 107 } 108 } 109 } 110 if errorCount == 0 { 111 return "", nil 112 } 113 desc := fmt.Sprintf("%d error", errorCount) 114 if errorCount > 1 { 115 desc += "s" 116 } 117 118 return fmt.Sprintf("See logs for details (%s found): %q", desc, filename), nil 119 }