160 lines
3.3 KiB
Go
160 lines
3.3 KiB
Go
|
package main
|
||
|
|
||
|
import (
|
||
|
"github.com/prometheus/client_golang/prometheus"
|
||
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||
|
"net/http"
|
||
|
"log"
|
||
|
"path/filepath"
|
||
|
"os"
|
||
|
"time"
|
||
|
"io/ioutil"
|
||
|
"strings"
|
||
|
"strconv"
|
||
|
"errors"
|
||
|
)
|
||
|
|
||
|
//
|
||
|
// HELPERS
|
||
|
//
|
||
|
var ErrNoInformation = errors.New("no project information")
|
||
|
|
||
|
type ProjectInfo struct {
|
||
|
LastSyncStart time.Time
|
||
|
Duration int
|
||
|
ExitCode int
|
||
|
}
|
||
|
|
||
|
func getProjectInfo(path string) (*ProjectInfo, error) {
|
||
|
f, err := os.Open(path)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
data, err := ioutil.ReadAll(f)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
components := strings.Split(string(data), ",")
|
||
|
|
||
|
if len(components) == 3 {
|
||
|
lastSyncUnix, err := strconv.ParseInt(components[0], 10, 64)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
lastSync := time.Unix(lastSyncUnix, 0)
|
||
|
|
||
|
duration, err := strconv.Atoi(components[1])
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
exitCode, err := strconv.Atoi(components[2])
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return &ProjectInfo{
|
||
|
LastSyncStart: lastSync,
|
||
|
Duration: duration,
|
||
|
ExitCode: exitCode,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
return nil, ErrNoInformation
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// METRICS
|
||
|
//
|
||
|
var (
|
||
|
stampsDirectory = "/home/mirror/merlin/stamps"
|
||
|
|
||
|
namespace = "merlin"
|
||
|
|
||
|
lastSyncStart = prometheus.NewDesc(
|
||
|
prometheus.BuildFQName(namespace, "", "last_sync_start"),
|
||
|
"Timestamp the last synchronization attempt was started.",
|
||
|
[]string{"project"}, nil,
|
||
|
)
|
||
|
|
||
|
lastSyncDuration = prometheus.NewDesc(
|
||
|
prometheus.BuildFQName(namespace, "", "last_sync_duration"),
|
||
|
"Duration of the last sync.",
|
||
|
[]string{"project"}, nil,
|
||
|
)
|
||
|
|
||
|
lastSyncExitCode = prometheus.NewDesc(
|
||
|
prometheus.BuildFQName(namespace, "", "last_sync_exit_code"),
|
||
|
"Duration of the last sync.",
|
||
|
[]string{"project"}, nil,
|
||
|
)
|
||
|
)
|
||
|
|
||
|
//
|
||
|
// EXPORTER
|
||
|
//
|
||
|
type Exporter struct {
|
||
|
stampsDirectory string
|
||
|
}
|
||
|
|
||
|
func NewExporter(stampsDirectory string) *Exporter {
|
||
|
return &Exporter{
|
||
|
stampsDirectory: stampsDirectory,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
|
||
|
ch <- lastSyncStart
|
||
|
ch <- lastSyncDuration
|
||
|
ch <- lastSyncExitCode
|
||
|
}
|
||
|
|
||
|
func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
|
||
|
log.Printf("collecting")
|
||
|
|
||
|
err := filepath.Walk(e.stampsDirectory, func(path string, info os.FileInfo, err error) error {
|
||
|
if info.IsDir() {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
projectName := info.Name()
|
||
|
|
||
|
// Fetch the project information
|
||
|
projectInfo, err := getProjectInfo(path)
|
||
|
if err != nil && err == ErrNoInformation {
|
||
|
log.Printf("no project information for %s", projectName)
|
||
|
return nil
|
||
|
} else if err != nil {
|
||
|
log.Printf("error processing %s: %v", projectName, err)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
ch <- prometheus.MustNewConstMetric(
|
||
|
lastSyncStart, prometheus.GaugeValue, float64(projectInfo.LastSyncStart.Unix()), projectName,
|
||
|
)
|
||
|
ch <- prometheus.MustNewConstMetric(
|
||
|
lastSyncDuration, prometheus.GaugeValue, float64(projectInfo.Duration), projectName,
|
||
|
)
|
||
|
ch <- prometheus.MustNewConstMetric(
|
||
|
lastSyncExitCode, prometheus.GaugeValue, float64(projectInfo.ExitCode), projectName,
|
||
|
)
|
||
|
return nil
|
||
|
})
|
||
|
|
||
|
if err != nil {
|
||
|
log.Printf("failed to walk directory: %v", err)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func main() {
|
||
|
// Setup the exporter
|
||
|
exporter := NewExporter(stampsDirectory)
|
||
|
prometheus.MustRegister(exporter)
|
||
|
|
||
|
http.Handle("/metrics", promhttp.Handler())
|
||
|
log.Fatal(http.ListenAndServe(":9101", nil))
|
||
|
}
|