package state import ( "encoding/json" "fmt" "io/ioutil" "jobwatch/types" "log" "os" "path/filepath" "strings" ) type LogEntryState string const MAX_LOG_ENTRIES = 2500 const MAX_LOG_SEND = 250 const ( LOG_OK LogEntryState = "O" LOG_WARN LogEntryState = "W" LOG_CRIT LogEntryState = "C" LOG_UNKWN LogEntryState = "U" ) type LogEntry struct { Id int `json:"id"` Date string `json:"date"` State LogEntryState `json:"state"` Text string `json:"text"` } type LogDb struct { JobFullName string `json:"job_full_name"` MaxSentByRemote map[string]int `json:"max_sent_by_remote"` Entries []LogEntry `json:"entries"` } func LoadLog(logPath string) (*LogDb, error) { if _, err := os.Stat(logPath); err != nil { log.Println("Existing log not readable, creating new") return &LogDb{}, nil } data, err := os.ReadFile(logPath) if err != nil { return nil, err } var logDb LogDb err = json.Unmarshal(data, &logDb) if err != nil { return nil, fmt.Errorf("Error unmarshaling state: %v", err) } log.Println("existing log loaded") return &logDb, nil } func (logDb LogDb) Save(logPath string) error { data, err := json.MarshalIndent(logDb, " ", " ") if err != nil { return fmt.Errorf("Error marshaling state: %v", err) } if err := os.WriteFile(logPath, data, 0600); err != nil { return fmt.Errorf("Error writing log file %v: %v", logPath, err) } log.Println("Log written") return nil } func AppendLog(job types.Job, entries []LogEntry) error { lock, err := Lock(job.GetJobBaseFileName(), "LOG") if err != nil { return err } defer func() { Unlock(lock) }() logDir, err := getLibDir("states") if err != nil { return err } var logPath = filepath.FromSlash(fmt.Sprintf("%v/%v.log.json", logDir, job.GetJobBaseFileName())) logDb, err := LoadLog(logPath) if err != nil { return err } logDb.JobFullName = job.GetJobBaseFileName() var maxId int = 1 if len(logDb.Entries) > 0 { maxId = logDb.Entries[len(logDb.Entries)-1].Id } for _, e := range entries { maxId++ e.Id = maxId logDb.Entries = append(logDb.Entries, e) } for len(logDb.Entries) > MAX_LOG_ENTRIES { logDb.Entries = logDb.Entries[:1] } return logDb.Save(logPath) } func ShowAllLogs() { dirs := findAllStateDirs() var remote = os.Getenv("REMOTE") if remote == "" { remote = "-no-remote-" } for _, d := range dirs { fsi, err := ioutil.ReadDir(d.Path) log.Printf("Scanning path %v", d.Path) if err != nil { continue } for _, e := range fsi { if strings.LastIndex(e.Name(), ".log.json") < 0 { continue } p := filepath.FromSlash(d.Path + "/" + e.Name()) jfn := strings.Replace(e.Name(), ".log.json", "", -1) lock, err := Lock(jfn, "LOG") if err != nil { log.Printf("Can't lock log %v: %v, skipping!", p, err) continue } defer func() { Unlock(lock) }() log.Printf("Processing log file %v", p) logDb, err := LoadLog(p) if err != nil { log.Printf("Can't read log %v: %v, skipping!", p, err) continue } var maxSent = logDb.MaxSentByRemote[remote] fmt.Printf("[[[JobWatch %v/%v]]]\n", d.UserId, logDb.JobFullName) var sentRem = MAX_LOG_SEND for _, l := range logDb.Entries { if l.Id <= maxSent { continue } fmt.Printf("%v %v: %v\n", l.State, l.Date, l.Text) maxSent = l.Id sentRem-- if sentRem <= 0 { break } } if logDb.MaxSentByRemote == nil { logDb.MaxSentByRemote = map[string]int{} } logDb.MaxSentByRemote[remote] = maxSent logDb.Save(p) } } } func ToLogState(s types.CMKState) LogEntryState { var p = LOG_UNKWN switch s { case types.OK: p = LOG_OK case types.WARN: p = LOG_WARN case types.CRIT: p = LOG_CRIT } return p }