github.com/damirazo/docker@v1.9.0/daemon/stats.go (about)

     1  package daemon
     2  
     3  import (
     4  	"encoding/json"
     5  	"io"
     6  
     7  	"github.com/docker/docker/api/types"
     8  	"github.com/docker/docker/api/types/versions/v1p20"
     9  	"github.com/docker/docker/daemon/execdriver"
    10  	"github.com/docker/docker/pkg/version"
    11  	lntypes "github.com/docker/libnetwork/types"
    12  	"github.com/opencontainers/runc/libcontainer"
    13  )
    14  
    15  // ContainerStatsConfig holds information for configuring the runtime
    16  // behavior of a daemon.ContainerStats() call.
    17  type ContainerStatsConfig struct {
    18  	Stream    bool
    19  	OutStream io.Writer
    20  	Stop      <-chan bool
    21  	Version   version.Version
    22  }
    23  
    24  // ContainerStats writes information about the container to the stream
    25  // given in the config object.
    26  func (daemon *Daemon) ContainerStats(prefixOrName string, config *ContainerStatsConfig) error {
    27  
    28  	container, err := daemon.Get(prefixOrName)
    29  	if err != nil {
    30  		return err
    31  	}
    32  
    33  	// If the container is not running and requires no stream, return an empty stats.
    34  	if !container.IsRunning() && !config.Stream {
    35  		return json.NewEncoder(config.OutStream).Encode(&types.Stats{})
    36  	}
    37  
    38  	updates, err := daemon.subscribeToContainerStats(container)
    39  	if err != nil {
    40  		return err
    41  	}
    42  
    43  	if config.Stream {
    44  		// Write an empty chunk of data.
    45  		// This is to ensure that the HTTP status code is sent immediately,
    46  		// even if the container has not yet produced any data.
    47  		config.OutStream.Write(nil)
    48  	}
    49  
    50  	var preCPUStats types.CPUStats
    51  	getStatJSON := func(v interface{}) *types.StatsJSON {
    52  		update := v.(*execdriver.ResourceStats)
    53  		// Retrieve the nw statistics from libnetwork and inject them in the Stats
    54  		if nwStats, err := daemon.getNetworkStats(container); err == nil {
    55  			update.Stats.Interfaces = nwStats
    56  		}
    57  		ss := convertStatsToAPITypes(update.Stats)
    58  		ss.PreCPUStats = preCPUStats
    59  		ss.MemoryStats.Limit = uint64(update.MemoryLimit)
    60  		ss.Read = update.Read
    61  		ss.CPUStats.SystemUsage = update.SystemUsage
    62  		preCPUStats = ss.CPUStats
    63  		return ss
    64  	}
    65  
    66  	enc := json.NewEncoder(config.OutStream)
    67  
    68  	defer daemon.unsubscribeToContainerStats(container, updates)
    69  
    70  	noStreamFirstFrame := true
    71  	for {
    72  		select {
    73  		case v, ok := <-updates:
    74  			if !ok {
    75  				return nil
    76  			}
    77  
    78  			var statsJSON interface{}
    79  			statsJSONPost120 := getStatJSON(v)
    80  			if config.Version.LessThan("1.21") {
    81  				var (
    82  					rxBytes   uint64
    83  					rxPackets uint64
    84  					rxErrors  uint64
    85  					rxDropped uint64
    86  					txBytes   uint64
    87  					txPackets uint64
    88  					txErrors  uint64
    89  					txDropped uint64
    90  				)
    91  				for _, v := range statsJSONPost120.Networks {
    92  					rxBytes += v.RxBytes
    93  					rxPackets += v.RxPackets
    94  					rxErrors += v.RxErrors
    95  					rxDropped += v.RxDropped
    96  					txBytes += v.TxBytes
    97  					txPackets += v.TxPackets
    98  					txErrors += v.TxErrors
    99  					txDropped += v.TxDropped
   100  				}
   101  				statsJSON = &v1p20.StatsJSON{
   102  					Stats: statsJSONPost120.Stats,
   103  					Network: types.NetworkStats{
   104  						RxBytes:   rxBytes,
   105  						RxPackets: rxPackets,
   106  						RxErrors:  rxErrors,
   107  						RxDropped: rxDropped,
   108  						TxBytes:   txBytes,
   109  						TxPackets: txPackets,
   110  						TxErrors:  txErrors,
   111  						TxDropped: txDropped,
   112  					},
   113  				}
   114  			} else {
   115  				statsJSON = statsJSONPost120
   116  			}
   117  
   118  			if !config.Stream && noStreamFirstFrame {
   119  				// prime the cpu stats so they aren't 0 in the final output
   120  				noStreamFirstFrame = false
   121  				continue
   122  			}
   123  
   124  			if err := enc.Encode(statsJSON); err != nil {
   125  				return err
   126  			}
   127  
   128  			if !config.Stream {
   129  				return nil
   130  			}
   131  		case <-config.Stop:
   132  			return nil
   133  		}
   134  	}
   135  }
   136  
   137  func (daemon *Daemon) getNetworkStats(c *Container) ([]*libcontainer.NetworkInterface, error) {
   138  	var list []*libcontainer.NetworkInterface
   139  
   140  	sb, err := daemon.netController.SandboxByID(c.NetworkSettings.SandboxID)
   141  	if err != nil {
   142  		return list, err
   143  	}
   144  
   145  	stats, err := sb.Statistics()
   146  	if err != nil {
   147  		return list, err
   148  	}
   149  
   150  	// Convert libnetwork nw stats into libcontainer nw stats
   151  	for ifName, ifStats := range stats {
   152  		list = append(list, convertLnNetworkStats(ifName, ifStats))
   153  	}
   154  
   155  	return list, nil
   156  }
   157  
   158  func convertLnNetworkStats(name string, stats *lntypes.InterfaceStatistics) *libcontainer.NetworkInterface {
   159  	n := &libcontainer.NetworkInterface{Name: name}
   160  	n.RxBytes = stats.RxBytes
   161  	n.RxPackets = stats.RxPackets
   162  	n.RxErrors = stats.RxErrors
   163  	n.RxDropped = stats.RxDropped
   164  	n.TxBytes = stats.TxBytes
   165  	n.TxPackets = stats.TxPackets
   166  	n.TxErrors = stats.TxErrors
   167  	n.TxDropped = stats.TxDropped
   168  	return n
   169  }