new play metric
All checks were successful
continuous-integration/drone/push Build is passing

it sets playing tracks on 1 and played but non playing tracks to 0. this
also needed a implementation of a watch goroutine that checks the status
of the MPD server each second to set this metric.
This commit is contained in:
Marvin Steadfast 2021-05-28 08:47:57 +02:00
parent 70ed46d408
commit b7418991e1
5 changed files with 125 additions and 26 deletions

View File

@ -1,10 +1,12 @@
//nolint:gochecknoglobals,golint,stylecheck //nolint:gochecknoglobals
package prepare package prepare
import "embed" import "embed"
// Files are files to be copied to the system.
//go:embed files //go:embed files
var Files embed.FS var Files embed.FS
// Templates are the used templates for creating file on the system.
//go:embed templates //go:embed templates
var Templates embed.FS var Templates embed.FS

View File

@ -7,6 +7,7 @@ import (
api "go.xsfx.dev/schnutibox/pkg/api/v1" api "go.xsfx.dev/schnutibox/pkg/api/v1"
) )
// Cfg stores a global config object.
var Cfg Config var Cfg Config
type Config struct { type Config struct {

View File

@ -4,18 +4,64 @@ package metrics
import ( import (
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promauto"
api "go.xsfx.dev/schnutibox/pkg/api/v1"
) )
var ( // Plays is a map of tracked plays.
TracksPlayed = promauto.NewCounterVec( // Its a map, so its easier to check if the metric is already initialized
prometheus.CounterOpts{ // and usable. The Key string is the RFID identification.
Name: "schnutibox_played_tracks_total", var Plays = make(map[string]*api.IdentifyResponse)
},
[]string{"rfid", "name"})
BoxErrors = promauto.NewCounter( // NewPlay initialize a new play metric.
prometheus.CounterOpts{ func NewPlay(rfid, name string, uris []string) {
Name: "schnutbox_errors_total", if _, ok := Plays[rfid]; !ok {
}, Plays[rfid] = &api.IdentifyResponse{
) Name: name,
Uris: uris,
}
}
}
// Play is the play metric.
var Play = promauto.NewGaugeVec(
prometheus.GaugeOpts{
Name: "schnutibox_plays",
Help: "play metrics",
},
[]string{"rfid", "name"},
) )
// BoxErrors counts schnutibox errors.
var BoxErrors = promauto.NewCounter(
prometheus.CounterOpts{
Name: "schnutibox_errors_total",
Help: "counter of errors",
},
)
// tracksEqual checks if uris slices are equal.
// This is needed to search for the right play item.
func tracksEqual(a, b []string) bool {
if len(a) != len(b) {
return false
}
for i, v := range a {
if v != b[i] {
return false
}
}
return true
}
// Set sets `1` on play gauge if item is playing, a `0` on every other play.
func Set(uris []string, state string) {
for r, p := range Plays {
if tracksEqual(uris, p.Uris) && state == "play" {
Play.WithLabelValues(r, p.Name).Set(1)
} else {
Play.WithLabelValues(r, p.Name).Set(0)
}
}
}

View File

@ -33,6 +33,7 @@ const (
snapclientGroup = "snapclient" snapclientGroup = "snapclient"
) )
// Cfg represents the structured data for the schnutibox config file.
var Cfg = struct { var Cfg = struct {
RFIDReader string RFIDReader string
ReadOnly bool ReadOnly bool

View File

@ -3,6 +3,7 @@ package run
import ( import (
"fmt" "fmt"
"time"
"github.com/fhs/gompd/v2/mpd" "github.com/fhs/gompd/v2/mpd"
"github.com/rs/zerolog" "github.com/rs/zerolog"
@ -14,6 +15,8 @@ import (
"go.xsfx.dev/schnutibox/pkg/web" "go.xsfx.dev/schnutibox/pkg/web"
) )
const TickerTime = time.Second
type mpc struct { type mpc struct {
conn *mpd.Client conn *mpd.Client
} }
@ -37,9 +40,6 @@ func (m *mpc) clear(logger zerolog.Logger) error {
func (m *mpc) play(logger zerolog.Logger, rfid string, name string, uris []string) error { func (m *mpc) play(logger zerolog.Logger, rfid string, name string, uris []string) error {
logger.Info().Msg("trying to add tracks") logger.Info().Msg("trying to add tracks")
// Metric labels.
mLabels := []string{rfid, name}
// Stop playing track. // Stop playing track.
if err := m.stop(logger); err != nil { if err := m.stop(logger); err != nil {
metrics.BoxErrors.Inc() metrics.BoxErrors.Inc()
@ -65,11 +65,60 @@ func (m *mpc) play(logger zerolog.Logger, rfid string, name string, uris []strin
} }
} }
metrics.TracksPlayed.WithLabelValues(mLabels...).Inc() metrics.NewPlay(rfid, name, uris)
return m.conn.Play(-1) return m.conn.Play(-1)
} }
func (m *mpc) watch() {
log.Debug().Msg("starting watch")
ticker := time.NewTicker(TickerTime)
go func() {
for {
<-ticker.C
// Check if we can connect to MPD server.
if err := m.conn.Ping(); err != nil {
log.Error().Err(err).Msg("could not ping MPD server")
metrics.BoxErrors.Inc()
continue
}
// Getting playlist info.
attrs, err := m.conn.PlaylistInfo(-1, -1)
if err != nil {
log.Error().Err(err).Msg("could not get playlist info")
metrics.BoxErrors.Inc()
continue
}
// Stores the tracklist it got from the MPD server.
uris := []string{}
// Builds uri list.
for _, a := range attrs {
uris = append(uris, a["file"])
}
// Gettings MPD state.
s, err := m.conn.Status()
if err != nil {
log.Error().Err(err).Msg("could not get status")
metrics.BoxErrors.Inc()
continue
}
// Sets the metrics.
metrics.Set(uris, s["state"])
}
}()
}
func Run(cmd *cobra.Command, args []string) { func Run(cmd *cobra.Command, args []string) {
log.Info().Msg("starting the RFID reader") log.Info().Msg("starting the RFID reader")
@ -80,6 +129,16 @@ func Run(cmd *cobra.Command, args []string) {
log.Fatal().Err(err).Msg("could not start RFID reader") log.Fatal().Err(err).Msg("could not start RFID reader")
} }
// Create MPD connection on every received event.
c, err := mpd.Dial("tcp", fmt.Sprintf("%s:%d", config.Cfg.MPD.Hostname, config.Cfg.MPD.Port))
if err != nil {
log.Fatal().Err(err).Msg("could not connect to MPD server")
}
m := newMpc(c)
m.watch()
go func() { go func() {
var id string var id string
@ -89,16 +148,6 @@ func Run(cmd *cobra.Command, args []string) {
logger := log.With().Str("id", id).Logger() logger := log.With().Str("id", id).Logger()
logger.Info().Msg("received id") logger.Info().Msg("received id")
// Create MPD connection on every received event.
c, err := mpd.Dial("tcp", fmt.Sprintf("%s:%d", config.Cfg.MPD.Hostname, config.Cfg.MPD.Port))
if err != nil {
logger.Error().Err(err).Msg("could not connect to MPD server")
continue
}
m := newMpc(c)
// Check of stop tag was detected. // Check of stop tag was detected.
if id == config.Cfg.Meta.Stop { if id == config.Cfg.Meta.Stop {
logger.Info().Msg("stopping") logger.Info().Msg("stopping")