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