github.com/sevki/docker@v1.7.1/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/libcontainer" 10 "github.com/docker/libcontainer/cgroups" 11 "github.com/docker/libnetwork/sandbox" 12 ) 13 14 func (daemon *Daemon) ContainerStats(name string, stream bool, out io.Writer) error { 15 updates, err := daemon.SubscribeToContainerStats(name) 16 if err != nil { 17 return err 18 } 19 20 var preCpuStats types.CpuStats 21 getStat := func(v interface{}) *types.Stats { 22 update := v.(*execdriver.ResourceStats) 23 // Retrieve the nw statistics from libnetwork and inject them in the Stats 24 if nwStats, err := daemon.getNetworkStats(name); err == nil { 25 update.Stats.Interfaces = nwStats 26 } 27 ss := convertToAPITypes(update.Stats) 28 ss.PreCpuStats = preCpuStats 29 ss.MemoryStats.Limit = uint64(update.MemoryLimit) 30 ss.Read = update.Read 31 ss.CpuStats.SystemUsage = update.SystemUsage 32 preCpuStats = ss.CpuStats 33 return ss 34 } 35 36 enc := json.NewEncoder(out) 37 38 if !stream { 39 // prime the cpu stats so they aren't 0 in the final output 40 s := getStat(<-updates) 41 42 // now pull stats again with the cpu stats primed 43 s = getStat(<-updates) 44 err := enc.Encode(s) 45 daemon.UnsubscribeToContainerStats(name, updates) 46 return err 47 } 48 49 for v := range updates { 50 s := getStat(v) 51 if err := enc.Encode(s); err != nil { 52 // TODO: handle the specific broken pipe 53 daemon.UnsubscribeToContainerStats(name, updates) 54 return err 55 } 56 } 57 return nil 58 } 59 60 // convertToAPITypes converts the libcontainer.Stats to the api specific 61 // structs. This is done to preserve API compatibility and versioning. 62 func convertToAPITypes(ls *libcontainer.Stats) *types.Stats { 63 s := &types.Stats{} 64 if ls.Interfaces != nil { 65 s.Network = types.Network{} 66 for _, iface := range ls.Interfaces { 67 s.Network.RxBytes += iface.RxBytes 68 s.Network.RxPackets += iface.RxPackets 69 s.Network.RxErrors += iface.RxErrors 70 s.Network.RxDropped += iface.RxDropped 71 s.Network.TxBytes += iface.TxBytes 72 s.Network.TxPackets += iface.TxPackets 73 s.Network.TxErrors += iface.TxErrors 74 s.Network.TxDropped += iface.TxDropped 75 } 76 } 77 cs := ls.CgroupStats 78 if cs != nil { 79 s.BlkioStats = types.BlkioStats{ 80 IoServiceBytesRecursive: copyBlkioEntry(cs.BlkioStats.IoServiceBytesRecursive), 81 IoServicedRecursive: copyBlkioEntry(cs.BlkioStats.IoServicedRecursive), 82 IoQueuedRecursive: copyBlkioEntry(cs.BlkioStats.IoQueuedRecursive), 83 IoServiceTimeRecursive: copyBlkioEntry(cs.BlkioStats.IoServiceTimeRecursive), 84 IoWaitTimeRecursive: copyBlkioEntry(cs.BlkioStats.IoWaitTimeRecursive), 85 IoMergedRecursive: copyBlkioEntry(cs.BlkioStats.IoMergedRecursive), 86 IoTimeRecursive: copyBlkioEntry(cs.BlkioStats.IoTimeRecursive), 87 SectorsRecursive: copyBlkioEntry(cs.BlkioStats.SectorsRecursive), 88 } 89 cpu := cs.CpuStats 90 s.CpuStats = types.CpuStats{ 91 CpuUsage: types.CpuUsage{ 92 TotalUsage: cpu.CpuUsage.TotalUsage, 93 PercpuUsage: cpu.CpuUsage.PercpuUsage, 94 UsageInKernelmode: cpu.CpuUsage.UsageInKernelmode, 95 UsageInUsermode: cpu.CpuUsage.UsageInUsermode, 96 }, 97 ThrottlingData: types.ThrottlingData{ 98 Periods: cpu.ThrottlingData.Periods, 99 ThrottledPeriods: cpu.ThrottlingData.ThrottledPeriods, 100 ThrottledTime: cpu.ThrottlingData.ThrottledTime, 101 }, 102 } 103 mem := cs.MemoryStats 104 s.MemoryStats = types.MemoryStats{ 105 Usage: mem.Usage, 106 MaxUsage: mem.MaxUsage, 107 Stats: mem.Stats, 108 Failcnt: mem.Failcnt, 109 } 110 } 111 return s 112 } 113 114 func copyBlkioEntry(entries []cgroups.BlkioStatEntry) []types.BlkioStatEntry { 115 out := make([]types.BlkioStatEntry, len(entries)) 116 for i, re := range entries { 117 out[i] = types.BlkioStatEntry{ 118 Major: re.Major, 119 Minor: re.Minor, 120 Op: re.Op, 121 Value: re.Value, 122 } 123 } 124 return out 125 } 126 127 func (daemon *Daemon) getNetworkStats(name string) ([]*libcontainer.NetworkInterface, error) { 128 var list []*libcontainer.NetworkInterface 129 130 c, err := daemon.Get(name) 131 if err != nil { 132 return list, err 133 } 134 135 nw, err := daemon.netController.NetworkByID(c.NetworkSettings.NetworkID) 136 if err != nil { 137 return list, err 138 } 139 ep, err := nw.EndpointByID(c.NetworkSettings.EndpointID) 140 if err != nil { 141 return list, err 142 } 143 144 stats, err := ep.Statistics() 145 if err != nil { 146 return list, err 147 } 148 149 // Convert libnetwork nw stats into libcontainer nw stats 150 for ifName, ifStats := range stats { 151 list = append(list, convertLnNetworkStats(ifName, ifStats)) 152 } 153 154 return list, nil 155 } 156 157 func convertLnNetworkStats(name string, stats *sandbox.InterfaceStatistics) *libcontainer.NetworkInterface { 158 n := &libcontainer.NetworkInterface{Name: name} 159 n.RxBytes = stats.RxBytes 160 n.RxPackets = stats.RxPackets 161 n.RxErrors = stats.RxErrors 162 n.RxDropped = stats.RxDropped 163 n.TxBytes = stats.TxBytes 164 n.TxPackets = stats.TxPackets 165 n.TxErrors = stats.TxErrors 166 n.TxDropped = stats.TxDropped 167 return n 168 }