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