package aplicare import ( "encoding/json" "fmt" "os" "time" ) type SyncLog struct { Timestamp string `json:"timestamp"` KodeRuang string `json:"kode_ruang,omitempty"` NamaRuang string `json:"nama_ruang,omitempty"` KodeKelas string `json:"kode_kelas,omitempty"` Kapasitas int `json:"kapasitas,omitempty"` Tersedia int `json:"tersedia,omitempty"` Action string `json:"action"` Status string `json:"status"` Error string `json:"error,omitempty"` ResponseMs int64 `json:"response_ms,omitempty"` } var logPath = "./logs/sync.log" func init() { _ = os.MkdirAll("./logs", 0755) } // WriteLog menulis 1 entry log ke sync.log func WriteLog(entry SyncLog) { entry.Timestamp = time.Now().Format(time.RFC3339) writeToFile(entry) } // WriteBatchLog menulis ringkasan 1 run sync func WriteBatchLog(result *SyncResult) { if result == nil { return } status := "sukses" if len(result.Errors) > 0 { status = "partial" } if result.Posted == 0 && result.Changed > 0 { status = "gagal" } summary := map[string]interface{}{ "timestamp": time.Now().Format(time.RFC3339), "action": "batch_sync", "total_rooms": result.TotalRooms, "changed": result.Changed, "posted": result.Posted, "dry_run": result.DryRun, "status": status, "errors": result.Errors, } f, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { return } defer f.Close() line, _ := json.Marshal(summary) _, _ = f.Write(append(line, '\n')) // Rotasi log — jaga ukuran file max 5MB rotateLogs() } func writeToFile(entry SyncLog) { f, err := os.OpenFile(logPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { fmt.Printf("gagal buka log file: %v\n", err) return } defer f.Close() line, _ := json.Marshal(entry) _, _ = f.Write(append(line, '\n')) } // rotateLogs — kalau file > 5MB, rename jadi sync.log.old func rotateLogs() { info, err := os.Stat(logPath) if err != nil { return } // 5MB if info.Size() > 5*1024*1024 { _ = os.Rename(logPath, logPath+".old") } }