Логирование: ревью всего кода и рефакторинг в соответствии с конвенциями
This commit is contained in:
+45
-15
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user