kiss server monitoring tool with email alerts
go monitoring
at main 165 lines 4.4 kB view raw
1package main 2 3import ( 4 "context" 5 "errors" 6 "fmt" 7 "os" 8 "os/signal" 9 "path" 10 "runtime/debug" 11 "strings" 12 "syscall" 13 "time" 14 15 "pkg.rbrt.fr/servmon/internal/alert" 16 "pkg.rbrt.fr/servmon/internal/config" 17 "pkg.rbrt.fr/servmon/internal/monitor" 18 19 "github.com/spf13/cobra" 20) 21 22var ( 23 flagConfig = "config" 24 cfgFile string 25) 26 27func main() { 28 homeDir, err := os.UserHomeDir() 29 if err != nil { 30 fmt.Fprintf(os.Stderr, "error getting user home directory: %v", err) 31 os.Exit(1) 32 } 33 34 version, err := getVersion() 35 if err != nil { 36 fmt.Fprintf(os.Stderr, "error getting version: %v", err) 37 version = "unknown" 38 } 39 40 rootCmd := &cobra.Command{ 41 Use: "servmon", 42 Short: "Server monitoring tool with email alerts", 43 Version: version, 44 RunE: func(cmd *cobra.Command, args []string) error { 45 cfgPath, err := cmd.Flags().GetString(flagConfig) 46 if err != nil { 47 return fmt.Errorf("error getting flag %s: %v", flagConfig, err) 48 } 49 50 if _, err := os.Stat(cfgPath); os.IsNotExist(err) { 51 cfg := config.Default() 52 if err := cfg.Save(cfgFile); err != nil { 53 return err 54 } 55 56 cmd.Println("✓ Configuration file generated at", cfgFile) 57 cmd.Println("Please edit the configuration file and restart servmon") 58 return nil 59 } else if err != nil { 60 return fmt.Errorf("error checking config file: %v", err) 61 } 62 63 cfg, err := config.Load(cfgPath) 64 if err != nil { 65 return err 66 } 67 68 // Show current system status 69 if status, err := monitor.GetCurrentStatus(); err == nil { 70 cmd.Println() 71 cmd.Println(status) 72 cmd.Println() 73 } 74 75 // Set up signal handling for graceful shutdown 76 sigChan := make(chan os.Signal, 1) 77 signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) 78 79 // Create context for graceful shutdown 80 ctx, cancel := context.WithCancel(context.Background()) 81 defer cancel() 82 83 // Initialize alerter 84 emailAlerter := alert.NewEmailAlerter(alert.EmailConfig{ 85 SMTPServer: cfg.Email.SMTPServer, 86 SMTPPort: cfg.Email.SMTPPort, 87 From: cfg.Email.From, 88 To: cfg.Email.To, 89 Username: cfg.Email.Username, 90 Password: cfg.Email.Password, 91 }) 92 93 // Check for recent reboot and send notification if needed 94 if err := monitor.CheckRebootAndNotify(ctx, cfg, emailAlerter); err != nil { 95 cmd.Printf("Warning: Failed to check reboot status: %v\n", err) 96 } 97 98 // Create monitor with alerter 99 mon := monitor.New(cfg, emailAlerter) 100 101 // Start monitoring 102 mon.Start(ctx) 103 104 cmd.Println() 105 cmd.Println("✓ ServMon started successfully. Monitoring active.") 106 cmd.Println("Monitoring in progress... Press Ctrl+C to stop.") 107 cmd.Println() 108 109 // Send startup notification 110 go func() { 111 hostname, err := os.Hostname() 112 if err != nil { 113 hostname = "unknown" 114 } 115 116 startupAlert := alert.NewAlert( 117 alert.LevelInfo, 118 fmt.Sprintf("Monitoring Started on %s", hostname), 119 "ServMon has started successfully and is now actively monitoring your system.", 120 ) 121 122 startupAlert.WithMetadata("cpu_threshold", fmt.Sprintf("%.1f%%", cfg.AlertThresholds.CPU.Threshold)) 123 startupAlert.WithMetadata("memory_threshold", fmt.Sprintf("%.1f%%", cfg.AlertThresholds.Memory.Threshold)) 124 startupAlert.WithMetadata("disk_paths", cfg.GetDiskPaths()) 125 126 if cfg.AlertThresholds.HTTP.URL != "" { 127 startupAlert.WithMetadata("http_endpoint", cfg.AlertThresholds.HTTP.URL) 128 } 129 130 if err := emailAlerter.Send(ctx, startupAlert); err != nil { 131 cmd.Printf("Warning: Failed to send startup notification: %v\n", err) 132 } else { 133 cmd.Println("✓ Startup notification sent successfully") 134 } 135 }() 136 137 // Wait for shutdown signal 138 sig := <-sigChan 139 cmd.Printf("Received signal %v, shutting down gracefully...\n", sig) 140 cancel() 141 142 // Give goroutines time to clean up 143 time.Sleep(1 * time.Second) 144 cmd.Println("✓ ServMon stopped successfully") 145 return nil 146 }, 147 } 148 149 rootCmd.CompletionOptions.DisableDefaultCmd = true 150 rootCmd.PersistentFlags().StringVar(&cfgFile, flagConfig, path.Join(homeDir, ".servmon.yaml"), "config file") 151 152 if err := rootCmd.Execute(); err != nil { 153 fmt.Fprint(os.Stderr, err) 154 os.Exit(1) 155 } 156} 157 158func getVersion() (string, error) { 159 version, ok := debug.ReadBuildInfo() 160 if !ok { 161 return "", errors.New("failed to get version") 162 } 163 164 return strings.TrimSpace(version.Main.Version), nil 165}