77 "encoding/json"
88 "fmt"
99 "net/http"
10+ "strconv"
1011 "strings"
1112 "time"
1213
@@ -20,14 +21,19 @@ import (
2021// This endpoint reports usage information for a specific project's committed resources,
2122// including per-AZ usage, physical usage, and detailed VM subresources.
2223func (api * HTTPAPI ) HandleReportUsage (w http.ResponseWriter , r * http.Request ) {
24+ startTime := time .Now ()
25+ statusCode := http .StatusOK
26+
2327 requestID := r .Header .Get ("X-Request-ID" )
2428 if requestID == "" {
2529 requestID = fmt .Sprintf ("req-%d" , time .Now ().UnixNano ())
2630 }
2731 log := baseLog .WithValues ("requestID" , requestID , "endpoint" , "report-usage" )
2832
2933 if r .Method != http .MethodPost {
30- http .Error (w , "Method not allowed" , http .StatusMethodNotAllowed )
34+ statusCode = http .StatusMethodNotAllowed
35+ http .Error (w , "Method not allowed" , statusCode )
36+ api .recordUsageMetrics (statusCode , startTime )
3137 return
3238 }
3339
@@ -36,15 +42,19 @@ func (api *HTTPAPI) HandleReportUsage(w http.ResponseWriter, r *http.Request) {
3642 projectID , err := extractProjectIDFromPath (r .URL .Path )
3743 if err != nil {
3844 log .Error (err , "failed to extract project ID from path" )
39- http .Error (w , "Invalid URL path: " + err .Error (), http .StatusBadRequest )
45+ statusCode = http .StatusBadRequest
46+ http .Error (w , "Invalid URL path: " + err .Error (), statusCode )
47+ api .recordUsageMetrics (statusCode , startTime )
4048 return
4149 }
4250
4351 // Parse request body
4452 var req liquid.ServiceUsageRequest
4553 if err := json .NewDecoder (r .Body ).Decode (& req ); err != nil {
4654 log .Error (err , "failed to decode request body" )
47- http .Error (w , "Invalid request body: " + err .Error (), http .StatusBadRequest )
55+ statusCode = http .StatusBadRequest
56+ http .Error (w , "Invalid request body: " + err .Error (), statusCode )
57+ api .recordUsageMetrics (statusCode , startTime )
4858 return
4959 }
5060
@@ -53,15 +63,26 @@ func (api *HTTPAPI) HandleReportUsage(w http.ResponseWriter, r *http.Request) {
5363 report , err := calculator .CalculateUsage (r .Context (), log , projectID , req .AllAZs )
5464 if err != nil {
5565 log .Error (err , "failed to calculate usage report" , "projectID" , projectID )
56- http .Error (w , "Failed to generate usage report: " + err .Error (), http .StatusInternalServerError )
66+ statusCode = http .StatusInternalServerError
67+ http .Error (w , "Failed to generate usage report: " + err .Error (), statusCode )
68+ api .recordUsageMetrics (statusCode , startTime )
5769 return
5870 }
5971
6072 w .Header ().Set ("Content-Type" , "application/json" )
61- w .WriteHeader (http . StatusOK )
73+ w .WriteHeader (statusCode )
6274 if err := json .NewEncoder (w ).Encode (report ); err != nil {
6375 log .Error (err , "failed to encode usage report" )
6476 }
77+ api .recordUsageMetrics (statusCode , startTime )
78+ }
79+
80+ // recordUsageMetrics records Prometheus metrics for a report-usage request.
81+ func (api * HTTPAPI ) recordUsageMetrics (statusCode int , startTime time.Time ) {
82+ duration := time .Since (startTime ).Seconds ()
83+ statusCodeStr := strconv .Itoa (statusCode )
84+ api .usageMonitor .requestCounter .WithLabelValues (statusCodeStr ).Inc ()
85+ api .usageMonitor .requestDuration .WithLabelValues (statusCodeStr ).Observe (duration )
6586}
6687
6788// extractProjectIDFromPath extracts the project UUID from the URL path.
0 commit comments