github.com/guilhermebr/docker@v1.4.2-0.20150428121140-67da055cebca/api/client/stats.go (about)

     1  package client
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"sort"
     8  	"strings"
     9  	"sync"
    10  	"text/tabwriter"
    11  	"time"
    12  
    13  	"github.com/docker/docker/api/types"
    14  	flag "github.com/docker/docker/pkg/mflag"
    15  	"github.com/docker/docker/pkg/units"
    16  )
    17  
    18  type containerStats struct {
    19  	Name             string
    20  	CPUPercentage    float64
    21  	Memory           float64
    22  	MemoryLimit      float64
    23  	MemoryPercentage float64
    24  	NetworkRx        float64
    25  	NetworkTx        float64
    26  	mu               sync.RWMutex
    27  	err              error
    28  }
    29  
    30  func (s *containerStats) Collect(cli *DockerCli) {
    31  	stream, _, err := cli.call("GET", "/containers/"+s.Name+"/stats", nil, nil)
    32  	if err != nil {
    33  		s.err = err
    34  		return
    35  	}
    36  	defer stream.Close()
    37  	var (
    38  		previousCPU    uint64
    39  		previousSystem uint64
    40  		start          = true
    41  		dec            = json.NewDecoder(stream)
    42  		u              = make(chan error, 1)
    43  	)
    44  	go func() {
    45  		for {
    46  			var v *types.Stats
    47  			if err := dec.Decode(&v); err != nil {
    48  				u <- err
    49  				return
    50  			}
    51  			var (
    52  				memPercent = float64(v.MemoryStats.Usage) / float64(v.MemoryStats.Limit) * 100.0
    53  				cpuPercent = 0.0
    54  			)
    55  			if !start {
    56  				cpuPercent = calculateCPUPercent(previousCPU, previousSystem, v)
    57  			}
    58  			start = false
    59  			s.mu.Lock()
    60  			s.CPUPercentage = cpuPercent
    61  			s.Memory = float64(v.MemoryStats.Usage)
    62  			s.MemoryLimit = float64(v.MemoryStats.Limit)
    63  			s.MemoryPercentage = memPercent
    64  			s.NetworkRx = float64(v.Network.RxBytes)
    65  			s.NetworkTx = float64(v.Network.TxBytes)
    66  			s.mu.Unlock()
    67  			previousCPU = v.CpuStats.CpuUsage.TotalUsage
    68  			previousSystem = v.CpuStats.SystemUsage
    69  			u <- nil
    70  		}
    71  	}()
    72  	for {
    73  		select {
    74  		case <-time.After(2 * time.Second):
    75  			// zero out the values if we have not received an update within
    76  			// the specified duration.
    77  			s.mu.Lock()
    78  			s.CPUPercentage = 0
    79  			s.Memory = 0
    80  			s.MemoryPercentage = 0
    81  			s.mu.Unlock()
    82  		case err := <-u:
    83  			if err != nil {
    84  				s.mu.Lock()
    85  				s.err = err
    86  				s.mu.Unlock()
    87  				return
    88  			}
    89  		}
    90  	}
    91  }
    92  
    93  func (s *containerStats) Display(w io.Writer) error {
    94  	s.mu.RLock()
    95  	defer s.mu.RUnlock()
    96  	if s.err != nil {
    97  		return s.err
    98  	}
    99  	fmt.Fprintf(w, "%s\t%.2f%%\t%s/%s\t%.2f%%\t%s/%s\n",
   100  		s.Name,
   101  		s.CPUPercentage,
   102  		units.HumanSize(s.Memory), units.HumanSize(s.MemoryLimit),
   103  		s.MemoryPercentage,
   104  		units.HumanSize(s.NetworkRx), units.HumanSize(s.NetworkTx))
   105  	return nil
   106  }
   107  
   108  // CmdStats displays a live stream of resource usage statistics for one or more containers.
   109  //
   110  // This shows real-time information on CPU usage, memory usage, and network I/O.
   111  //
   112  // Usage: docker stats CONTAINER [CONTAINER...]
   113  func (cli *DockerCli) CmdStats(args ...string) error {
   114  	cmd := cli.Subcmd("stats", "CONTAINER [CONTAINER...]", "Display a live stream of one or more containers' resource usage statistics", true)
   115  	cmd.Require(flag.Min, 1)
   116  	cmd.ParseFlags(args, true)
   117  
   118  	names := cmd.Args()
   119  	sort.Strings(names)
   120  	var (
   121  		cStats []*containerStats
   122  		w      = tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
   123  	)
   124  	printHeader := func() {
   125  		io.WriteString(cli.out, "\033[2J")
   126  		io.WriteString(cli.out, "\033[H")
   127  		io.WriteString(w, "CONTAINER\tCPU %\tMEM USAGE/LIMIT\tMEM %\tNET I/O\n")
   128  	}
   129  	for _, n := range names {
   130  		s := &containerStats{Name: n}
   131  		cStats = append(cStats, s)
   132  		go s.Collect(cli)
   133  	}
   134  	// do a quick pause so that any failed connections for containers that do not exist are able to be
   135  	// evicted before we display the initial or default values.
   136  	time.Sleep(500 * time.Millisecond)
   137  	var errs []string
   138  	for _, c := range cStats {
   139  		c.mu.Lock()
   140  		if c.err != nil {
   141  			errs = append(errs, fmt.Sprintf("%s: %v", c.Name, c.err))
   142  		}
   143  		c.mu.Unlock()
   144  	}
   145  	if len(errs) > 0 {
   146  		return fmt.Errorf("%s", strings.Join(errs, ", "))
   147  	}
   148  	for range time.Tick(500 * time.Millisecond) {
   149  		printHeader()
   150  		toRemove := []int{}
   151  		for i, s := range cStats {
   152  			if err := s.Display(w); err != nil {
   153  				toRemove = append(toRemove, i)
   154  			}
   155  		}
   156  		for j := len(toRemove) - 1; j >= 0; j-- {
   157  			i := toRemove[j]
   158  			cStats = append(cStats[:i], cStats[i+1:]...)
   159  		}
   160  		if len(cStats) == 0 {
   161  			return nil
   162  		}
   163  		w.Flush()
   164  	}
   165  	return nil
   166  }
   167  
   168  func calculateCPUPercent(previousCPU, previousSystem uint64, v *types.Stats) float64 {
   169  	var (
   170  		cpuPercent = 0.0
   171  		// calculate the change for the cpu usage of the container in between readings
   172  		cpuDelta = float64(v.CpuStats.CpuUsage.TotalUsage - previousCPU)
   173  		// calculate the change for the entire system between readings
   174  		systemDelta = float64(v.CpuStats.SystemUsage - previousSystem)
   175  	)
   176  
   177  	if systemDelta > 0.0 && cpuDelta > 0.0 {
   178  		cpuPercent = (cpuDelta / systemDelta) * float64(len(v.CpuStats.CpuUsage.PercpuUsage)) * 100.0
   179  	}
   180  	return cpuPercent
   181  }