Skip to content

Commit

Permalink
Merge pull request #16 from Spendesk/feat/workflow_run_duration
Browse files Browse the repository at this point in the history
Add github_workflow_run_duration_ms metric for each workflow run
  • Loading branch information
Victor Demonchy authored Mar 10, 2021
2 parents c86ed15 + 5971217 commit 78dc876
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 84 deletions.
32 changes: 29 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ If you want to monitor a public repository, you must put the public_repo option

## Exported stats

### github_job
### github_workflow_run_status
Gauge type

**Result possibility**
Expand All @@ -42,6 +42,32 @@ Gauge type
| workflow | Workflow Name |
| status | Workflow status (completed/in_progress) |

### github_workflow_run_duration_ms
Gauge type

**Result possibility**

| Gauge | Description |
|---|---|
| milliseconds | Number of milliseconds that a specific workflow run took time to complete. |

**Fields**

| Name | Description |
|---|---|
| event | Event type like push/pull_request/...|
| head_branch | Branch name |
| head_sha | Commit ID |
| node_id | Node ID (github actions) (mandatory ??) |
| repo | Repository like \<org>/\<repo> |
| run_number | Build id for the repo (incremental id => 1/2/3/4/...) |
| workflow_id | Workflow ID |
| workflow | Workflow Name |
| status | Workflow status (completed/in_progress) |

### github_job
> :warning: **This is a duplicate of the `github_workflow_run_status` metric that will soon be deprecated, do not use anymore.**
### github_runner_status
Gauge type
(If you have self hosted runner)
Expand Down Expand Up @@ -105,10 +131,10 @@ Gauge type
| repo | Repository like \<org>/\<repo> |
| status | Workflow status |

Es:
Example:

```
# 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.
# TYPE github_workflow_usage gauge
github_workflow_usage_seconds{id="2862037",name="Create Release",node_id="MDg6V29ya2Zsb3cyODYyMDM3",repo="xxx/xxx",state="active",os="UBUNTU"} 706.609
```
```
6 changes: 4 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func runWeb(ctx *cli.Context) {
go metrics.WorkflowsCache()
go metrics.GetRunnersFromGithub()
go metrics.GetRunnersOrganizationFromGithub()
go metrics.GetJobsFromGithub()
go metrics.GetWorkflowRunsFromGithub()
go metrics.GetBillableFromGithub()

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
Expand All @@ -52,6 +52,8 @@ func runWeb(ctx *cli.Context) {
func init() {
prometheus.MustRegister(metrics.RunnersGauge)
prometheus.MustRegister(metrics.RunnersOrganizationGauge)
prometheus.MustRegister(metrics.JobsGauge)
prometheus.MustRegister(metrics.WorkflowRunStatusGauge)
prometheus.MustRegister(metrics.WorkflowRunStatusDeprecatedGauge)
prometheus.MustRegister(metrics.WorkflowRunDurationGauge)
prometheus.MustRegister(metrics.WorkflowBillGauge)
}
79 changes: 0 additions & 79 deletions metrics/get_jobs_from_github.go

This file was deleted.

114 changes: 114 additions & 0 deletions metrics/get_workflow_runs_from_github.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package metrics

import (
"encoding/json"
"log"
"net/http"
"strconv"
"time"

"github-actions-exporter/config"

"github.com/prometheus/client_golang/prometheus"
)

var (
WorkflowRunStatusDeprecatedGauge = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "github_job",
Help: "Workflow run status, old name and duplicate of github_workflow_run_status that will soon be deprecated",
},
[]string{"repo", "id", "node_id", "head_branch", "head_sha", "run_number", "workflow_id", "workflow", "event", "status"},
)
WorkflowRunStatusGauge = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "github_workflow_run_status",
Help: "Workflow run status",
},
[]string{"repo", "id", "node_id", "head_branch", "head_sha", "run_number", "workflow_id", "workflow", "event", "status"},
)
WorkflowRunDurationGauge = prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "github_workflow_run_duration_ms",
Help: "Workflow run duration (in milliseconds)",
},
[]string{"repo", "id", "node_id", "head_branch", "head_sha", "run_number", "workflow_id", "workflow", "event", "status"},
)
)

type workflowRunsReturn struct {
TotalCount int `json:"total_count"`
WorkflowRuns []workflowRun `json:"workflow_runs"`
}

type workflowRunDurationReturn struct {
RunDurationMs float64 `json:"run_duration_ms"`
}

type workflowRun struct {
ID int `json:"id"`
NodeID string `json:"node_id"`
HeadBranch string `json:"head_branch"`
HeadSha string `json:"head_sha"`
RunNumber int `json:"run_number"`
Event string `json:"event"`
Status string `json:"status"`
Conclusion string `json:"conclusion"`
UpdatedAt string `json:"updated_at"`
WorkflowID int `json:"workflow_id"`
}

func GetWorkflowRunsFromGithub() {
client := &http.Client{}

for {
for _, repo := range config.Github.Repositories {
var runs workflowRunsReturn
req, _ := http.NewRequest("GET", "https://"+config.Github.ApiUrl+"/repos/"+repo+"/actions/runs", nil)
req.Header.Set("Authorization", "token "+config.Github.Token)
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
if resp.StatusCode != 200 {
log.Fatalf("the status code returned by the server for runs in repo %s is different from 200: %d", repo, resp.StatusCode)
}
err = json.NewDecoder(resp.Body).Decode(&runs)
if err != nil {
log.Fatal(err)
}
for _, r := range runs.WorkflowRuns {
var s float64 = 0
if r.Conclusion == "success" {
s = 1
} else if r.Conclusion == "skipped" {
s = 2
} else if r.Status == "in_progress" {
s = 3
} else if r.Status == "queued" {
s = 4
}
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)
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)

var duration workflowRunDurationReturn
req, _ := http.NewRequest("GET", "https://"+config.Github.ApiUrl+"/repos/"+repo+"/actions/runs/"+strconv.Itoa(r.ID)+"/timing", nil)
req.Header.Set("Authorization", "token "+config.Github.Token)
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
if resp.StatusCode != 200 {
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)
}
err = json.NewDecoder(resp.Body).Decode(&duration)
if err != nil {
log.Fatal(err)
}
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)
}
}

time.Sleep(time.Duration(config.Github.Refresh) * time.Second)
}
}
File renamed without changes.

0 comments on commit 78dc876

Please sign in to comment.