package util import ( "io" "os" "path/filepath" "slices" "strconv" log "github.com/sirupsen/logrus" "google.golang.org/grpc/grpclog" "gopkg.in/natefinch/lumberjack.v2" "github.com/netbirdio/netbird/formatter" ) const defaultLogSize = 15 const ( LogConsole = "console" LogSyslog = "syslog" ) var ( SpecialLogs = []string{ LogSyslog, LogConsole, } ) // InitLog parses and sets log-level input func InitLog(logLevel string, logs ...string) error { level, err := log.ParseLevel(logLevel) if err != nil { log.Errorf("Failed parsing log-level %s: %s", logLevel, err) return err } var writers []io.Writer logFmt := os.Getenv("NB_LOG_FORMAT") for _, logPath := range logs { switch logPath { case LogSyslog: AddSyslogHook() logFmt = "syslog" case LogConsole: writers = append(writers, os.Stderr) case "": log.Warnf("empty log path received: %#v", logPath) default: writers = append(writers, newRotatedOutput(logPath)) } } if len(writers) > 1 { log.SetOutput(io.MultiWriter(writers...)) } else if len(writers) == 1 { log.SetOutput(writers[0]) } switch logFmt { case "json": formatter.SetJSONFormatter(log.StandardLogger()) case "syslog": formatter.SetSyslogFormatter(log.StandardLogger()) default: formatter.SetTextFormatter(log.StandardLogger()) } log.SetLevel(level) setGRPCLibLogger() return nil } // FindFirstLogPath returns the first logs entry that could be a log path, that is neither empty, nor a special value func FindFirstLogPath(logs []string) string { for _, logFile := range logs { if logFile != "" && !slices.Contains(SpecialLogs, logFile) { return logFile } } return "" } func newRotatedOutput(logPath string) io.Writer { maxLogSize := getLogMaxSize() lumberjackLogger := &lumberjack.Logger{ // Log file absolute path, os agnostic Filename: filepath.ToSlash(logPath), MaxSize: maxLogSize, // MB MaxBackups: 10, MaxAge: 30, // days Compress: true, } return lumberjackLogger } func setGRPCLibLogger() { logOut := log.StandardLogger().Writer() if os.Getenv("GRPC_GO_LOG_SEVERITY_LEVEL") != "info" { grpclog.SetLoggerV2(grpclog.NewLoggerV2(io.Discard, logOut, logOut)) return } var v int vLevel := os.Getenv("GRPC_GO_LOG_VERBOSITY_LEVEL") if vl, err := strconv.Atoi(vLevel); err == nil { v = vl } grpclog.SetLoggerV2(grpclog.NewLoggerV2WithVerbosity(logOut, logOut, logOut, v)) } func getLogMaxSize() int { if sizeVar, ok := os.LookupEnv("NB_LOG_MAX_SIZE_MB"); ok { size, err := strconv.ParseInt(sizeVar, 10, 64) if err != nil { log.Errorf("Failed parsing log-size %s: %s. Should be just an integer", sizeVar, err) return defaultLogSize } log.Infof("Setting log file max size to %d MB", size) return int(size) } return defaultLogSize }