Skip to content

Commit 78dc876

Browse files
author
Victor Demonchy
authored
Merge pull request #16 from Spendesk/feat/workflow_run_duration
Add github_workflow_run_duration_ms metric for each workflow run
2 parents c86ed15 + 5971217 commit 78dc876

5 files changed

+147
-84
lines changed

README.md

+29-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ If you want to monitor a public repository, you must put the public_repo option
1616

1717
## Exported stats
1818

19-
### github_job
19+
### github_workflow_run_status
2020
Gauge type
2121

2222
**Result possibility**
@@ -42,6 +42,32 @@ Gauge type
4242
| workflow | Workflow Name |
4343
| status | Workflow status (completed/in_progress) |
4444

45+
### github_workflow_run_duration_ms
46+
Gauge type
47+
48+
**Result possibility**
49+
50+
| Gauge | Description |
51+
|---|---|
52+
| milliseconds | Number of milliseconds that a specific workflow run took time to complete. |
53+
54+
**Fields**
55+
56+
| Name | Description |
57+
|---|---|
58+
| event | Event type like push/pull_request/...|
59+
| head_branch | Branch name |
60+
| head_sha | Commit ID |
61+
| node_id | Node ID (github actions) (mandatory ??) |
62+
| repo | Repository like \<org>/\<repo> |
63+
| run_number | Build id for the repo (incremental id => 1/2/3/4/...) |
64+
| workflow_id | Workflow ID |
65+
| workflow | Workflow Name |
66+
| status | Workflow status (completed/in_progress) |
67+
68+
### github_job
69+
> :warning: **This is a duplicate of the `github_workflow_run_status` metric that will soon be deprecated, do not use anymore.**
70+
4571
### github_runner_status
4672
Gauge type
4773
(If you have self hosted runner)
@@ -105,10 +131,10 @@ Gauge type
105131
| repo | Repository like \<org>/\<repo> |
106132
| status | Workflow status |
107133

108-
Es:
134+
Example:
109135

110136
```
111137
# HELP github_workflow_usage Number of billable seconds used by a specific workflow during the current billing cycle. Any job re-runs are also included in the usage. Only apply to workflows in private repositories that use GitHub-hosted runners.
112138
# TYPE github_workflow_usage gauge
113139
github_workflow_usage_seconds{id="2862037",name="Create Release",node_id="MDg6V29ya2Zsb3cyODYyMDM3",repo="xxx/xxx",state="active",os="UBUNTU"} 706.609
114-
```
140+
```

main.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ func runWeb(ctx *cli.Context) {
3737
go metrics.WorkflowsCache()
3838
go metrics.GetRunnersFromGithub()
3939
go metrics.GetRunnersOrganizationFromGithub()
40-
go metrics.GetJobsFromGithub()
40+
go metrics.GetWorkflowRunsFromGithub()
4141
go metrics.GetBillableFromGithub()
4242

4343
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
@@ -52,6 +52,8 @@ func runWeb(ctx *cli.Context) {
5252
func init() {
5353
prometheus.MustRegister(metrics.RunnersGauge)
5454
prometheus.MustRegister(metrics.RunnersOrganizationGauge)
55-
prometheus.MustRegister(metrics.JobsGauge)
55+
prometheus.MustRegister(metrics.WorkflowRunStatusGauge)
56+
prometheus.MustRegister(metrics.WorkflowRunStatusDeprecatedGauge)
57+
prometheus.MustRegister(metrics.WorkflowRunDurationGauge)
5658
prometheus.MustRegister(metrics.WorkflowBillGauge)
5759
}

metrics/get_jobs_from_github.go

-79
This file was deleted.
+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package metrics
2+
3+
import (
4+
"encoding/json"
5+
"log"
6+
"net/http"
7+
"strconv"
8+
"time"
9+
10+
"github-actions-exporter/config"
11+
12+
"github.com/prometheus/client_golang/prometheus"
13+
)
14+
15+
var (
16+
WorkflowRunStatusDeprecatedGauge = prometheus.NewGaugeVec(
17+
prometheus.GaugeOpts{
18+
Name: "github_job",
19+
Help: "Workflow run status, old name and duplicate of github_workflow_run_status that will soon be deprecated",
20+
},
21+
[]string{"repo", "id", "node_id", "head_branch", "head_sha", "run_number", "workflow_id", "workflow", "event", "status"},
22+
)
23+
WorkflowRunStatusGauge = prometheus.NewGaugeVec(
24+
prometheus.GaugeOpts{
25+
Name: "github_workflow_run_status",
26+
Help: "Workflow run status",
27+
},
28+
[]string{"repo", "id", "node_id", "head_branch", "head_sha", "run_number", "workflow_id", "workflow", "event", "status"},
29+
)
30+
WorkflowRunDurationGauge = prometheus.NewGaugeVec(
31+
prometheus.GaugeOpts{
32+
Name: "github_workflow_run_duration_ms",
33+
Help: "Workflow run duration (in milliseconds)",
34+
},
35+
[]string{"repo", "id", "node_id", "head_branch", "head_sha", "run_number", "workflow_id", "workflow", "event", "status"},
36+
)
37+
)
38+
39+
type workflowRunsReturn struct {
40+
TotalCount int `json:"total_count"`
41+
WorkflowRuns []workflowRun `json:"workflow_runs"`
42+
}
43+
44+
type workflowRunDurationReturn struct {
45+
RunDurationMs float64 `json:"run_duration_ms"`
46+
}
47+
48+
type workflowRun struct {
49+
ID int `json:"id"`
50+
NodeID string `json:"node_id"`
51+
HeadBranch string `json:"head_branch"`
52+
HeadSha string `json:"head_sha"`
53+
RunNumber int `json:"run_number"`
54+
Event string `json:"event"`
55+
Status string `json:"status"`
56+
Conclusion string `json:"conclusion"`
57+
UpdatedAt string `json:"updated_at"`
58+
WorkflowID int `json:"workflow_id"`
59+
}
60+
61+
func GetWorkflowRunsFromGithub() {
62+
client := &http.Client{}
63+
64+
for {
65+
for _, repo := range config.Github.Repositories {
66+
var runs workflowRunsReturn
67+
req, _ := http.NewRequest("GET", "https://"+config.Github.ApiUrl+"/repos/"+repo+"/actions/runs", nil)
68+
req.Header.Set("Authorization", "token "+config.Github.Token)
69+
resp, err := client.Do(req)
70+
if err != nil {
71+
log.Fatal(err)
72+
}
73+
if resp.StatusCode != 200 {
74+
log.Fatalf("the status code returned by the server for runs in repo %s is different from 200: %d", repo, resp.StatusCode)
75+
}
76+
err = json.NewDecoder(resp.Body).Decode(&runs)
77+
if err != nil {
78+
log.Fatal(err)
79+
}
80+
for _, r := range runs.WorkflowRuns {
81+
var s float64 = 0
82+
if r.Conclusion == "success" {
83+
s = 1
84+
} else if r.Conclusion == "skipped" {
85+
s = 2
86+
} else if r.Status == "in_progress" {
87+
s = 3
88+
} else if r.Status == "queued" {
89+
s = 4
90+
}
91+
WorkflowRunStatusGauge.WithLabelValues(repo, strconv.Itoa(r.ID), r.NodeID, r.HeadBranch, r.HeadSha, strconv.Itoa(r.RunNumber), strconv.Itoa(r.WorkflowID), workflows[repo][r.WorkflowID].Name, r.Event, r.Status).Set(s)
92+
WorkflowRunStatusDeprecatedGauge.WithLabelValues(repo, strconv.Itoa(r.ID), r.NodeID, r.HeadBranch, r.HeadSha, strconv.Itoa(r.RunNumber), strconv.Itoa(r.WorkflowID), workflows[repo][r.WorkflowID].Name, r.Event, r.Status).Set(s)
93+
94+
var duration workflowRunDurationReturn
95+
req, _ := http.NewRequest("GET", "https://"+config.Github.ApiUrl+"/repos/"+repo+"/actions/runs/"+strconv.Itoa(r.ID)+"/timing", nil)
96+
req.Header.Set("Authorization", "token "+config.Github.Token)
97+
resp, err := client.Do(req)
98+
if err != nil {
99+
log.Fatal(err)
100+
}
101+
if resp.StatusCode != 200 {
102+
log.Fatalf("the status code returned by the server for duration of run #%d in repo %s is different from 200: %d", r.ID, repo, resp.StatusCode)
103+
}
104+
err = json.NewDecoder(resp.Body).Decode(&duration)
105+
if err != nil {
106+
log.Fatal(err)
107+
}
108+
WorkflowRunDurationGauge.WithLabelValues(repo, strconv.Itoa(r.ID), r.NodeID, r.HeadBranch, r.HeadSha, strconv.Itoa(r.RunNumber), strconv.Itoa(r.WorkflowID), workflows[repo][r.WorkflowID].Name, r.Event, r.Status).Set(duration.RunDurationMs)
109+
}
110+
}
111+
112+
time.Sleep(time.Duration(config.Github.Refresh) * time.Second)
113+
}
114+
}
File renamed without changes.

0 commit comments

Comments
 (0)