Skip to content

Commit 025b1fd

Browse files
committed
feat: optionally add human-readable labels.
OxQL provides uuid labels for silos, projects, etc., but these aren't very useful for metrics queries: the user has to look up the uuid for each silo/project of interest. This patch adds the option to fetch silo and project metadata from the api and enrich labels where appropriate. We should support this in the api instead eventually, but it's quicker to add here for now.
1 parent 58b23af commit 025b1fd

3 files changed

Lines changed: 96 additions & 0 deletions

File tree

config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ type Config struct {
1414
Token string `mapstructure:"token"`
1515
MetricPatterns []string `mapstructure:"metric_patterns"`
1616
ScrapeConcurrency int `mapstructure:"scrape_concurrency"`
17+
18+
// AddLabels configures the receiver to add human-readable labels to metrics using the Oxide API.
19+
AddLabels bool `mapstructure:"add_labels"`
1720
}
1821

1922
func (cfg *Config) Validate() error {

scraper.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,32 @@ func (s *oxideScraper) Scrape(ctx context.Context) (pmetric.Metrics, error) {
159159
s.scrapeDuration.Record(ctx, elapsed.Seconds())
160160
s.scrapeCount.Add(ctx, 1, metric.WithAttributes(attribute.String("status", "success")))
161161

162+
// Cache mappings from resource UUIDs to human-readable names. Note: we can
163+
// also add mappings for higher-cardinality resources like instances and disks,
164+
// but this would add more latency to the 0th query on the page.
165+
//
166+
// TODO: add human-readable labels to metrics in oximeter so that we don't
167+
// have to enrich them here. Tracked in
168+
// https://github.com/oxidecomputer/omicron/issues/9119.
169+
siloToName := map[string]string{}
170+
projectToName := map[string]string{}
171+
if s.cfg.AddLabels {
172+
silos, err := s.client.SiloListAllPages(ctx, oxide.SiloListParams{})
173+
if err != nil {
174+
return metrics, fmt.Errorf("listing silos: %w", err)
175+
}
176+
for _, silo := range silos {
177+
siloToName[silo.Id] = string(silo.Name)
178+
}
179+
projects, err := s.client.ProjectListAllPages(ctx, oxide.ProjectListParams{})
180+
if err != nil {
181+
return metrics, fmt.Errorf("listing projects: %w", err)
182+
}
183+
for _, project := range projects {
184+
projectToName[project.Id] = string(project.Name)
185+
}
186+
}
187+
162188
for _, result := range results {
163189
for _, table := range result.Tables {
164190
for _, series := range table.Timeseries {
@@ -180,6 +206,8 @@ func (s *oxideScraper) Scrape(ctx context.Context) (pmetric.Metrics, error) {
180206
))
181207
}
182208

209+
enrichLabels(resource, siloToName, projectToName)
210+
183211
var sm pmetric.ScopeMetrics
184212
if rm.ScopeMetrics().Len() == 0 {
185213
sm = rm.ScopeMetrics().AppendEmpty()
@@ -292,6 +320,19 @@ func addLabels(series oxide.Timeseries, table oxide.OxqlTable, resource pcommon.
292320
return nil
293321
}
294322

323+
func enrichLabels(resource pcommon.Resource, silos map[string]string, projects map[string]string) {
324+
if siloID, ok := resource.Attributes().Get("silo_id"); ok {
325+
if siloName, ok := silos[siloID.Str()]; ok {
326+
resource.Attributes().PutStr("silo_name", siloName)
327+
}
328+
}
329+
if projectID, ok := resource.Attributes().Get("project_id"); ok {
330+
if projectName, ok := projects[projectID.Str()]; ok {
331+
resource.Attributes().PutStr("project_name", projectName)
332+
}
333+
}
334+
}
335+
295336
func addHistogram(dataPoints pmetric.HistogramDataPointSlice, quantileGauge pmetric.Gauge, table oxide.OxqlTable, series oxide.Timeseries) error {
296337
timestamps := series.Points.Timestamps
297338
for idx, point := range series.Points.Values {

scraper_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,58 @@ func TestAddLabels(t *testing.T) {
109109
}
110110
}
111111

112+
func TestEnrichLabels(t *testing.T) {
113+
for _, tc := range []struct {
114+
name string
115+
resource pcommon.Resource
116+
silos map[string]string
117+
projects map[string]string
118+
wantResource pcommon.Resource
119+
}{
120+
{
121+
name: "silo",
122+
resource: func() pcommon.Resource {
123+
r := pcommon.NewResource()
124+
r.Attributes().PutStr("silo_id", "123e4567-e89b-12d3-a456-426614174000")
125+
return r
126+
}(),
127+
silos: map[string]string{
128+
"123e4567-e89b-12d3-a456-426614174000": "default",
129+
},
130+
projects: map[string]string{},
131+
wantResource: func() pcommon.Resource {
132+
r := pcommon.NewResource()
133+
r.Attributes().PutStr("silo_id", "123e4567-e89b-12d3-a456-426614174000")
134+
r.Attributes().PutStr("silo_name", "default")
135+
return r
136+
}(),
137+
},
138+
{
139+
name: "project",
140+
resource: func() pcommon.Resource {
141+
r := pcommon.NewResource()
142+
r.Attributes().PutStr("project_id", "987fcdeb-51a2-43f7-b890-123456789abc")
143+
return r
144+
}(),
145+
silos: map[string]string{},
146+
projects: map[string]string{
147+
"987fcdeb-51a2-43f7-b890-123456789abc": "my-project",
148+
},
149+
wantResource: func() pcommon.Resource {
150+
r := pcommon.NewResource()
151+
r.Attributes().PutStr("project_id", "987fcdeb-51a2-43f7-b890-123456789abc")
152+
r.Attributes().PutStr("project_name", "my-project")
153+
return r
154+
}(),
155+
},
156+
} {
157+
t.Run(tc.name, func(t *testing.T) {
158+
enrichLabels(tc.resource, tc.silos, tc.projects)
159+
require.Equal(t, tc.wantResource.Attributes().AsRaw(), tc.resource.Attributes().AsRaw())
160+
})
161+
}
162+
}
163+
112164
func TestAddPoint(t *testing.T) {
113165
now := time.Now()
114166
table := oxide.OxqlTable{Name: "test_metric"}

0 commit comments

Comments
 (0)