add: SQLite-backed structured logging with retention
Replace JSON/file logging with a logs.db (WAL) store. Thread the logger through the encoder and metadata client for debug instrumentation of every ffmpeg/ffprobe/opusenc invocation and OMDb/TVmaze request. API keys are redacted before request URLs are logged. Retention defaults to 7 days, overridable via log_retention_days in config.
This commit is contained in:
@@ -6,11 +6,13 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"videnc-vibe/internal/logger"
|
||||
"videnc-vibe/pkg/types"
|
||||
)
|
||||
|
||||
@@ -51,15 +53,49 @@ type TVMazeEpisode struct {
|
||||
type Client struct {
|
||||
omdbAPIKey string
|
||||
httpClient *http.Client
|
||||
log *logger.Logger
|
||||
}
|
||||
|
||||
func NewClient(apiKey string) *Client {
|
||||
func NewClient(apiKey string, log *logger.Logger) *Client {
|
||||
return &Client{
|
||||
omdbAPIKey: apiKey,
|
||||
httpClient: &http.Client{Timeout: 10 * time.Second},
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
// redactURL strips secret query parameters (e.g. apikey) before logging.
|
||||
func redactURL(rawURL string) string {
|
||||
u, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return rawURL
|
||||
}
|
||||
q := u.Query()
|
||||
for _, k := range []string{"apikey", "api_key"} {
|
||||
if q.Has(k) {
|
||||
q.Set(k, "REDACTED")
|
||||
}
|
||||
}
|
||||
u.RawQuery = q.Encode()
|
||||
return u.String()
|
||||
}
|
||||
|
||||
func (c *Client) logHTTP(label, rawURL string, status int, body []byte) {
|
||||
if c.log == nil {
|
||||
return
|
||||
}
|
||||
extra, _ := json.Marshal(struct {
|
||||
URL string `json:"url"`
|
||||
Status int `json:"status"`
|
||||
Body string `json:"body"`
|
||||
}{
|
||||
URL: redactURL(rawURL),
|
||||
Status: status,
|
||||
Body: string(body),
|
||||
})
|
||||
c.log.Debug(label, "", string(extra))
|
||||
}
|
||||
|
||||
func ParseFilename(filename string) (isSeries bool, imdbID, tvmazeID, season, episode string) {
|
||||
filename = strings.TrimSuffix(filename, ".mkv")
|
||||
|
||||
@@ -111,9 +147,9 @@ func ParseMediaType(filename string) types.MediaType {
|
||||
}
|
||||
|
||||
func (c *Client) FetchMovieMetadata(ctx context.Context, imdbID string) (*types.Metadata, error) {
|
||||
url := fmt.Sprintf("%s?i=%s&apikey=%s", omdbAPIURL, imdbID, c.omdbAPIKey)
|
||||
reqURL := fmt.Sprintf("%s?i=%s&apikey=%s", omdbAPIURL, imdbID, c.omdbAPIKey)
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqURL, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("building OMDb request: %w", err)
|
||||
}
|
||||
@@ -128,6 +164,8 @@ func (c *Client) FetchMovieMetadata(ctx context.Context, imdbID string) (*types.
|
||||
return nil, fmt.Errorf("reading OMDb body: %w", err)
|
||||
}
|
||||
|
||||
c.logHTTP("OMDb GET", reqURL, resp.StatusCode, body)
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("OMDb HTTP %d: %s", resp.StatusCode, snippet(body))
|
||||
}
|
||||
@@ -197,8 +235,8 @@ func (c *Client) FetchSeriesMetadata(ctx context.Context, tvmazeID, season, epis
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) fetchTVMazeJSON(ctx context.Context, url string, v interface{}) error {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
func (c *Client) fetchTVMazeJSON(ctx context.Context, reqURL string, v interface{}) error {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, reqURL, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("build request: %w", err)
|
||||
}
|
||||
@@ -213,6 +251,8 @@ func (c *Client) fetchTVMazeJSON(ctx context.Context, url string, v interface{})
|
||||
return fmt.Errorf("read body: %w", err)
|
||||
}
|
||||
|
||||
c.logHTTP("TVmaze GET", reqURL, resp.StatusCode, body)
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("HTTP %d: %s", resp.StatusCode, snippet(body))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user