diff --git a/internal/imgcache/cache.go b/internal/imgcache/cache.go index 59f73f3..1302c71 100644 --- a/internal/imgcache/cache.go +++ b/internal/imgcache/cache.go @@ -238,10 +238,13 @@ func (c *Cache) StoreSource( Path: req.SourcePath, Query: req.SourceQuery, ContentHash: contentHash, - StatusCode: httpStatusOK, + StatusCode: result.StatusCode, ContentType: result.ContentType, + ContentLength: result.ContentLength, ResponseHeaders: result.Headers, FetchedAt: time.Now().Unix(), + FetchDurationMs: result.FetchDurationMs, + RemoteAddr: result.RemoteAddr, } if err := c.srcMetadata.Store(req.SourceHost, pathHash, meta); err != nil { diff --git a/internal/imgcache/fetcher.go b/internal/imgcache/fetcher.go index 3ab6acc..c3b78f4 100644 --- a/internal/imgcache/fetcher.go +++ b/internal/imgcache/fetcher.go @@ -164,7 +164,12 @@ func (f *HTTPFetcher) Fetch(ctx context.Context, url string) (*FetchResult, erro req.Header.Set("User-Agent", f.config.UserAgent) req.Header.Set("Accept", strings.Join(f.config.AllowedContentTypes, ", ")) + startTime := time.Now() + resp, err := f.client.Do(req) + + fetchDuration := time.Since(startTime) + if err != nil { if errors.Is(err, context.DeadlineExceeded) { return nil, ErrUpstreamTimeout @@ -173,6 +178,12 @@ func (f *HTTPFetcher) Fetch(ctx context.Context, url string) (*FetchResult, erro return nil, fmt.Errorf("upstream request failed: %w", err) } + // Get remote address if available + var remoteAddr string + if resp.Request != nil && resp.Request.URL != nil { + remoteAddr = resp.Request.Host + } + // Check status code if resp.StatusCode < 200 || resp.StatusCode >= 300 { _ = resp.Body.Close() @@ -198,10 +209,13 @@ func (f *HTTPFetcher) Fetch(ctx context.Context, url string) (*FetchResult, erro success = true return &FetchResult{ - Content: &semaphoreReleasingReadCloser{limitedBody, resp.Body, sem}, - ContentLength: resp.ContentLength, - ContentType: contentType, - Headers: resp.Header, + Content: &semaphoreReleasingReadCloser{limitedBody, resp.Body, sem}, + ContentLength: resp.ContentLength, + ContentType: contentType, + Headers: resp.Header, + StatusCode: resp.StatusCode, + FetchDurationMs: fetchDuration.Milliseconds(), + RemoteAddr: remoteAddr, }, nil } diff --git a/internal/imgcache/imgcache.go b/internal/imgcache/imgcache.go index 62d533d..c5abdc9 100644 --- a/internal/imgcache/imgcache.go +++ b/internal/imgcache/imgcache.go @@ -178,6 +178,12 @@ type FetchResult struct { ContentType string // Headers contains all response headers from upstream Headers map[string][]string + // StatusCode is the HTTP status code from upstream + StatusCode int + // FetchDurationMs is how long the fetch took in milliseconds + FetchDurationMs int64 + // RemoteAddr is the IP:port of the upstream server + RemoteAddr string } // Processor handles image transformation (resize, format conversion) diff --git a/internal/imgcache/service.go b/internal/imgcache/service.go index 3328610..1e43efa 100644 --- a/internal/imgcache/service.go +++ b/internal/imgcache/service.go @@ -157,18 +157,23 @@ func (s *Service) fetchAndProcess(ctx context.Context, req *ImageRequest) (*Imag } // Process the image + processStart := time.Now() + processResult, err := s.processor.Process(ctx, bytes.NewReader(sourceData), req) if err != nil { return nil, fmt.Errorf("image processing failed: %w", err) } + processDuration := time.Since(processStart) + // Log conversion details inputSize := int64(len(sourceData)) outputSize := processResult.ContentLength sizePercent := float64(outputSize) / float64(inputSize) * 100.0 //nolint:mnd // percentage calculation s.log.Info("image converted", - "file", req.SourcePath, + "host", req.SourceHost, + "path", req.SourcePath, "input_format", processResult.InputFormat, "output_format", req.Format, "input_bytes", inputSize, @@ -176,6 +181,9 @@ func (s *Service) fetchAndProcess(ctx context.Context, req *ImageRequest) (*Imag "input_dimensions", fmt.Sprintf("%dx%d", processResult.InputWidth, processResult.InputHeight), "output_dimensions", fmt.Sprintf("%dx%d", processResult.Width, processResult.Height), "size_ratio", fmt.Sprintf("%.1f%%", sizePercent), + "convert_ms", processDuration.Milliseconds(), + "quality", req.Quality, + "fit", req.FitMode, ) // Store output content to cache diff --git a/internal/imgcache/storage.go b/internal/imgcache/storage.go index f057977..d456f22 100644 --- a/internal/imgcache/storage.go +++ b/internal/imgcache/storage.go @@ -194,11 +194,14 @@ type SourceMetadata struct { ContentHash string `json:"content_hash,omitempty"` StatusCode int `json:"status_code"` ContentType string `json:"content_type,omitempty"` + ContentLength int64 `json:"content_length,omitempty"` ResponseHeaders map[string][]string `json:"response_headers,omitempty"` FetchedAt int64 `json:"fetched_at"` + FetchDurationMs int64 `json:"fetch_duration_ms,omitempty"` ExpiresAt int64 `json:"expires_at,omitempty"` ETag string `json:"etag,omitempty"` LastModified string `json:"last_modified,omitempty"` + RemoteAddr string `json:"remote_addr,omitempty"` } // Store writes metadata to storage.