diff --git a/logger.go b/logger.go new file mode 100644 index 0000000..f8b2d98 --- /dev/null +++ b/logger.go @@ -0,0 +1,36 @@ +package reforger_utils + +import ( + "log/slog" + "os" + "sync" +) + +var ( + loggerMu sync.RWMutex + logger = newDefaultLogger() +) + +func newDefaultLogger() *slog.Logger { + return slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo})) +} + +// SetLogger configures the package logger. +// Passing nil resets it to the default JSON logger writing to stdout. +func SetLogger(l *slog.Logger) { + loggerMu.Lock() + defer loggerMu.Unlock() + + if l == nil { + logger = newDefaultLogger() + return + } + + logger = l +} + +func getLogger() *slog.Logger { + loggerMu.RLock() + defer loggerMu.RUnlock() + return logger +} diff --git a/util.go b/util.go index ddae90f..ca88dba 100644 --- a/util.go +++ b/util.go @@ -54,9 +54,8 @@ func DoAssetsRequest(addonID, version string) (AssetsReply, error) { defer res.Body.Close() if res.StatusCode != 200 { - // print entire body body, _ := io.ReadAll(res.Body) - fmt.Println(string(body)) + getLogger().Error("assets request failed", "status", res.Status, "addon_id", addonID, "version", version, "body", string(body)) return AssetsReply{}, fmt.Errorf("bad status: %s", res.Status) } @@ -64,7 +63,7 @@ func DoAssetsRequest(addonID, version string) (AssetsReply, error) { decoder := json.NewDecoder(res.Body) var assetsReply AssetsReply if err := decoder.Decode(&assetsReply); err != nil { - panic(err) + return AssetsReply{}, fmt.Errorf("failed to decode assets response: %w", err) } return assetsReply, nil @@ -94,7 +93,7 @@ func GetManifest(manifestUrl string) (Manifest, error) { // Decode the JSON response var manifest Manifest if err := json.NewDecoder(res.Body).Decode(&manifest); err != nil { - panic(err) + return Manifest{}, fmt.Errorf("failed to decode manifest response: %w", err) } return manifest, nil @@ -135,9 +134,12 @@ func Download(manifest Manifest, downloadDir string, filen string) ([]byte, erro // start downloading chunks ret := make([]byte, manifest.Size) totalMB := float32(manifest.Size) / 1024.0 / 1024.0 + lastLoggedPercent := float32(-1) + log := getLogger().With("file", filen) + log.Info("download started", "size_bytes", manifest.Size) // Download all fragments serially to keep execution simple and deterministic. - for _, fragment := range manifest.Fragments { + for idx, fragment := range manifest.Fragments { url := transformShaToURL(fragment.Sha512, fragment.Size) content, err := getContent(url) if err != nil { @@ -150,7 +152,10 @@ func Download(manifest Manifest, downloadDir string, filen string) ([]byte, erro } percent := float32(downloaded) / float32(manifest.Size) * 100.0 downedMB := float32(downloaded) / 1024.0 / 1024.0 - fmt.Printf("\r\033[32mDownloaded\033[0m %fMB/%fMB (\033[36m%.2f%%\033[0m) of \033[33m%s\033[0m", downedMB, totalMB, percent, filen) + if percent-lastLoggedPercent >= 5 || idx == len(manifest.Fragments)-1 { + log.Info("download progress", "downloaded_mb", downedMB, "total_mb", totalMB, "percent", percent) + lastLoggedPercent = percent + } } // sort trackFrag by Offset @@ -166,7 +171,7 @@ func Download(manifest Manifest, downloadDir string, filen string) ([]byte, erro // gap detected gapSize := frag.Offset - curPos if currentRem+gapSize > len(remContent) { - panic("not enough remainder content to fill gap") + return nil, fmt.Errorf("not enough remainder content to fill gap in %s", filen) } copy(ret[curPos:curPos+gapSize], remContent[currentRem:currentRem+gapSize]) remContent = remContent[gapSize:] @@ -176,7 +181,7 @@ func Download(manifest Manifest, downloadDir string, filen string) ([]byte, erro // Append rest of remainder content copy(ret[curPos:], remContent) - fmt.Println() + log.Info("download completed", "size_bytes", len(ret)) return ret, nil } @@ -193,6 +198,7 @@ func getContent(url string) (*[]byte, error) { defer resp.Body.Close() if resp.StatusCode != 200 { + getLogger().Error("content request failed", "status", resp.Status, "url", url) return nil, fmt.Errorf("bad status: %s", resp.Status) }