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) } }