@@ -4,11 +4,16 @@ import (
4
4
"log"
5
5
"net/http"
6
6
"strings"
7
+ "fmt"
8
+ "time"
9
+ "database/sql"
7
10
"github.com/karmada-io/dashboard/cmd/metrics-scraper/app/scrape"
8
-
9
11
"github.com/gin-gonic/gin"
10
12
)
11
-
13
+ type MetricInfo struct {
14
+ Help string `json:"help"`
15
+ Type string `json:"type"`
16
+ }
12
17
13
18
func QueryMetrics (c * gin.Context ) {
14
19
appName := c .Param ("app_name" )
@@ -25,38 +30,195 @@ func QueryMetrics(c *gin.Context) {
25
30
c .JSON (http .StatusInternalServerError , gin.H {"error" : "Failed to open database" })
26
31
return
27
32
}
28
- defer db .Close ()
33
+
34
+ // Add transaction for consistent reads
35
+ tx , err := db .Begin ()
36
+ if err != nil {
37
+ log .Printf ("Error starting transaction: %v" , err )
38
+ c .JSON (http .StatusInternalServerError , gin.H {"error" : "Failed to start transaction" })
39
+ return
40
+ }
41
+ defer tx .Rollback ()
29
42
30
43
switch queryType {
31
44
case "mname" :
32
- metricNames , err := scrape . Mname ( db , sanitizedPodName )
45
+ rows , err := tx . Query ( fmt . Sprintf ( "SELECT DISTINCT name FROM %s" , sanitizedPodName ) )
33
46
if err != nil {
34
- log .Printf ("Error retrieving metric names: %v" , err )
35
- c .JSON (http .StatusInternalServerError , gin.H {"error" : "Failed to retrieve metric names" })
47
+ log .Printf ("Error querying metric names: %v, SQL Error: %v" , err , err )
48
+ c .JSON (http .StatusInternalServerError , gin.H {"error" : "Failed to query metric names" })
36
49
return
37
50
}
38
- c .JSON (http .StatusOK , gin.H {"metricNames" : metricNames })
51
+ defer rows .Close ()
52
+
53
+ var metricNames []string
54
+ for rows .Next () {
55
+ var metricName string
56
+ if err := rows .Scan (& metricName ); err != nil {
57
+ log .Printf ("Error scanning metric name: %v" , err )
58
+ c .JSON (http .StatusInternalServerError , gin.H {"error" : "Failed to scan metric name" })
59
+ return
60
+ }
61
+ metricNames = append (metricNames , metricName )
62
+ }
63
+
64
+ c .JSON (http .StatusOK , gin.H { "metricNames" : metricNames })
39
65
40
66
case "details" :
41
- if metricName == "" {
42
- c .JSON (http .StatusBadRequest , gin.H {"error" : "Metric name required for details" })
67
+ if metricName == "" {
68
+ c .JSON (http .StatusBadRequest , gin.H {"error" : "Metric name required for details" })
69
+ return
70
+ }
71
+ query := fmt .Sprintf (`
72
+ SELECT
73
+ m.currentTime,
74
+ m.name,
75
+ v.value,
76
+ v.measure,
77
+ v.id
78
+ FROM %s m
79
+ INNER JOIN %s_values v ON m.id = v.metric_id
80
+ WHERE m.name = ?
81
+ ` , sanitizedPodName , sanitizedPodName )
82
+ rows , err := tx .Query (query , metricName )
83
+ if err != nil {
84
+ log .Printf ("Error querying metric details: %v" , err )
85
+ c .JSON (http .StatusInternalServerError , gin.H {"error" : "Failed to query metric details" })
43
86
return
44
87
}
45
- details , err := scrape .Details (db , sanitizedPodName , metricName )
88
+ defer rows .Close ()
89
+
90
+ type MetricValue struct {
91
+ Value string `json:"value"`
92
+ Measure string `json:"measure"`
93
+ Labels map [string ]string `json:"labels"`
94
+ }
95
+
96
+ type MetricDetails struct {
97
+ Name string `json:"name"`
98
+ Values []MetricValue `json:"values"`
99
+ }
100
+
101
+ detailsMap := make (map [string ]MetricDetails )
102
+
103
+ for rows .Next () {
104
+ var currentTime time.Time
105
+ var name , value , measure string
106
+ var valueID int
107
+ if err := rows .Scan (& currentTime , & name , & value , & measure , & valueID ); err != nil {
108
+ log .Printf ("Error scanning metric details: %v" , err )
109
+ c .JSON (http .StatusInternalServerError , gin.H {"error" : "Failed to scan metric details" })
110
+ return
111
+ }
112
+
113
+ labelsQuery := fmt .Sprintf ("SELECT key, value FROM %s_labels WHERE value_id = ?" , sanitizedPodName )
114
+ labelsRows , err := tx .Query (labelsQuery , valueID )
115
+ if err != nil {
116
+ log .Printf ("Error querying labels: %v" , err )
117
+ c .JSON (http .StatusInternalServerError , gin.H {"error" : "Failed to query labels" })
118
+ return
119
+ }
120
+ defer labelsRows .Close ()
121
+
122
+ labels := make (map [string ]string )
123
+ for labelsRows .Next () {
124
+ var labelKey , labelValue string
125
+ if err := labelsRows .Scan (& labelKey , & labelValue ); err != nil {
126
+ log .Printf ("Error scanning labels: %v" , err )
127
+ c .JSON (http .StatusInternalServerError , gin.H {"error" : "Failed to scan labels" })
128
+ return
129
+ }
130
+ labels [labelKey ] = labelValue
131
+ }
132
+
133
+ timeKey := currentTime .Format (time .RFC3339 )
134
+
135
+ detail , exists := detailsMap [timeKey ]
136
+ if ! exists {
137
+ detail = MetricDetails {
138
+ Name : name ,
139
+ Values : []MetricValue {},
140
+ }
141
+ }
142
+
143
+ detail .Values = append (detail .Values , MetricValue {
144
+ Value : value ,
145
+ Measure : measure ,
146
+ Labels : labels ,
147
+ })
148
+
149
+ detailsMap [timeKey ] = detail
150
+ }
151
+
152
+ c .JSON (http .StatusOK , gin.H {"details" : detailsMap })
153
+
154
+ case "metricsdetails" :
155
+ // Handle metricsdetails query type
156
+ db , err := sql .Open ("sqlite" , strings .ReplaceAll (appName , "-" , "_" )+ ".db" )
46
157
if err != nil {
47
- log .Printf ("Error retrieving metric details : %v" , err )
48
- c .JSON (http .StatusInternalServerError , gin.H {"error" : "Failed to retrieve metric details " })
158
+ log .Printf ("Error opening database : %v" , err )
159
+ c .JSON (http .StatusInternalServerError , gin.H {"error" : "Failed to open database " })
49
160
return
50
161
}
51
- c . JSON ( http . StatusOK , gin. H { "details" : details } )
162
+ defer db . Close ( )
52
163
53
- case "metricsdetails" :
54
- result , err := scrape .MetricsDetails (sanitizedAppName )
164
+ // Get all relevant tables
165
+ rows , err := db .Query (`
166
+ SELECT name
167
+ FROM sqlite_master
168
+ WHERE type='table'
169
+ AND name NOT LIKE '%_values'
170
+ AND name NOT LIKE '%_labels'
171
+ AND name NOT LIKE '%_time_load'
172
+ AND name != 'sqlite_sequence'
173
+ ` )
55
174
if err != nil {
56
- log .Printf ("Error retrieving metrics details : %v" , err )
57
- c .JSON (http .StatusInternalServerError , gin.H {"error" : "Failed to retrieve metrics details " })
175
+ log .Printf ("Error querying tables : %v" , err )
176
+ c .JSON (http .StatusInternalServerError , gin.H {"error" : "Failed to query tables " })
58
177
return
59
178
}
179
+ defer rows .Close ()
180
+
181
+ result := make (map [string ]map [string ]MetricInfo )
182
+
183
+ // Process each table (pod)
184
+ for rows .Next () {
185
+ var tableName string
186
+ if err := rows .Scan (& tableName ); err != nil {
187
+ log .Printf ("Error scanning table name: %v" , err )
188
+ continue
189
+ }
190
+
191
+ // Get metrics for this pod
192
+ metricRows , err := db .Query (fmt .Sprintf (`
193
+ SELECT DISTINCT name, help, type
194
+ FROM %s
195
+ GROUP BY name
196
+ ` , tableName ))
197
+ if err != nil {
198
+ log .Printf ("Error querying metrics for table %s: %v" , tableName , err )
199
+ continue
200
+ }
201
+ defer metricRows .Close ()
202
+
203
+ podMetrics := make (map [string ]MetricInfo )
204
+
205
+ // Process each metric
206
+ for metricRows .Next () {
207
+ var name , help , metricType string
208
+ if err := metricRows .Scan (& name , & help , & metricType ); err != nil {
209
+ log .Printf ("Error scanning metric info: %v" , err )
210
+ continue
211
+ }
212
+
213
+ podMetrics [name ] = MetricInfo {
214
+ Help : help ,
215
+ Type : metricType ,
216
+ }
217
+ }
218
+
219
+ result [tableName ] = podMetrics
220
+ }
221
+
60
222
c .JSON (http .StatusOK , result )
61
223
return
62
224
}
0 commit comments