package loghandler import ( "bufio" "encoding/json" "fmt" "io" "jannex/log-manager/modules/cache" "jannex/log-manager/modules/config" "jannex/log-manager/modules/structs" "jannex/log-manager/modules/utils" "os" "path/filepath" "sort" "strconv" "strings" "sync" "time" ) var FileMutexMap = make(map[string]*sync.Mutex) var FileMutexMapLock sync.Mutex func getFileMutex(filePath string) *sync.Mutex { FileMutexMapLock.Lock() defer FileMutexMapLock.Unlock() mutex, ok := FileMutexMap[filePath] if !ok { mutex = &sync.Mutex{} FileMutexMap[filePath] = mutex } return mutex } func AddLog(body structs.LogBody) { year, month, day := time.Now().Date() date := strconv.Itoa(day) + "-" + strconv.Itoa(int(month)) + "-" + strconv.Itoa(year) logFolder := config.Cfg.LogFolder utils.CreateDirectoryIfNotExists(logFolder + body.Type) path := logFolder + body.Type + "/" + date + ".log" // get the mutex for this file mutex := getFileMutex(path) // lock the mutex to ensure the file is only opened once at a time mutex.Lock() defer mutex.Unlock() file, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { fmt.Println(err) return } defer file.Close() for _, log := range body.Logs { if _, err := fmt.Fprintln(file, log); err != nil { fmt.Println(err) } } marshaledLogs, err := json.Marshal(body.Logs) if err != nil { fmt.Println(err) return } for clientId, sseClient := range cache.GetSSEClients() { if sseClient.LogType == body.Type && sseClient.Date == date { sseClient.MessageChannel <- structs.SSEClientChannelMessage{ ClientId: clientId, Message: marshaledLogs, } } } } func GetAvailableLogFiles(logType string) ([]string, error) { var availableLogs []string path := config.Cfg.LogFolder + logType files, err := os.ReadDir(path) if err != nil { fmt.Println(err) return []string{}, err } // sort the files by date sort.Slice(files, func(i, j int) bool { dateFormat := "2-1-2006" dateA, _ := time.Parse(dateFormat, strings.Split(files[i].Name(), ".")[0]) dateB, _ := time.Parse(dateFormat, strings.Split(files[j].Name(), ".")[0]) return dateA.After(dateB) }) for _, file := range files { availableLogs = append(availableLogs, strings.Split(file.Name(), ".")[0]) } return availableLogs, nil } func GetLogByDate(logType string, date string) ([]string, error) { var logs []string path := config.Cfg.LogFolder + logType + "/" + date + ".log" // get the mutex for this file mutex := getFileMutex(path) // lock the mutex to ensure the file is only opened once at a time mutex.Lock() defer mutex.Unlock() file, err := os.Open(path) if err != nil { fmt.Println(err) return []string{}, err } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { logs = append(logs, scanner.Text()) } if len(logs) == 0 { return []string{}, nil } return logs, nil } func GetAvailableLogTypes() []string { var availableLogTypes []string files, err := os.ReadDir(config.Cfg.LogFolder) if err != nil { fmt.Println(err) return []string{} } for _, file := range files { availableLogTypes = append(availableLogTypes, file.Name()) } if len(availableLogTypes) == 0 { return []string{} } return availableLogTypes } func StartBackgroundLogDeleter() { ticker := time.NewTicker(24 * time.Hour) for range ticker.C { CheckForDeletableLogs() } } func CheckForDeletableLogs() { daysToKeepLogs := config.Cfg.DaysToKeepLogs logFolder := config.Cfg.LogFolder err := filepath.Walk(logFolder, func(path string, info os.FileInfo, err error) error { if err != nil { fmt.Println(err) return err } if !info.IsDir() { // check if the file is older than the daysToKeepLogs // get the date from the file name date := strings.Split(info.Name(), ".")[0] dateFormat := "2-1-2006" fileDate, err := time.Parse(dateFormat, date) if err != nil { fmt.Println(err) return err } // get the date from the daysToKeepLogs daysToKeepLogsDate := time.Now().AddDate(0, 0, -daysToKeepLogs) // compare the dates if fileDate.Before(daysToKeepLogsDate) { // delete the file err := os.Remove(path) if err != nil { fmt.Println(err) return err } AddLog(structs.LogBody{ Type: "system", Logs: []string{"I " + utils.GetTime() + "LogManager: Deleted log file " + path + " because it was older than " + strconv.Itoa(daysToKeepLogs) + " days."}, }) } } else { // Check if the directory is empty. isEmpty, err := isDirEmpty(path) if err != nil { fmt.Printf("Error checking %s: %v\n", path, err) return err } if isEmpty && path != logFolder { fmt.Printf("Deleting empty directory: %s\n", path) if err := os.Remove(path); err != nil { fmt.Printf("Error deleting %s: %v\n", path, err) } AddLog(structs.LogBody{ Type: "system", Logs: []string{"I " + utils.GetTime() + "LogManager: Deleted empty log folder " + path}, }) } } return nil }) if err != nil { fmt.Println(err) } } func isDirEmpty(dirPath string) (bool, error) { dir, err := os.Open(dirPath) if err != nil { return false, err } defer dir.Close() _, err = dir.Readdirnames(1) // Try to read an entry from the directory. if err == nil { // There is at least one entry in the directory; it's not empty. return false, nil } if err == io.EOF { // The directory is empty. return true, nil } return false, err }