diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..86a594e --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +.gitignore +chart +README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..08760a6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +druid-tasks-exporter + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..37b9d67 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM golang:1.21.4-alpine AS build + +WORKDIR /app + +COPY go.mod ./ +COPY go.sum ./ +RUN go mod download + +COPY *.go ./ +RUN go build -o druid-tasks-exporter + +FROM scratch +WORKDIR / +COPY --from=build /app/druid-tasks-exporter . +CMD ["/druid-tasks-exporter"] diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..15897f7 --- /dev/null +++ b/go.mod @@ -0,0 +1,17 @@ +module github.com/wiremind/druid-tasks-exporter + +go 1.21.4 + +require github.com/prometheus/client_golang v1.17.0 + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect + github.com/prometheus/common v0.44.0 // indirect + github.com/prometheus/procfs v0.11.1 // indirect + golang.org/x/sys v0.11.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..0aec189 --- /dev/null +++ b/go.sum @@ -0,0 +1,31 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= +github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= +github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= diff --git a/main.go b/main.go new file mode 100644 index 0000000..c80ef57 --- /dev/null +++ b/main.go @@ -0,0 +1,98 @@ +package main + +import ( + "bytes" + "encoding/json" + "flag" + "io" + "log" + "net/http" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +var ( + addr = flag.String("listen-address", ":8080", "The address to listen on for HTTP requests.") + druidUri = flag.String("druid-uri", "http://BROKER:8082/druid/v2/sql/", "The URI to reach Druid's router or broker SQL API.") +) + +type Task struct { + Type string + Status string + Total int +} + +type DruidTasksExporter struct { + Tasks *prometheus.Desc +} + +func NewDruidTasksExporter() *DruidTasksExporter { + return &DruidTasksExporter{ + Tasks: prometheus.NewDesc( + "dte_druid_tasks_total", + "Total number of Druid tasks per type and status.", + []string{"type", "status"}, + prometheus.Labels{}, + )} +} + +func (d *DruidTasksExporter) RetrieveMetrics() []Task { + + query, _ := json.Marshal(map[string]string{ + "query": "SELECT type,status,count(*) AS total FROM sys.tasks GROUP BY status,type", + }) + + reqBody := bytes.NewBuffer(query) + resp, err := http.Post(*druidUri, "application/json", reqBody) + if err != nil { + log.Fatalf("An Error occured while making the request: %v", err) + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + log.Fatalf("An Error occured while reading the response: %v", err) + } + + var tasks []Task + err = json.Unmarshal(body, &tasks) + if err != nil { + log.Fatalf("An Error occured while unmarshalling %s: %v", body, err) + } + + return tasks +} + +func (c *DruidTasksExporter) Describe(ch chan<- *prometheus.Desc) { + ch <- c.Tasks +} + +func (d *DruidTasksExporter) Collect(ch chan<- prometheus.Metric) { + tasks := d.RetrieveMetrics() + for _, task := range tasks { + ch <- prometheus.MustNewConstMetric( + d.Tasks, + prometheus.GaugeValue, + float64(task.Total), + task.Type, + task.Status, + ) + } +} +func ok(w http.ResponseWriter, _ *http.Request) { + io.WriteString(w, "ok") +} + +func main() { + flag.Parse() + + druid := NewDruidTasksExporter() + reg := prometheus.NewPedanticRegistry() + reg.MustRegister(druid) + + http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{})) + http.HandleFunc("/", ok) + log.Printf("The server is listening on %s", *addr) + log.Fatal(http.ListenAndServe(*addr, nil)) +}