github.com/campoy/docker@v1.8.0-rc1/api/client/stats.go (about)

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