diff --git a/cmd/gqgmcd/main.go b/cmd/gqgmcd/main.go index 13bb01766d4eed72f38ebaa37686fb50b67be6d1..d759f3474c744506efad790e516cf1dafdcbb6c5 100644 --- a/cmd/gqgmcd/main.go +++ b/cmd/gqgmcd/main.go @@ -8,143 +8,37 @@ package main import ( - "flag" - "html/template" + flag "github.com/spf13/pflag" "log" "net/http" - "path" - "time" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" + "gitlab.com/lyda/gqgmc/config" "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 device = flag.String("device", "/dev/gqgmc", "Device for Geiger Counter.") -var model = flag.String("model", "gqgmc", "Model of Geiger Counter.") -var templateDir = flag.String("template-dir", "templates", "Template directory.") -var staticDir = flag.String("static-dir", "static", "Static files directory.") - -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) - } -} +var addr = flag.String("listen-address", ":8080", "Address for HTTP requests") +var device = flag.String("device", "/dev/gqgmc", "Device for Geiger Counter") +var model = flag.String("model", "gqgmc", "Model of Geiger Counter") +var templateDir = flag.String("template-dir", "templates", "Template directory") +var staticDir = flag.String("static-dir", "static", "Static files directory") +var cfg = flag.String("config", "gqgmc.conf", "Config file") func main() { 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}) - - 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) + gc, _ := geiger.New(geiger.Config{Model: c.Model, Device: c.Device}) - http.HandleFunc("/", indexHandler) - http.HandleFunc("/favicon.ico", staticHandler) - http.HandleFunc("/robots.txt", staticHandler) - http.HandleFunc("/humans.txt", staticHandler) - http.Handle("/metrics", promhttp.Handler()) - http.Handle("/static", http.StripPrefix("/static/", http.FileServer(http.Dir(*staticDir)))) + p := pages.New(gc, c.StaticDir, c.TemplateDir) + p.Register() + m := metrics.Register(gc) - go gatherMetrics() + go m.Gather() log.Fatal(http.ListenAndServe(*addr, nil)) } diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000000000000000000000000000000000000..22a0be3865ff497460b6dac3d3eb345bd996565d --- /dev/null +++ b/config/config.go @@ -0,0 +1,45 @@ +// +// 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 +} diff --git a/server/metrics/metrics.go b/server/metrics/metrics.go new file mode 100644 index 0000000000000000000000000000000000000000..2f479bcfef854d98b778c68e617ddca5cc8ee866 --- /dev/null +++ b/server/metrics/metrics.go @@ -0,0 +1,103 @@ +// +// 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) + } +} diff --git a/server/pages/pages.go b/server/pages/pages.go new file mode 100644 index 0000000000000000000000000000000000000000..ef62b6b37c0b207ba20e717978053b2ca8f4ab52 --- /dev/null +++ b/server/pages/pages.go @@ -0,0 +1,67 @@ +// +// 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) +}