@@ -7,18 +7,21 @@ import (
7
7
8
8
"github.com/google/uuid"
9
9
"github.com/prometheus/client_golang/prometheus"
10
+ "golang.org/x/exp/slices"
10
11
"golang.org/x/sync/errgroup"
11
12
"golang.org/x/xerrors"
12
13
13
14
"cdr.dev/slog"
14
15
15
16
"github.com/coder/coder/v2/coderd/database"
17
+ "github.com/coder/coder/v2/coderd/util/slice"
16
18
"github.com/coder/coder/v2/codersdk"
17
19
)
18
20
19
21
var (
20
22
templatesActiveUsersDesc = prometheus .NewDesc ("coderd_insights_templates_active_users" , "The number of active users of the template." , []string {"template_name" }, nil )
21
23
applicationsUsageSecondsDesc = prometheus .NewDesc ("coderd_insights_applications_usage_seconds" , "The application usage per template." , []string {"template_name" , "application_name" , "slug" }, nil )
24
+ parametersDesc = prometheus .NewDesc ("coderd_insights_parameters" , "The parameter usage per template." , []string {"template_name" , "parameter_name" , "parameter_type" , "parameter_value" }, nil )
22
25
)
23
26
24
27
type MetricsCollector struct {
@@ -33,10 +36,20 @@ type MetricsCollector struct {
33
36
type insightsData struct {
34
37
templates []database.GetTemplateInsightsByTemplateRow
35
38
apps []database.GetTemplateAppInsightsByTemplateRow
39
+ params []parameterRow
36
40
37
41
templateNames map [uuid.UUID ]string
38
42
}
39
43
44
+ type parameterRow struct {
45
+ templateID uuid.UUID
46
+ name string
47
+ aType string
48
+ value string
49
+
50
+ count int64
51
+ }
52
+
40
53
var _ prometheus.Collector = new (MetricsCollector )
41
54
42
55
func NewMetricsCollector (db database.Store , logger slog.Logger , timeWindow time.Duration , tickInterval time.Duration ) (* MetricsCollector , error ) {
@@ -75,10 +88,11 @@ func (mc *MetricsCollector) Run(ctx context.Context) (func(), error) {
75
88
// Phase 1: Fetch insights from database
76
89
// FIXME errorGroup will be used to fetch insights for apps and parameters
77
90
eg , egCtx := errgroup .WithContext (ctx )
78
- eg .SetLimit (2 )
91
+ eg .SetLimit (3 )
79
92
80
93
var templateInsights []database.GetTemplateInsightsByTemplateRow
81
94
var appInsights []database.GetTemplateAppInsightsByTemplateRow
95
+ var paramInsights []parameterRow
82
96
83
97
eg .Go (func () error {
84
98
var err error
@@ -102,13 +116,25 @@ func (mc *MetricsCollector) Run(ctx context.Context) (func(), error) {
102
116
}
103
117
return err
104
118
})
119
+ eg .Go (func () error {
120
+ var err error
121
+ rows , err := mc .database .GetTemplateParameterInsights (egCtx , database.GetTemplateParameterInsightsParams {
122
+ StartTime : startTime ,
123
+ EndTime : endTime ,
124
+ })
125
+ if err != nil {
126
+ mc .logger .Error (ctx , "unable to fetch parameter insights from database" , slog .Error (err ))
127
+ }
128
+ paramInsights = convertParameterInsights (rows )
129
+ return err
130
+ })
105
131
err := eg .Wait ()
106
132
if err != nil {
107
133
return
108
134
}
109
135
110
136
// Phase 2: Collect template IDs, and fetch relevant details
111
- templateIDs := uniqueTemplateIDs (templateInsights , appInsights )
137
+ templateIDs := uniqueTemplateIDs (templateInsights , appInsights , paramInsights )
112
138
113
139
templateNames := make (map [uuid.UUID ]string , len (templateIDs ))
114
140
if len (templateIDs ) > 0 {
@@ -126,6 +152,7 @@ func (mc *MetricsCollector) Run(ctx context.Context) (func(), error) {
126
152
mc .data .Store (& insightsData {
127
153
templates : templateInsights ,
128
154
apps : appInsights ,
155
+ params : paramInsights ,
129
156
130
157
templateNames : templateNames ,
131
158
})
@@ -153,6 +180,7 @@ func (mc *MetricsCollector) Run(ctx context.Context) (func(), error) {
153
180
func (* MetricsCollector ) Describe (descCh chan <- * prometheus.Desc ) {
154
181
descCh <- templatesActiveUsersDesc
155
182
descCh <- applicationsUsageSecondsDesc
183
+ descCh <- parametersDesc
156
184
}
157
185
158
186
func (mc * MetricsCollector ) Collect (metricsCh chan <- prometheus.Metric ) {
@@ -200,18 +228,26 @@ func (mc *MetricsCollector) Collect(metricsCh chan<- prometheus.Metric) {
200
228
for _ , templateRow := range data .templates {
201
229
metricsCh <- prometheus .MustNewConstMetric (templatesActiveUsersDesc , prometheus .GaugeValue , float64 (templateRow .ActiveUsers ), data .templateNames [templateRow .TemplateID ])
202
230
}
231
+
232
+ // Parameters
233
+ for _ , parameterRow := range data .params {
234
+ metricsCh <- prometheus .MustNewConstMetric (parametersDesc , prometheus .GaugeValue , float64 (parameterRow .count ), data .templateNames [parameterRow .templateID ], parameterRow .name , parameterRow .aType , parameterRow .value )
235
+ }
203
236
}
204
237
205
238
// Helper functions below.
206
239
207
- func uniqueTemplateIDs (templateInsights []database.GetTemplateInsightsByTemplateRow , appInsights []database.GetTemplateAppInsightsByTemplateRow ) []uuid.UUID {
240
+ func uniqueTemplateIDs (templateInsights []database.GetTemplateInsightsByTemplateRow , appInsights []database.GetTemplateAppInsightsByTemplateRow , paramInsights [] parameterRow ) []uuid.UUID {
208
241
tids := map [uuid.UUID ]bool {}
209
242
for _ , t := range templateInsights {
210
243
tids [t .TemplateID ] = true
211
244
}
212
245
for _ , t := range appInsights {
213
246
tids [t .TemplateID ] = true
214
247
}
248
+ for _ , t := range paramInsights {
249
+ tids [t .templateID ] = true
250
+ }
215
251
216
252
uniqueUUIDs := make ([]uuid.UUID , len (tids ))
217
253
var i int
@@ -229,3 +265,54 @@ func onlyTemplateNames(templates []database.Template) map[uuid.UUID]string {
229
265
}
230
266
return m
231
267
}
268
+
269
+ func convertParameterInsights (rows []database.GetTemplateParameterInsightsRow ) []parameterRow {
270
+ type uniqueKey struct {
271
+ templateID uuid.UUID
272
+ parameterName string
273
+ parameterType string
274
+ parameterValue string
275
+ }
276
+
277
+ m := map [uniqueKey ]int64 {}
278
+ for _ , r := range rows {
279
+ for _ , t := range r .TemplateIDs {
280
+ key := uniqueKey {
281
+ templateID : t ,
282
+ parameterName : r .Name ,
283
+ parameterType : r .Type ,
284
+ parameterValue : r .Value ,
285
+ }
286
+
287
+ if _ , ok := m [key ]; ! ok {
288
+ m [key ] = 0
289
+ }
290
+ m [key ] = m [key ] + r .Count
291
+ }
292
+ }
293
+
294
+ converted := make ([]parameterRow , len (m ))
295
+ var i int
296
+ for k , c := range m {
297
+ converted [i ] = parameterRow {
298
+ templateID : k .templateID ,
299
+ name : k .parameterName ,
300
+ aType : k .parameterType ,
301
+ value : k .parameterValue ,
302
+ count : c ,
303
+ }
304
+ i ++
305
+ }
306
+
307
+ slices .SortFunc (converted , func (a , b parameterRow ) int {
308
+ if a .templateID != b .templateID {
309
+ return slice .Ascending (a .templateID .String (), b .templateID .String ())
310
+ }
311
+ if a .name != b .name {
312
+ return slice .Ascending (a .name , b .name )
313
+ }
314
+ return slice .Ascending (a .value , b .value )
315
+ })
316
+
317
+ return converted
318
+ }
0 commit comments