Skip to content

Commit

Permalink
Merge pull request #1 from metonymic-smokey/develop
Browse files Browse the repository at this point in the history
Use real time temperature data
  • Loading branch information
metonymic-smokey authored Sep 17, 2021
2 parents d56b31e + fe332ad commit 36ef43f
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 48 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
# prom-exporter
Repo for learning how to implement Prometheus exporter

### Instructions to run:
* Have Prometheus running on port 9090 and make sure port 2112 is not in use.
* Async exporter: `go run async.go temp.go`.
* Sync exporter: `go run sync.go temp.go`.
78 changes: 34 additions & 44 deletions async.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,35 @@
package main

import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"

"github.com/gorilla/mux"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"
)

func recordMetrics(temp float64) {
go func() {
for {
opsProcessed.Inc()
jobsInQueue.Set(temp)
func recordMetrics() {
for {
dat, err := getTempData()
if err != nil {
fmt.Println(err)
time.Sleep(2 * time.Second)
continue
}
}()
if len(dat.Data.Timestep) == 0 {
continue
}

for _, interval := range dat.Data.Timestep[0].TempVal {
jobsInQueue.Set(interval.Values.Temp)
}

opsProcessed.Inc()
time.Sleep(2 * time.Second)
}
}

var opsProcessed = promauto.NewGauge(
Expand All @@ -36,49 +46,29 @@ var jobsInQueue = promauto.NewGauge(
},
)

type Temperature struct {
Temp float64 `json:"temperature"`
}
var httpDuration = promauto.NewHistogramVec(prometheus.HistogramOpts{
Name: "http_response_time_seconds",
Help: "Duration of HTTP requests.",
}, []string{"path"})

type Final struct {
StartTime string `json:"startTime"`
Values Temperature `json:"values"`
}
func prometheusMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
route := mux.CurrentRoute(r)
path, _ := route.GetPathTemplate()

type Interval struct {
Timestep string `json:"timestep"`
StartTime string `json:"startTime"`
EndTime string `json:"endTime"`
TempVal []Final `json:"intervals"`
}
timer := prometheus.NewTimer(httpDuration.WithLabelValues(path))

type Timelines struct {
Timestep []Interval `json:"timelines"`
}

type Response struct {
Data Timelines `json:"data"`
timer.ObserveDuration()
})
}

func main() {
url := fmt.Sprintf("https://api.tomorrow.io/v4/timelines?location=%f,%f&fields=temperature&timesteps=%s&units=%s", 73.98529171943665, 40.75872069597532, "1h", "metric")

req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("apikey", "APIKEY")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()

body, _ := ioutil.ReadAll(res.Body)
go recordMetrics()

var dat Response
if err := json.Unmarshal(body, &dat); err != nil {
panic(err)
}

for _, interval := range dat.Data.Timestep[0].TempVal {
recordMetrics(interval.Values.Temp)
}
router := mux.NewRouter()
router.Use(prometheusMiddleware)

http.Handle("/metrics", promhttp.Handler())
//http.Handle("/metrics", promhttp.Handler())
router.Path("/metrics").Handler(promhttp.Handler())
http.ListenAndServe(":2112", nil)
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.16

require (
github.com/google/go-cmp v0.5.6 // indirect
github.com/gorilla/mux v1.8.0
github.com/prometheus/client_golang v1.11.0
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
Expand Down
34 changes: 30 additions & 4 deletions sync.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package main

import (
"fmt"
"log"
"math/rand"
"net/http"
"time"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
Expand All @@ -15,12 +17,26 @@ type CityStats struct {

//temperature by country
func (c *CityStats) TemperatureAndHumidity() (
tempByCity map[string]int, humidityByCity map[string]float64,
tempByCity map[string]float64, humidityByCity map[string]float64,
) {
tempByCity = map[string]int{
"bangalore": rand.Intn(100),
"london": rand.Intn(1000),
// get real time API temp data here
tempByCity = make(map[string]float64)
dat, err := getTempData()
if err != nil {
fmt.Println(err)
return
}
if len(dat.Data.Timestep) == 0 {
fmt.Println("empty result!")
return
}

cities := []string{"bangalore", "london"}

for ind, interval := range dat.Data.Timestep[0].TempVal {
tempByCity[cities[ind%2]] = interval.Values.Temp
}

humidityByCity = map[string]float64{
"bangalore": rand.Float64(),
"london": rand.Float64(),
Expand All @@ -43,14 +59,23 @@ var (
"humidity of a city as a fraction",
[]string{"city"}, nil,
)
httpDuration = prometheus.NewHistogram(prometheus.HistogramOpts{
Name: "http_response_time_seconds",
Help: "Duration of HTTP requests.",
Buckets: prometheus.LinearBuckets(20, 5, 5),
})
)

func (cc CityStatsCollector) Describe(ch chan<- *prometheus.Desc) {
prometheus.DescribeByCollect(cc, ch)
}

func (cc CityStatsCollector) Collect(ch chan<- prometheus.Metric) {
begin := time.Now()
tempByCity, humidityByCity := cc.CityStats.TemperatureAndHumidity()
duration := time.Since(begin)
httpDuration.Observe(float64(duration))

for city, temp := range tempByCity {
ch <- prometheus.MustNewConstMetric(
tempDesc,
Expand Down Expand Up @@ -88,6 +113,7 @@ func main() {
reg.MustRegister(
prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{}),
prometheus.NewGoCollector(),
httpDuration,
)

http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{}))
Expand Down
60 changes: 60 additions & 0 deletions temp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package main

import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
)

type Temperature struct {
Temp float64 `json:"temperature"`
}

type Final struct {
StartTime string `json:"startTime"`
Values Temperature `json:"values"`
}

type Interval struct {
Timestep string `json:"timestep"`
StartTime string `json:"startTime"`
EndTime string `json:"endTime"`
TempVal []Final `json:"intervals"`
}

type Timelines struct {
Timestep []Interval `json:"timelines"`
}

type Response struct {
Data Timelines `json:"data"`
}

func getTempData() (Response, error) {
url := fmt.Sprintf("https://api.tomorrow.io/v4/timelines?location=%f,%f&fields=temperature&timesteps=%s&units=%s", 73.98529171943665, 40.75872069597532, "1h", "metric")

req, err := http.NewRequest("GET", url, nil)
if err != nil {
return Response{}, errors.New("error in GET request")
}
req.Header.Add("apikey", "APIKEY")
res, err := http.DefaultClient.Do(req)
if err != nil {
return Response{}, err
}
defer res.Body.Close()

body, err := ioutil.ReadAll(res.Body)
if err != nil {
return Response{}, errors.New("error reading response")
}

var dat Response
if err := json.Unmarshal(body, &dat); err != nil {
return Response{}, errors.New("error unmarshalling JSON")
}

return dat, nil
}

0 comments on commit 36ef43f

Please sign in to comment.