Логирование: ревью всего кода и рефакторинг в соответствии с конвенциями

This commit is contained in:
av
2026-06-28 20:13:40 +03:00
parent c739a20749
commit 9cfccc7b4a
24 changed files with 473 additions and 203 deletions
+45 -15
View File
@@ -21,6 +21,9 @@ import (
"strings"
"sync"
"time"
"git.vakhrushev.me/av/jellybit/internal/logctx"
"git.vakhrushev.me/av/jellybit/internal/logging"
)
// Config — параметры подключения к qBittorrent WebUI.
@@ -119,19 +122,25 @@ func (c *Client) login(ctx context.Context) error {
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Referer", c.base.String()) // qBit проверяет Referer/Host
log := logctx.FromOr(ctx, c.log)
call := logging.ExtCall{Service: logging.ServiceQBittorrent, Operation: "auth/login", Start: time.Now()}
resp, err := c.hc.Do(req)
if err != nil {
call.Failure(log, err)
return fmt.Errorf("qbittorrent login: %w", err)
}
defer func() { _ = resp.Body.Close() }()
call.Status = resp.StatusCode
body, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<10))
if resp.StatusCode != http.StatusOK || strings.TrimSpace(string(body)) != "Ok." {
c.log.Error("qbittorrent: login failed",
"status", resp.StatusCode, "body", strings.TrimSpace(string(body)))
return fmt.Errorf("qbittorrent login failed: status %d body %q",
// Тело логина не содержит секретов (qBit отвечает "Ok."/"Fails."), но
// учётные данные (логин/пароль) в лог не идут — только факт неуспеха.
err := fmt.Errorf("qbittorrent login failed: status %d body %q",
resp.StatusCode, strings.TrimSpace(string(body)))
call.Failure(log, err)
return err
}
c.log.Debug("qbittorrent: login ok", "user", c.user)
call.Success(log, "authenticated", true)
return nil
}
@@ -148,7 +157,7 @@ func (c *Client) do(ctx context.Context, build func() (*http.Request, error)) (*
}
if resp.StatusCode == http.StatusForbidden {
_ = resp.Body.Close()
c.log.Debug("qbittorrent: session expired (403), re-login")
logctx.FromOr(ctx, c.log).Debug("qbittorrent session expired, re-login")
if err := c.login(ctx); err != nil {
return nil, err
}
@@ -193,6 +202,8 @@ func (c *Client) Add(ctx context.Context, ar AddRequest) error {
contentType := mw.FormDataContentType()
payload := buf.Bytes()
log := logctx.FromOr(ctx, c.log)
call := logging.ExtCall{Service: logging.ServiceQBittorrent, Operation: "torrents/add", Start: time.Now()}
resp, err := c.do(ctx, func() (*http.Request, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodPost,
c.endpoint("/api/v2/torrents/add"), bytes.NewReader(payload))
@@ -204,27 +215,33 @@ func (c *Client) Add(ctx context.Context, ar AddRequest) error {
return req, nil
})
if err != nil {
call.Failure(log, err, "category", ar.Category)
return fmt.Errorf("qbittorrent add: %w", err)
}
defer func() { _ = resp.Body.Close() }()
call.Status = resp.StatusCode
body, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<10))
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("qbittorrent add: status %d body %q",
err := fmt.Errorf("qbittorrent add: status %d body %q",
resp.StatusCode, strings.TrimSpace(string(body)))
call.Failure(log, err, "category", ar.Category)
return err
}
if strings.TrimSpace(string(body)) == "Fails." {
c.log.Error("qbittorrent: add rejected",
"category", ar.Category, "urls", len(ar.URLs), "torrents", len(ar.Torrents))
return fmt.Errorf("qbittorrent add: rejected (Fails.)")
err := fmt.Errorf("qbittorrent add: rejected (Fails.)")
call.Failure(log, err, "category", ar.Category,
"urls", len(ar.URLs), "torrents", len(ar.Torrents))
return err
}
c.log.Info("qbittorrent: torrent added",
"category", ar.Category, "save_path", ar.SavePath,
call.Success(log, "category", ar.Category, "save_path", ar.SavePath,
"urls", len(ar.URLs), "torrents", len(ar.Torrents), "paused", ar.Paused)
return nil
}
// Torrents возвращает задачи указанной категории (пустая — все).
func (c *Client) Torrents(ctx context.Context, category string) ([]Torrent, error) {
log := logctx.FromOr(ctx, c.log)
call := logging.ExtCall{Service: logging.ServiceQBittorrent, Operation: "torrents/info", Start: time.Now()}
resp, err := c.do(ctx, func() (*http.Request, error) {
u := c.endpoint("/api/v2/torrents/info")
if category != "" {
@@ -233,19 +250,25 @@ func (c *Client) Torrents(ctx context.Context, category string) ([]Torrent, erro
return http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
})
if err != nil {
call.Failure(log, err)
return nil, fmt.Errorf("qbittorrent info: %w", err)
}
defer func() { _ = resp.Body.Close() }()
call.Status = resp.StatusCode
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<10))
return nil, fmt.Errorf("qbittorrent info: status %d body %q",
err := fmt.Errorf("qbittorrent info: status %d body %q",
resp.StatusCode, strings.TrimSpace(string(body)))
call.Failure(log, err)
return nil, err
}
var ts []Torrent
if err := json.NewDecoder(resp.Body).Decode(&ts); err != nil {
call.Failure(log, err)
return nil, fmt.Errorf("decode qbittorrent info: %w", err)
}
c.log.Debug("qbittorrent: torrents fetched", "category", category, "count", len(ts))
// Поллинг частый — на DEBUG, чтобы не зашумлять INFO (как healthcheck).
call.SuccessDebug(log, "category", category, "count", len(ts))
return ts, nil
}
@@ -253,23 +276,30 @@ func (c *Client) Torrents(ctx context.Context, category string) ([]Torrent, erro
// включая корневую папку для многофайловых раздач, и размеры). Нужен
// распознаванию как один из сигналов; абсолютный путь — join(save_path, Name).
func (c *Client) Files(ctx context.Context, hash string) ([]File, error) {
log := logctx.FromOr(ctx, c.log)
call := logging.ExtCall{Service: logging.ServiceQBittorrent, Operation: "torrents/files", Start: time.Now()}
resp, err := c.do(ctx, func() (*http.Request, error) {
u := c.endpoint("/api/v2/torrents/files?hash=" + url.QueryEscape(hash))
return http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
})
if err != nil {
call.Failure(log, err)
return nil, fmt.Errorf("qbittorrent files: %w", err)
}
defer func() { _ = resp.Body.Close() }()
call.Status = resp.StatusCode
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<10))
return nil, fmt.Errorf("qbittorrent files: status %d body %q",
err := fmt.Errorf("qbittorrent files: status %d body %q",
resp.StatusCode, strings.TrimSpace(string(body)))
call.Failure(log, err)
return nil, err
}
var fs []File
if err := json.NewDecoder(resp.Body).Decode(&fs); err != nil {
call.Failure(log, err)
return nil, fmt.Errorf("decode qbittorrent files: %w", err)
}
c.log.Debug("qbittorrent: files fetched", "hash", hash, "count", len(fs))
call.SuccessDebug(log, "count", len(fs))
return fs, nil
}