github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/daemon/stats/collector.go (about) 1 package stats 2 3 import ( 4 "time" 5 6 "github.com/docker/docker/api/types" 7 "github.com/docker/docker/container" 8 "github.com/docker/docker/pkg/pubsub" 9 "github.com/sirupsen/logrus" 10 ) 11 12 // Collect registers the container with the collector and adds it to 13 // the event loop for collection on the specified interval returning 14 // a channel for the subscriber to receive on. 15 func (s *Collector) Collect(c *container.Container) chan interface{} { 16 s.m.Lock() 17 defer s.m.Unlock() 18 publisher, exists := s.publishers[c] 19 if !exists { 20 publisher = pubsub.NewPublisher(100*time.Millisecond, 1024) 21 s.publishers[c] = publisher 22 } 23 return publisher.Subscribe() 24 } 25 26 // StopCollection closes the channels for all subscribers and removes 27 // the container from metrics collection. 28 func (s *Collector) StopCollection(c *container.Container) { 29 s.m.Lock() 30 if publisher, exists := s.publishers[c]; exists { 31 publisher.Close() 32 delete(s.publishers, c) 33 } 34 s.m.Unlock() 35 } 36 37 // Unsubscribe removes a specific subscriber from receiving updates for a container's stats. 38 func (s *Collector) Unsubscribe(c *container.Container, ch chan interface{}) { 39 s.m.Lock() 40 publisher := s.publishers[c] 41 if publisher != nil { 42 publisher.Evict(ch) 43 if publisher.Len() == 0 { 44 delete(s.publishers, c) 45 } 46 } 47 s.m.Unlock() 48 } 49 50 // Run starts the collectors and will indefinitely collect stats from the supervisor 51 func (s *Collector) Run() { 52 type publishersPair struct { 53 container *container.Container 54 publisher *pubsub.Publisher 55 } 56 // we cannot determine the capacity here. 57 // it will grow enough in first iteration 58 var pairs []publishersPair 59 60 for range time.Tick(s.interval) { 61 // it does not make sense in the first iteration, 62 // but saves allocations in further iterations 63 pairs = pairs[:0] 64 65 s.m.Lock() 66 for container, publisher := range s.publishers { 67 // copy pointers here to release the lock ASAP 68 pairs = append(pairs, publishersPair{container, publisher}) 69 } 70 s.m.Unlock() 71 if len(pairs) == 0 { 72 continue 73 } 74 75 systemUsage, err := s.getSystemCPUUsage() 76 if err != nil { 77 logrus.Errorf("collecting system cpu usage: %v", err) 78 continue 79 } 80 81 onlineCPUs, err := s.getNumberOnlineCPUs() 82 if err != nil { 83 logrus.Errorf("collecting system online cpu count: %v", err) 84 continue 85 } 86 87 for _, pair := range pairs { 88 stats, err := s.supervisor.GetContainerStats(pair.container) 89 90 switch err.(type) { 91 case nil: 92 // FIXME: move to containerd on Linux (not Windows) 93 stats.CPUStats.SystemUsage = systemUsage 94 stats.CPUStats.OnlineCPUs = onlineCPUs 95 96 pair.publisher.Publish(*stats) 97 98 case notRunningErr, notFoundErr: 99 // publish empty stats containing only name and ID if not running or not found 100 pair.publisher.Publish(types.StatsJSON{ 101 Name: pair.container.Name, 102 ID: pair.container.ID, 103 }) 104 105 default: 106 logrus.Errorf("collecting stats for %s: %v", pair.container.ID, err) 107 } 108 } 109 } 110 } 111 112 type notRunningErr interface { 113 error 114 Conflict() 115 } 116 117 type notFoundErr interface { 118 error 119 NotFound() 120 }