Commit 725e6dbb authored by Kevin Lyda's avatar Kevin Lyda 💬
Browse files

Move to modules.

Move various bits into modules.  Add config file support.
parent f432daa9
Pipeline #1266 passed with stage
in 2 minutes and 7 seconds
...@@ -8,143 +8,37 @@ ...@@ -8,143 +8,37 @@
package main package main
import ( import (
"flag" flag "github.com/spf13/pflag"
"html/template"
"log" "log"
"net/http" "net/http"
"path"
"time"
"github.com/prometheus/client_golang/prometheus" "gitlab.com/lyda/gqgmc/config"
"github.com/prometheus/client_golang/prometheus/promhttp"
"gitlab.com/lyda/gqgmc/devices/geiger" "gitlab.com/lyda/gqgmc/devices/geiger"
"gitlab.com/lyda/gqgmc/server/metrics"
"gitlab.com/lyda/gqgmc/server/pages"
) )
var addr = flag.String("listen-address", ":8080", "Address for HTTP requests.") var addr = flag.String("listen-address", ":8080", "Address for HTTP requests")
var device = flag.String("device", "/dev/gqgmc", "Device for Geiger Counter.") var device = flag.String("device", "/dev/gqgmc", "Device for Geiger Counter")
var model = flag.String("model", "gqgmc", "Model of Geiger Counter.") var model = flag.String("model", "gqgmc", "Model of Geiger Counter")
var templateDir = flag.String("template-dir", "templates", "Template directory.") var templateDir = flag.String("template-dir", "templates", "Template directory")
var staticDir = flag.String("static-dir", "static", "Static files directory.") var staticDir = flag.String("static-dir", "static", "Static files directory")
var cfg = flag.String("config", "gqgmc.conf", "Config file")
type indexPage struct {
Model string
Version string
Serial string
Volts int16
CPM uint16
CPS uint16
}
var gc geiger.Counter
var indexPg indexPage
func indexHandler(w http.ResponseWriter, r *http.Request) {
indexPg.CPM, _ = gc.GetCPM()
indexPg.Volts, _ = gc.Volts()
t, err := template.ParseFiles(path.Join(*templateDir, "index.html"))
if err != nil {
log.Printf("Template error: %s\n", err)
}
t.Execute(w, &indexPg)
}
func staticHandler(w http.ResponseWriter, r *http.Request) {
staticFile := path.Join(*staticDir, path.Base(r.URL.Path))
http.ServeFile(w, r, staticFile)
}
// Metrics collects the metrics.
type Metrics struct {
CPM,
CPS,
Volts,
Errors *prometheus.HistogramVec
}
var metrics = &Metrics{
CPM: prometheus.NewHistogramVec(prometheus.HistogramOpts{
Namespace: "gqgmc",
Subsystem: "geiger",
Name: "cpm",
Help: "CPM readings",
Buckets: []float64{50, 99, 999, 1999},
}, []string{"serial"}),
CPS: prometheus.NewHistogramVec(prometheus.HistogramOpts{
Namespace: "gqgmc",
Subsystem: "geiger",
Name: "cps",
Help: "CPS readings",
Buckets: []float64{1, 2, 16, 33},
}, []string{"serial"}),
Volts: prometheus.NewHistogramVec(prometheus.HistogramOpts{
Namespace: "gqgmc",
Subsystem: "power",
Name: "volts",
Help: "Voltage readings",
Buckets: []float64{22, 27, 42, 50},
}, []string{"serial"}),
Errors: prometheus.NewHistogramVec(prometheus.HistogramOpts{
Namespace: "gqgmc",
Subsystem: "sys",
Name: "errors",
Help: "Error counts",
Buckets: []float64{1, 10, 100},
}, []string{"serial"}),
}
func gatherMetrics() {
var (
cpm, cps uint16
volts int16
errCt float64
err error
)
for {
if cpm, err = gc.GetCPM(); err != nil {
log.Printf("gc.GetCPM error: %s\n", err)
errCt++
} else {
metrics.CPM.WithLabelValues(gc.Serial()).Observe(float64(cpm))
}
if cps, err = gc.GetCPS(); err != nil {
log.Printf("gc.GetCPS error: %s\n", err)
errCt++
} else {
metrics.CPS.WithLabelValues(gc.Serial()).Observe(float64(cps))
}
if volts, err = gc.Volts(); err != nil {
log.Printf("gc.Volts error: %s\n", err)
errCt++
} else {
metrics.Volts.WithLabelValues(gc.Serial()).Observe(float64(volts))
}
metrics.Errors.WithLabelValues(gc.Serial()).Observe(errCt)
time.Sleep(5 * time.Second)
}
}
func main() { func main() {
flag.Parse() flag.Parse()
c, err := config.ReadConfig(*cfg)
if err != nil {
log.Printf("Couldn't read config: %s\n", err)
return
}
gc, _ = geiger.New(geiger.Config{Model: *model, Device: *device}) gc, _ := geiger.New(geiger.Config{Model: c.Model, Device: c.Device})
indexPg.Model = gc.Model()
indexPg.Version = gc.Version()
indexPg.Serial = gc.Serial()
prometheus.MustRegister(metrics.CPM)
prometheus.MustRegister(metrics.CPS)
prometheus.MustRegister(metrics.Volts)
prometheus.MustRegister(metrics.Errors)
http.HandleFunc("/", indexHandler) p := pages.New(gc, c.StaticDir, c.TemplateDir)
http.HandleFunc("/favicon.ico", staticHandler) p.Register()
http.HandleFunc("/robots.txt", staticHandler) m := metrics.Register(gc)
http.HandleFunc("/humans.txt", staticHandler)
http.Handle("/metrics", promhttp.Handler())
http.Handle("/static", http.StripPrefix("/static/", http.FileServer(http.Dir(*staticDir))))
go gatherMetrics() go m.Gather()
log.Fatal(http.ListenAndServe(*addr, nil)) log.Fatal(http.ListenAndServe(*addr, nil))
} }
//
// config.go
// Copyright (C) 2017 kevin <kevin@phrye.com>
//
// Distributed under terms of the GPL license.
//
package config
import (
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
// Config for storing configuration.
type Config struct {
ListenAddress string `mapstructure:"listen_address"`
Device string `mapstructure:"device"`
Model string `mapstructure:"model"`
TemplateDir string `mapstructure:"template_dir"`
StaticDir string `mapstructure:"static_dir"`
}
func setDefaults() {
viper.BindPFlag("listen_address", pflag.Lookup("listen-address"))
viper.BindPFlag("device", pflag.Lookup("device"))
viper.BindPFlag("model", pflag.Lookup("model"))
viper.BindPFlag("template_dir", pflag.Lookup("template-dir"))
viper.BindPFlag("static_dir", pflag.Lookup("static-dir"))
}
// ReadConfig reads the client configuration from a file into a Config struct.
func ReadConfig(cfg string) (*Config, error) {
setDefaults()
viper.SetConfigFile(cfg)
viper.SetConfigType("hcl")
if err := viper.ReadInConfig(); err != nil {
return nil, err
}
c := &Config{}
if err := viper.Unmarshal(c); err != nil {
return nil, err
}
return c, nil
}
//
// metrics.go
// Copyright (C) 2017 kevin <kevin@ie.suberic.net>
//
// Distributed under terms of the MIT license.
//
package metrics
import (
"log"
"net/http"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"gitlab.com/lyda/gqgmc/devices/geiger"
)
// Metrics collects the metrics.
type Metrics struct {
gc geiger.Counter
cpm,
cps,
volts,
errs *prometheus.HistogramVec
}
// Register metrics and metrics page.
func Register(gc geiger.Counter) *Metrics {
var metrics = &Metrics{
gc: gc,
cpm: prometheus.NewHistogramVec(prometheus.HistogramOpts{
Namespace: "gqgmc",
Subsystem: "geiger",
Name: "cpm",
Help: "CPM readings",
Buckets: []float64{50, 99, 999, 1999},
}, []string{"serial"}),
cps: prometheus.NewHistogramVec(prometheus.HistogramOpts{
Namespace: "gqgmc",
Subsystem: "geiger",
Name: "cps",
Help: "CPS readings",
Buckets: []float64{1, 2, 16, 33},
}, []string{"serial"}),
volts: prometheus.NewHistogramVec(prometheus.HistogramOpts{
Namespace: "gqgmc",
Subsystem: "power",
Name: "volts",
Help: "Voltage readings",
Buckets: []float64{22, 27, 42, 50},
}, []string{"serial"}),
errs: prometheus.NewHistogramVec(prometheus.HistogramOpts{
Namespace: "gqgmc",
Subsystem: "sys",
Name: "errors",
Help: "Error counts",
Buckets: []float64{1, 10, 100},
}, []string{"serial"}),
}
prometheus.MustRegister(metrics.cpm)
prometheus.MustRegister(metrics.cps)
prometheus.MustRegister(metrics.volts)
prometheus.MustRegister(metrics.errs)
http.Handle("/metrics", promhttp.Handler())
return metrics
}
// Gather loop to gather metrics
func (m *Metrics) Gather() {
var (
cpm, cps uint16
volts int16
errCt float64
err error
)
for {
if cpm, err = m.gc.GetCPM(); err != nil {
log.Printf("gc.GetCPM error: %s\n", err)
errCt++
} else {
m.cpm.WithLabelValues(m.gc.Serial()).Observe(float64(cpm))
}
if cps, err = m.gc.GetCPS(); err != nil {
log.Printf("gc.GetCPS error: %s\n", err)
errCt++
} else {
m.cps.WithLabelValues(m.gc.Serial()).Observe(float64(cps))
}
if volts, err = m.gc.Volts(); err != nil {
log.Printf("gc.Volts error: %s\n", err)
errCt++
} else {
m.volts.WithLabelValues(m.gc.Serial()).Observe(float64(volts))
}
m.errs.WithLabelValues(m.gc.Serial()).Observe(errCt)
time.Sleep(5 * time.Second)
}
}
//
// pages.go
// Copyright (C) 2017 kevin <kevin@ie.suberic.net>
//
// Distributed under terms of the MIT license.
//
package pages
import (
"html/template"
"log"
"net/http"
"path"
"gitlab.com/lyda/gqgmc/devices/geiger"
)
// Pages where data for pages goes
type Pages struct {
gc geiger.Counter
staticDir,
templateDir string
}
type indexPage struct {
Model string
Version string
Serial string
Volts int16
CPM uint16
}
// New create new Pages.
func New(gc geiger.Counter, staticDir, templateDir string) Pages {
return Pages{gc: gc, staticDir: staticDir, templateDir: templateDir}
}
// Register pages.
func (p Pages) Register() {
http.HandleFunc("/", p.indexHandler)
http.HandleFunc("/favicon.ico", p.staticHandler)
http.HandleFunc("/robots.txt", p.staticHandler)
http.HandleFunc("/humans.txt", p.staticHandler)
http.Handle("/static", http.StripPrefix("/static/", http.FileServer(http.Dir(p.staticDir))))
}
func (p Pages) indexHandler(w http.ResponseWriter, r *http.Request) {
var indexPg indexPage
indexPg.CPM, _ = p.gc.GetCPM()
indexPg.Volts, _ = p.gc.Volts()
indexPg.Model = p.gc.Model()
indexPg.Version = p.gc.Version()
indexPg.Serial = p.gc.Serial()
t, err := template.ParseFiles(path.Join(p.templateDir, "index.html"))
if err != nil {
log.Printf("Template error: %s\n", err)
}
t.Execute(w, &indexPg)
}
func (p Pages) staticHandler(w http.ResponseWriter, r *http.Request) {
staticFile := path.Join(p.staticDir, path.Base(r.URL.Path))
http.ServeFile(w, r, staticFile)
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment