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  }