blob: 5539a898ca9ef3c082d522f597805421e48d4e8d [file] [log] [blame]
package metrics
import (
"context"
"strings"
"github.com/docker/go-events"
metrics "github.com/docker/go-metrics"
"github.com/docker/swarmkit/api"
"github.com/docker/swarmkit/manager/state/store"
)
var (
ns = metrics.NewNamespace("swarm", "manager", nil)
// counts of the various objects in swarmkit
nodesMetric metrics.LabeledGauge
tasksMetric metrics.LabeledGauge
// none of these objects have state, so they're just regular gauges
servicesMetric metrics.Gauge
networksMetric metrics.Gauge
secretsMetric metrics.Gauge
configsMetric metrics.Gauge
)
func init() {
nodesMetric = ns.NewLabeledGauge("nodes", "The number of nodes", "", "state")
tasksMetric = ns.NewLabeledGauge("tasks", "The number of tasks in the cluster object store", metrics.Total, "state")
servicesMetric = ns.NewGauge("services", "The number of services in the cluster object store", metrics.Total)
networksMetric = ns.NewGauge("networks", "The number of networks in the cluster object store", metrics.Total)
secretsMetric = ns.NewGauge("secrets", "The number of secrets in the cluster object store", metrics.Total)
configsMetric = ns.NewGauge("configs", "The number of configs in the cluster object store", metrics.Total)
resetMetrics()
metrics.Register(ns)
}
// Collector collects swarmkit metrics
type Collector struct {
store *store.MemoryStore
// stopChan signals to the state machine to stop running.
stopChan chan struct{}
// doneChan is closed when the state machine terminates.
doneChan chan struct{}
}
// NewCollector creates a new metrics collector
func NewCollector(store *store.MemoryStore) *Collector {
return &Collector{
store: store,
stopChan: make(chan struct{}),
doneChan: make(chan struct{}),
}
}
// Run contains the collector event loop
func (c *Collector) Run(ctx context.Context) error {
defer close(c.doneChan)
watcher, cancel, err := store.ViewAndWatch(c.store, func(readTx store.ReadTx) error {
nodes, err := store.FindNodes(readTx, store.All)
if err != nil {
return err
}
tasks, err := store.FindTasks(readTx, store.All)
if err != nil {
return err
}
services, err := store.FindServices(readTx, store.All)
if err != nil {
return err
}
networks, err := store.FindNetworks(readTx, store.All)
if err != nil {
return err
}
secrets, err := store.FindSecrets(readTx, store.All)
if err != nil {
return err
}
configs, err := store.FindConfigs(readTx, store.All)
if err != nil {
return err
}
for _, obj := range nodes {
c.handleEvent(obj.EventCreate())
}
for _, obj := range tasks {
c.handleEvent(obj.EventCreate())
}
for _, obj := range services {
c.handleEvent(obj.EventCreate())
}
for _, obj := range networks {
c.handleEvent(obj.EventCreate())
}
for _, obj := range secrets {
c.handleEvent(obj.EventCreate())
}
for _, obj := range configs {
c.handleEvent(obj.EventCreate())
}
return nil
})
if err != nil {
return err
}
defer cancel()
for {
select {
case event := <-watcher:
c.handleEvent(event)
case <-c.stopChan:
return nil
}
}
}
// Stop stops the collector.
func (c *Collector) Stop() {
close(c.stopChan)
<-c.doneChan
// Clean the metrics on exit.
resetMetrics()
}
// resetMetrics resets all metrics to their default (base) value
func resetMetrics() {
for _, state := range api.NodeStatus_State_name {
nodesMetric.WithValues(strings.ToLower(state)).Set(0)
}
for _, state := range api.TaskState_name {
tasksMetric.WithValues(strings.ToLower(state)).Set(0)
}
servicesMetric.Set(0)
networksMetric.Set(0)
secretsMetric.Set(0)
configsMetric.Set(0)
}
// handleEvent handles a single incoming cluster event.
func (c *Collector) handleEvent(event events.Event) {
switch event.(type) {
case api.EventNode:
c.handleNodeEvent(event)
case api.EventTask:
c.handleTaskEvent(event)
case api.EventService:
c.handleServiceEvent(event)
case api.EventNetwork:
c.handleNetworkEvent(event)
case api.EventSecret:
c.handleSecretsEvent(event)
case api.EventConfig:
c.handleConfigsEvent(event)
}
}
func (c *Collector) handleNodeEvent(event events.Event) {
var prevNode, newNode *api.Node
switch v := event.(type) {
case api.EventCreateNode:
prevNode, newNode = nil, v.Node
case api.EventUpdateNode:
prevNode, newNode = v.OldNode, v.Node
case api.EventDeleteNode:
prevNode, newNode = v.Node, nil
}
// Skip updates if nothing changed.
if prevNode != nil && newNode != nil && prevNode.Status.State == newNode.Status.State {
return
}
if prevNode != nil {
nodesMetric.WithValues(strings.ToLower(prevNode.Status.State.String())).Dec(1)
}
if newNode != nil {
nodesMetric.WithValues(strings.ToLower(newNode.Status.State.String())).Inc(1)
}
}
func (c *Collector) handleTaskEvent(event events.Event) {
var prevTask, newTask *api.Task
switch v := event.(type) {
case api.EventCreateTask:
prevTask, newTask = nil, v.Task
case api.EventUpdateTask:
prevTask, newTask = v.OldTask, v.Task
case api.EventDeleteTask:
prevTask, newTask = v.Task, nil
}
// Skip updates if nothing changed.
if prevTask != nil && newTask != nil && prevTask.Status.State == newTask.Status.State {
return
}
if prevTask != nil {
tasksMetric.WithValues(
strings.ToLower(prevTask.Status.State.String()),
).Dec(1)
}
if newTask != nil {
tasksMetric.WithValues(
strings.ToLower(newTask.Status.State.String()),
).Inc(1)
}
}
func (c *Collector) handleServiceEvent(event events.Event) {
switch event.(type) {
case api.EventCreateService:
servicesMetric.Inc(1)
case api.EventDeleteService:
servicesMetric.Dec(1)
}
}
func (c *Collector) handleNetworkEvent(event events.Event) {
switch event.(type) {
case api.EventCreateNetwork:
networksMetric.Inc(1)
case api.EventDeleteNetwork:
networksMetric.Dec(1)
}
}
func (c *Collector) handleSecretsEvent(event events.Event) {
switch event.(type) {
case api.EventCreateSecret:
secretsMetric.Inc(1)
case api.EventDeleteSecret:
secretsMetric.Dec(1)
}
}
func (c *Collector) handleConfigsEvent(event events.Event) {
switch event.(type) {
case api.EventCreateConfig:
configsMetric.Inc(1)
case api.EventDeleteConfig:
configsMetric.Dec(1)
}
}