github.com/psychoss/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 }