Files
trufflehog/pkg/common/http_metrics.go
Amaan Ullah 9adec3cc36
Lint / golangci-lint (push) Waiting to run
Lint / semgrep (push) Waiting to run
Release / Release (push) Waiting to run
Scan for secrets / test (push) Waiting to run
Test / test (push) Waiting to run
Test / test-community (push) Waiting to run
Add metrics to SaneHTTPClient (#4471)
* Add metrics to SaneHTTPClient
1. Increment counter for URL
2. Time the latency
3. Increment counter for non-200 calls
Added Tests

* resolve lint issues
2025-09-18 17:25:05 +05:00

109 lines
2.8 KiB
Go

package common
import (
"net/url"
"strconv"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
var (
httpRequestsTotal = promauto.NewCounterVec(
prometheus.CounterOpts{
Namespace: MetricsNamespace,
Subsystem: "http_client",
Name: "requests_total",
Help: "Total number of HTTP requests made, labeled by URL.",
},
[]string{"url"},
)
httpRequestDuration = promauto.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: MetricsNamespace,
Subsystem: "http_client",
Name: "request_duration_seconds",
Help: "HTTP request latency in seconds, labeled by URL.",
Buckets: prometheus.DefBuckets,
},
[]string{"url"},
)
httpNon200ResponsesTotal = promauto.NewCounterVec(
prometheus.CounterOpts{
Namespace: MetricsNamespace,
Subsystem: "http_client",
Name: "non_200_responses_total",
Help: "Total number of non-200 HTTP responses, labeled by URL and status code.",
},
[]string{"url", "status_code"},
)
)
// sanitizeURL sanitizes a URL to avoid high cardinality metrics.
// It keeps only the host and path, removing query parameters, fragments, and user info.
func sanitizeURL(rawURL string) string {
if rawURL == "" {
return "unknown"
}
parsedURL, err := url.Parse(rawURL)
if err != nil {
return "invalid_url"
}
// Build sanitized URL with just scheme, host, and path
sanitized := &url.URL{
Scheme: parsedURL.Scheme,
Host: parsedURL.Host,
Path: parsedURL.Path,
}
// If host is empty, try to extract from the raw URL
if sanitized.Host == "" {
// For relative URLs or malformed URLs, just use a placeholder
return "relative_or_invalid"
}
// Normalize path
if sanitized.Path == "" {
sanitized.Path = "/"
}
// Limit path length to avoid extremely long paths creating high cardinality
if len(sanitized.Path) > 100 {
sanitized.Path = sanitized.Path[:100] + "..."
}
result := sanitized.String()
// Final fallback to avoid empty strings
if result == "" {
return "unknown"
}
return result
}
// recordHTTPRequest records metrics for an HTTP request.
func recordHTTPRequest(sanitizedURL string) {
httpRequestsTotal.WithLabelValues(sanitizedURL).Inc()
}
// recordHTTPResponse records metrics for an HTTP response.
func recordHTTPResponse(sanitizedURL string, statusCode int, durationSeconds float64) {
// Record latency
httpRequestDuration.WithLabelValues(sanitizedURL).Observe(durationSeconds)
// Record non-200 responses
if statusCode != 200 {
httpNon200ResponsesTotal.WithLabelValues(sanitizedURL, strconv.Itoa(statusCode)).Inc()
}
}
// recordNetworkError records metrics for failed HTTP response
func recordNetworkError(sanitizedURL string) {
httpNon200ResponsesTotal.WithLabelValues(sanitizedURL, "network_error").Inc()
}