Skip to content

Commit a48ecc1

Browse files
committed
Add azure_devops_resourceusage_build
1 parent 9f0ddf0 commit a48ecc1

File tree

4 files changed

+143
-9
lines changed

4 files changed

+143
-9
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Normally no configuration is needed but can be customized using environment vari
2222
| `SCRAPE_TIME_DEPLOYMENT` | not set, default see `SCRAPE_TIME` | Interval for deployment metrics |
2323
| `SCRAPE_TIME_PULLREQUEST` | not set, default see `SCRAPE_TIME` | Interval for pullrequest metrics |
2424
| `SCRAPE_TIME_STATS` | not set, default see `SCRAPE_TIME` | Interval for stats metrics |
25+
| `SCRAPE_TIME_RESOURCEUSAGE` | not set, default see `SCRAPE_TIME` | Interval for resourceusage metrics |
2526
| `SCRAPE_TIME_LIVE` | `30s` | Time (time.Duration) between API calls |
2627
| `SERVER_BIND` | `:8080` | IP/Port binding |
2728
| `AZURE_DEVOPS_URL` | none | Azure DevOps url (only if on-prem) |
@@ -78,6 +79,7 @@ Metrics
7879
| `azure_devops_stats_project_builds` | stats | Number of builds per project, definition and result (counter) |
7980
| `azure_devops_stats_project_builds_wait` | stats | Build wait time per project, definition and result (summary) |
8081
| `azure_devops_stats_project_builds_duration` | stats | Build duration per project, definition and result (summary) |
82+
| `azure_devops_resourceusage_build` | resourceusage | Usage of limited and paid Azure DevOps resources |
8183

8284

8385
Usage
@@ -98,7 +100,9 @@ Application Options:
98100
--scrape.time.deployment= Scrape time for deployment metrics (time.duration) [$SCRAPE_TIME_DEPLOYMENT]
99101
--scrape.time.pullrequest= Scrape time for pullrequest metrics (time.duration) [$SCRAPE_TIME_PULLREQUEST]
100102
--scrape.time.stats= Scrape time for stats metrics (time.duration) [$SCRAPE_TIME_STATS]
103+
--scrape.time.resourceusage= Scrape time for resourceusage metrics (time.duration) [$SCRAPE_TIME_RESOURCEUSAGE]
101104
--scrape.time.live= Scrape time for live metrics (time.duration) (default: 30s) [$SCRAPE_TIME_LIVE]
105+
--stats.summary.maxage= Stats Summary metrics max age (time.duration) [$STATS_SUMMARY_MAX_AGE]
102106
--whitelist.project= Filter projects (UUIDs) [$AZURE_DEVOPS_FILTER_PROJECT]
103107
--blacklist.project= Filter projects (UUIDs) [$AZURE_DEVOPS_BLACKLIST_PROJECT]
104108
--whitelist.agentpool= Filter of agent pool (IDs) [$AZURE_DEVOPS_FILTER_AGENTPOOL]
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package AzureDevopsClient
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/url"
7+
)
8+
9+
type ResourceUsageBuild struct {
10+
DistributedTaskAgents *int `json:"distributedTaskAgents"`
11+
PaidPrivateAgentSlots *int `json:"paidPrivateAgentSlots"`
12+
TotalUsage *int `json:"totalUsage"`
13+
XamlControllers *int `json:"xamlControllers"`
14+
}
15+
16+
func (c *AzureDevopsClient) GetResourceUsageBuild() (ret ResourceUsageBuild, error error) {
17+
defer c.concurrencyUnlock()
18+
c.concurrencyLock()
19+
20+
url := fmt.Sprintf(
21+
"/_apis/build/resourceusage?api-version=%v",
22+
// FIXME: hardcoded api version
23+
url.QueryEscape("5.1-preview.2"),
24+
)
25+
response, err := c.rest().R().Get(url)
26+
if err := c.checkResponse(response, err); err != nil {
27+
error = err
28+
return
29+
}
30+
31+
err = json.Unmarshal(response.Body(), &ret)
32+
if err != nil {
33+
error = err
34+
return
35+
}
36+
37+
return
38+
}

main.go

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,16 @@ var opts struct {
3636
ServerBind string `long:"bind" env:"SERVER_BIND" description:"Server address" default:":8080"`
3737

3838
// scrape time settings
39-
ScrapeTime time.Duration `long:"scrape.time" env:"SCRAPE_TIME" description:"Default scrape time (time.duration)" default:"30m"`
40-
ScrapeTimeProjects *time.Duration `long:"scrape.time.projects" env:"SCRAPE_TIME_PROJECTS" description:"Scrape time for project metrics (time.duration)"`
41-
ScrapeTimeRepository *time.Duration `long:"scrape.time.repository" env:"SCRAPE_TIME_REPOSITORY" description:"Scrape time for repository metrics (time.duration)"`
42-
ScrapeTimeBuild *time.Duration `long:"scrape.time.build" env:"SCRAPE_TIME_BUILD" description:"Scrape time for build metrics (time.duration)"`
43-
ScrapeTimeRelease *time.Duration `long:"scrape.time.release" env:"SCRAPE_TIME_RELEASE" description:"Scrape time for release metrics (time.duration)"`
44-
ScrapeTimeDeployment *time.Duration `long:"scrape.time.deployment" env:"SCRAPE_TIME_DEPLOYMENT" description:"Scrape time for deployment metrics (time.duration)"`
45-
ScrapeTimePullRequest *time.Duration `long:"scrape.time.pullrequest" env:"SCRAPE_TIME_PULLREQUEST" description:"Scrape time for pullrequest metrics (time.duration)"`
46-
ScrapeTimeStats *time.Duration `long:"scrape.time.stats" env:"SCRAPE_TIME_STATS" description:"Scrape time for stats metrics (time.duration)"`
47-
ScrapeTimeLive *time.Duration `long:"scrape.time.live" env:"SCRAPE_TIME_LIVE" description:"Scrape time for live metrics (time.duration)" default:"30s"`
39+
ScrapeTime time.Duration `long:"scrape.time" env:"SCRAPE_TIME" description:"Default scrape time (time.duration)" default:"30m"`
40+
ScrapeTimeProjects *time.Duration `long:"scrape.time.projects" env:"SCRAPE_TIME_PROJECTS" description:"Scrape time for project metrics (time.duration)"`
41+
ScrapeTimeRepository *time.Duration `long:"scrape.time.repository" env:"SCRAPE_TIME_REPOSITORY" description:"Scrape time for repository metrics (time.duration)"`
42+
ScrapeTimeBuild *time.Duration `long:"scrape.time.build" env:"SCRAPE_TIME_BUILD" description:"Scrape time for build metrics (time.duration)"`
43+
ScrapeTimeRelease *time.Duration `long:"scrape.time.release" env:"SCRAPE_TIME_RELEASE" description:"Scrape time for release metrics (time.duration)"`
44+
ScrapeTimeDeployment *time.Duration `long:"scrape.time.deployment" env:"SCRAPE_TIME_DEPLOYMENT" description:"Scrape time for deployment metrics (time.duration)"`
45+
ScrapeTimePullRequest *time.Duration `long:"scrape.time.pullrequest" env:"SCRAPE_TIME_PULLREQUEST" description:"Scrape time for pullrequest metrics (time.duration)"`
46+
ScrapeTimeStats *time.Duration `long:"scrape.time.stats" env:"SCRAPE_TIME_STATS" description:"Scrape time for stats metrics (time.duration)"`
47+
ScrapeTimeResourceUsage *time.Duration `long:"scrape.time.resourceusage" env:"SCRAPE_TIME_RESOURCEUSAGE" description:"Scrape time for resourceusage metrics (time.duration)"`
48+
ScrapeTimeLive *time.Duration `long:"scrape.time.live" env:"SCRAPE_TIME_LIVE" description:"Scrape time for live metrics (time.duration)" default:"30s"`
4849

4950
// summary options
5051
StatsSummaryMaxAge *time.Duration `long:"stats.summary.maxage" env:"STATS_SUMMARY_MAX_AGE" description:"Stats Summary metrics max age (time.duration)"`
@@ -93,6 +94,7 @@ func main() {
9394
Logger.Infof("set scape interval[Release]: %v", scrapeIntervalStatus(opts.ScrapeTimeRelease))
9495
Logger.Infof("set scape interval[Deployment]: %v", scrapeIntervalStatus(opts.ScrapeTimeDeployment))
9596
Logger.Infof("set scape interval[Stats]: %v", scrapeIntervalStatus(opts.ScrapeTimeStats))
97+
Logger.Infof("set scape interval[ResourceUsage]: %v", scrapeIntervalStatus(opts.ScrapeTimeResourceUsage))
9698
initMetricCollector()
9799

98100
Logger.Infof("Starting http server on %s", opts.ServerBind)
@@ -145,6 +147,10 @@ func initArgparser() {
145147
opts.ScrapeTimeStats = &opts.ScrapeTime
146148
}
147149

150+
if opts.ScrapeTimeResourceUsage == nil {
151+
opts.ScrapeTimeResourceUsage = &opts.ScrapeTime
152+
}
153+
148154
if opts.ScrapeTimeLive == nil {
149155
opts.ScrapeTimeLive = &opts.ScrapeTime
150156
}
@@ -307,6 +313,15 @@ func initMetricCollector() {
307313
Logger.Infof("collector[%s]: disabled", collectorName)
308314
}
309315

316+
collectorName = "ResourceUsage"
317+
if opts.ScrapeTimeStats.Seconds() > 0 {
318+
collectorGeneralList[collectorName] = NewCollectorGeneral(collectorName, &MetricsCollectorResourceUsage{})
319+
collectorGeneralList[collectorName].SetAzureProjects(&projectList)
320+
collectorGeneralList[collectorName].SetScrapeTime(*opts.ScrapeTimeStats)
321+
} else {
322+
Logger.Infof("collector[%s]: disabled", collectorName)
323+
}
324+
310325
for _, collector := range collectorGeneralList {
311326
collector.Run()
312327
}

metrics_resourceusage.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"github.com/prometheus/client_golang/prometheus"
6+
)
7+
8+
type MetricsCollectorResourceUsage struct {
9+
CollectorProcessorGeneral
10+
11+
prometheus struct {
12+
resourceUsageBuild *prometheus.GaugeVec
13+
}
14+
}
15+
16+
func (m *MetricsCollectorResourceUsage) Setup(collector *CollectorGeneral) {
17+
m.CollectorReference = collector
18+
19+
m.prometheus.resourceUsageBuild = prometheus.NewGaugeVec(
20+
prometheus.GaugeOpts{
21+
Name: "azure_devops_resourceusage_build",
22+
Help: "Azure DevOps resource usage for build",
23+
},
24+
[]string{
25+
"name",
26+
},
27+
)
28+
29+
prometheus.MustRegister(m.prometheus.resourceUsageBuild)
30+
}
31+
32+
func (m *MetricsCollectorResourceUsage) Reset() {
33+
m.prometheus.resourceUsageBuild.Reset()
34+
}
35+
36+
func (m *MetricsCollectorResourceUsage) Collect(ctx context.Context, callback chan<- func()) {
37+
m.CollectResourceUsage(ctx, callback)
38+
}
39+
40+
func (m *MetricsCollectorResourceUsage) CollectResourceUsage(ctx context.Context, callback chan<- func()) {
41+
resourceUsage, err := AzureDevopsClient.GetResourceUsageBuild()
42+
if err != nil {
43+
Logger.Errorf("call[GetResourceUsageBuild]: %v", err)
44+
return
45+
}
46+
47+
resourceUsageMetric := NewMetricCollectorList()
48+
49+
if resourceUsage.DistributedTaskAgents != nil {
50+
resourceUsageMetric.Add(prometheus.Labels{
51+
"name": "DistributedTaskAgents",
52+
}, float64(*resourceUsage.DistributedTaskAgents))
53+
}
54+
55+
if resourceUsage.PaidPrivateAgentSlots != nil {
56+
resourceUsageMetric.Add(prometheus.Labels{
57+
"name": "PaidPrivateAgentSlots",
58+
}, float64(*resourceUsage.PaidPrivateAgentSlots))
59+
}
60+
61+
if resourceUsage.TotalUsage != nil {
62+
resourceUsageMetric.Add(prometheus.Labels{
63+
"name": "TotalUsage",
64+
}, float64(*resourceUsage.TotalUsage))
65+
}
66+
67+
if resourceUsage.XamlControllers != nil {
68+
resourceUsageMetric.Add(prometheus.Labels{
69+
"name": "XamlControllers",
70+
}, float64(*resourceUsage.XamlControllers))
71+
}
72+
73+
callback <- func() {
74+
resourceUsageMetric.GaugeSet(m.prometheus.resourceUsageBuild)
75+
}
76+
77+
}

0 commit comments

Comments
 (0)