From 4223ca6e55e68f947d555ae9d7676b187ce86365 Mon Sep 17 00:00:00 2001 From: Pulkit Kathuria Date: Tue, 14 Jan 2025 09:00:47 +0900 Subject: [PATCH] feat: notify own any error --- main.go | 17 +------------ pkg/flags.go | 2 +- pkg/log.go | 67 ++++++++++++++++++++++++++++++++++++++++----------- pkg/notify.go | 44 ++++++++++----------------------- pkg/system.go | 27 +-------------------- 5 files changed, 69 insertions(+), 88 deletions(-) diff --git a/main.go b/main.go index 24e5294..f8516b6 100644 --- a/main.go +++ b/main.go @@ -22,7 +22,7 @@ var filePathsMutex sync.Mutex func main() { pkg.Parseflags(&f) - pkg.SetupLoggingStdout(f.LogLevel, f.LogFile) // nolint: errcheck + pkg.SetupLoggingStdout(f) // nolint: errcheck flag.VisitAll(func(f *flag.Flag) { slog.Info(f.Name, slog.String("value", f.Value.String())) }) @@ -38,12 +38,10 @@ func main() { var err error newFilePaths, err := pkg.FilesByPattern(f.FilePath, f.NotifyOnlyRecent) if err != nil { - pkg.NotifyOwnError(fmt.Errorf("error finding files: %s", err.Error()), f) slog.Error("Error finding files", "error", err.Error()) return } if len(newFilePaths) == 0 { - pkg.NotifyOwnError(fmt.Errorf("no files found"), f) slog.Error("No files found", "filePath", f.FilePath) slog.Warn("Keep watching for new files") } @@ -57,12 +55,10 @@ func main() { for _, filePath := range filePaths { isText, err := pkg.IsTextFile(filePath) if err != nil { - pkg.NotifyOwnError(fmt.Errorf("error checking if file is text: %s", err.Error()), f) slog.Error("Error checking if file is text", "error", err.Error(), "filePath", filePath) return } if !isText { - pkg.NotifyOwnError(fmt.Errorf("file is not a text file"), f) slog.Error("File is not a text file", "filePath", filePath) return } @@ -78,25 +74,21 @@ func main() { func startCron() { if err := gocron.Every(1).Second().Do(pkg.PrintMemUsage, &f); err != nil { - pkg.NotifyOwnError(fmt.Errorf("error scheduling memory usage: %s", err.Error()), f) slog.Error("Error scheduling memory usage", "error", err.Error()) return } if err := gocron.Every(f.Every).Second().Do(syncFilePaths); err != nil { - pkg.NotifyOwnError(fmt.Errorf("error scheduling syncFilePaths: %s", err.Error()), f) slog.Error("Error scheduling syncFilePaths", "error", err.Error()) return } if f.HealthCheckEvery > 0 { if err := gocron.Every(f.HealthCheckEvery).Second().Do(sendHealthCheck); err != nil { - pkg.NotifyOwnError(fmt.Errorf("error scheduling health check: %s", err.Error()), f) slog.Error("Error scheduling health check", "error", err.Error()) return } } if err := gocron.Every(f.Every).Second().Do(cron); err != nil { - pkg.NotifyOwnError(fmt.Errorf("error scheduling cron: %s", err.Error()), f) slog.Error("Error scheduling cron", "error", err.Error()) return } @@ -112,7 +104,6 @@ func cron() { } if f.PostAlways != "" { if _, err := pkg.ExecShell(f.PostAlways); err != nil { - pkg.NotifyOwnError(fmt.Errorf("error running post always command: %s", err.Error()), f) slog.Error("Error running post command", "error", err.Error()) } } @@ -122,12 +113,10 @@ func syncFilePaths() { var err error newFilePaths, err := pkg.FilesByPattern(f.FilePath, f.NotifyOnlyRecent) if err != nil { - pkg.NotifyOwnError(fmt.Errorf("error finding files: %s", err.Error()), f) slog.Error("Error finding files", "error", err.Error()) return } if len(newFilePaths) == 0 { - pkg.NotifyOwnError(fmt.Errorf("no files found"), f) slog.Error("No files found", "filePath", f.FilePath) slog.Warn("Keep watching for new files") return @@ -170,7 +159,6 @@ func sendHealthCheck() { func validate() { if f.FilePath == "" { - pkg.NotifyOwnError(fmt.Errorf("file-path is required"), f) slog.Error("file-path is required") os.Exit(1) } @@ -180,7 +168,6 @@ func watch(filePath string) { watcher, err := pkg.NewWatcher(filePath, f) if err != nil { - pkg.NotifyOwnError(fmt.Errorf("error creating watcher: %s", err.Error()), f) slog.Error("Error creating watcher", "error", err.Error(), "filePath", filePath) return } @@ -190,7 +177,6 @@ func watch(filePath string) { result, err := watcher.Scan() if err != nil { - pkg.NotifyOwnError(fmt.Errorf("error scanning file: %s", err.Error()), f) slog.Error("Error scanning file", "error", err.Error(), "filePath", filePath) return } @@ -216,7 +202,6 @@ func watch(filePath string) { } if f.PostCommand != "" { if _, err := pkg.ExecShell(f.PostCommand); err != nil { - pkg.NotifyOwnError(fmt.Errorf("error running post command: %s", err.Error()), f) slog.Error("Error running post command", "error", err.Error()) } } diff --git a/pkg/flags.go b/pkg/flags.go index fe7cf07..3465d9b 100644 --- a/pkg/flags.go +++ b/pkg/flags.go @@ -40,7 +40,7 @@ func Parseflags(f *Flags) { sends health check ping to ms teams webhook `) flag.IntVar(&f.LogLevel, "log-level", 0, "log level (0=info, -4=debug, 4=warn, 8=error)") - flag.IntVar(&f.MemLimit, "mem-limit", 300, "memory limit in MB (0 to disable)") + flag.IntVar(&f.MemLimit, "mem-limit", 256, "memory limit in MB (0 to disable)") flag.IntVar(&f.FilePathsCap, "file-paths-cap", 100, "max number of file paths to watch") flag.IntVar(&f.Min, "min", 1, "on minimum num of matches, it should notify") flag.BoolVar(&f.NotifyOnlyRecent, "notify-only-recent", true, "Notify on latest file only by timestamp based on --every") diff --git a/pkg/log.go b/pkg/log.go index eb90751..0d02fb3 100644 --- a/pkg/log.go +++ b/pkg/log.go @@ -1,6 +1,7 @@ package pkg import ( + "context" "fmt" "log/slog" "os" @@ -10,25 +11,63 @@ import ( "github.com/natefinch/lumberjack" ) -func SetupLoggingStdout(logLevel int, logFile string) error { +// GlobalHandler is a custom handler that catches all logs +type GlobalHandler struct { + next slog.Handler + msTeamsHook string + proxy string +} + +func (h *GlobalHandler) Handle(ctx context.Context, r slog.Record) error { + if r.Level.String() == "ERROR" { + err := fmt.Errorf("global log capture - Level: %s, Message: %s", r.Level.String(), r.Message) + NotifyOwnError(err, r, h.msTeamsHook, h.proxy) + } + + return h.next.Handle(ctx, r) +} + +func (h *GlobalHandler) Enabled(ctx context.Context, level slog.Level) bool { + return h.next.Enabled(ctx, level) +} + +func (h *GlobalHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + return &GlobalHandler{next: h.next.WithAttrs(attrs)} +} + +func (h *GlobalHandler) WithGroup(name string) slog.Handler { + return &GlobalHandler{next: h.next.WithGroup(name)} +} + +func SetupLoggingStdout(f Flags) error { opts := &slogcolor.Options{ - Level: slog.Level(logLevel), + Level: slog.Level(f.LogLevel), TimeFormat: "2006-01-02 15:04:05", NoColor: !isatty.IsTerminal(os.Stderr.Fd()), SrcFileMode: slogcolor.ShortFile, } - if logFile == "" { - slog.SetDefault(slog.New(slogcolor.NewHandler(os.Stderr, opts))) - return nil + + var handler slog.Handler + if f.LogFile == "" { + handler = slogcolor.NewHandler(os.Stdout, opts) + } else { + handler = slogcolor.NewHandler(&lumberjack.Logger{ + Filename: f.LogFile, + MaxSize: 10, // megabytes + MaxBackups: 3, + MaxAge: 3, // days + LocalTime: true, + Compress: true, + }, opts) + fmt.Println("logging to file", f.LogFile) + } + + // Wrap the handler with the GlobalHandler + globalHandler := &GlobalHandler{ + next: handler, + msTeamsHook: f.MSTeamsHook, + proxy: f.Proxy, } - slog.SetDefault(slog.New(slogcolor.NewHandler(&lumberjack.Logger{ - Filename: logFile, - MaxSize: 10, // megabytes - MaxBackups: 3, - MaxAge: 3, // days - LocalTime: true, - Compress: true, - }, opts))) - fmt.Println("logging to file", logFile) + slog.SetDefault(slog.New(globalHandler)) return nil } diff --git a/pkg/notify.go b/pkg/notify.go index 366e1a4..abc8df4 100644 --- a/pkg/notify.go +++ b/pkg/notify.go @@ -4,12 +4,11 @@ import ( "fmt" "log/slog" "os" - "runtime" gmt "github.com/kevincobain2000/go-msteams/src" ) -func NotifyOwnError(e error, f Flags) { +func NotifyOwnError(e error, r slog.Record, msTeamsHook, proxy string) { hostname, _ := os.Hostname() details := []gmt.Details{ { @@ -21,7 +20,18 @@ func NotifyOwnError(e error, f Flags) { Message: e.Error(), }, } - err := gmt.Send(hostname, details, f.MSTeamsHook, f.Proxy) + r.Attrs(func(attr slog.Attr) bool { + details = append(details, gmt.Details{ + Label: attr.Key, + Message: fmt.Sprintf("%v", attr.Value), + }) + return true + }) + if msTeamsHook == "" { + slog.Warn("MS Teams hook not set") + return + } + err := gmt.Send(hostname, details, msTeamsHook, proxy) if err != nil { slog.Error("Error sending to Teams", "error", err.Error()) } else { @@ -54,34 +64,6 @@ func Notify(result *ScanResult, f Flags, version string) { } } -func GetPanicDetails(f *Flags, m *runtime.MemStats) []gmt.Details { - return []gmt.Details{ - { - Label: "File Path", - Message: f.FilePath, - }, - { - Label: "Match Pattern", - Message: f.Match, - }, - { - Label: "Ignore Pattern", - Message: f.Ignore, - }, - { - Label: "Panic Message", - Message: "go-watch-logs has panicked", - }, - { - Label: "Mem Limit", - Message: fmt.Sprintf("%d(MB)", f.MemLimit), - }, - { - Label: "Alloc", - Message: fmt.Sprintf("%d(MB)", BToMb(m.Alloc)), - }, - } -} func GetHealthCheckDetails(f *Flags, version string) []gmt.Details { return []gmt.Details{ { diff --git a/pkg/system.go b/pkg/system.go index 08741cf..cdc2d00 100644 --- a/pkg/system.go +++ b/pkg/system.go @@ -5,8 +5,6 @@ import ( "os" "os/exec" "runtime" - - gmt "github.com/kevincobain2000/go-msteams/src" ) func SystemProxy() string { @@ -33,7 +31,7 @@ func PrintMemUsage(f *Flags) { "HeapSys (Bytes)", m.HeapSys, ) if f.MemLimit > 0 && m.Alloc > uint64(f.MemLimit)*1024*1024 { - sendPanicCheck(f, &m) + slog.Error("Memory Limit Exceeded", "limit", f.MemLimit, "current", BToMb(m.Alloc)) panic("Memory Limit Exceeded") } } @@ -48,29 +46,6 @@ func ExecShell(command string) (string, error) { return string(out), err } -func sendPanicCheck(f *Flags, m *runtime.MemStats) { - details := GetPanicDetails(f, m) - var logDetails []interface{} // nolint: prealloc - for _, detail := range details { - logDetails = append(logDetails, detail.Label, detail.Message) - } - slog.Warn("Sending Panic Check", logDetails...) - - if f.MSTeamsHook == "" { - slog.Warn("MS Teams hook not set") - return - } - - hostname, _ := os.Hostname() - - err := gmt.Send(hostname, details, f.MSTeamsHook, f.Proxy) - if err != nil { - slog.Error("Error sending to Teams", "error", err.Error()) - } else { - slog.Info("Successfully sent to MS Teams") - } -} - func ReadFromPipeInput() string { fi, _ := os.Stdin.Stat() if (fi.Mode() & os.ModeCharDevice) == 0 {