github.com/kim0/docker@v0.6.2-0.20161130212042-4addda3f07e7/daemon/stats.go (about) 1 package daemon 2 3 import ( 4 "encoding/json" 5 "errors" 6 "runtime" 7 "time" 8 9 "golang.org/x/net/context" 10 11 "github.com/docker/docker/api/types" 12 "github.com/docker/docker/api/types/backend" 13 "github.com/docker/docker/api/types/versions" 14 "github.com/docker/docker/api/types/versions/v1p20" 15 "github.com/docker/docker/container" 16 "github.com/docker/docker/pkg/ioutils" 17 ) 18 19 // ContainerStats writes information about the container to the stream 20 // given in the config object. 21 func (daemon *Daemon) ContainerStats(ctx context.Context, prefixOrName string, config *backend.ContainerStatsConfig) error { 22 // Remote API version (used for backwards compatibility) 23 apiVersion := config.Version 24 25 container, err := daemon.GetContainer(prefixOrName) 26 if err != nil { 27 return err 28 } 29 30 // If the container is not running and requires no stream, return an empty stats. 31 if !container.IsRunning() && !config.Stream { 32 return json.NewEncoder(config.OutStream).Encode(&types.Stats{}) 33 } 34 35 outStream := config.OutStream 36 if config.Stream { 37 wf := ioutils.NewWriteFlusher(outStream) 38 defer wf.Close() 39 wf.Flush() 40 outStream = wf 41 } 42 43 var preCPUStats types.CPUStats 44 var preRead time.Time 45 getStatJSON := func(v interface{}) *types.StatsJSON { 46 ss := v.(types.StatsJSON) 47 ss.PreCPUStats = preCPUStats 48 ss.PreRead = preRead 49 preCPUStats = ss.CPUStats 50 preRead = ss.Read 51 return &ss 52 } 53 54 enc := json.NewEncoder(outStream) 55 56 updates := daemon.subscribeToContainerStats(container) 57 defer daemon.unsubscribeToContainerStats(container, updates) 58 59 noStreamFirstFrame := true 60 for { 61 select { 62 case v, ok := <-updates: 63 if !ok { 64 return nil 65 } 66 67 var statsJSON interface{} 68 statsJSONPost120 := getStatJSON(v) 69 if versions.LessThan(apiVersion, "1.21") { 70 if runtime.GOOS == "windows" { 71 return errors.New("API versions pre v1.21 do not support stats on Windows") 72 } 73 var ( 74 rxBytes uint64 75 rxPackets uint64 76 rxErrors uint64 77 rxDropped uint64 78 txBytes uint64 79 txPackets uint64 80 txErrors uint64 81 txDropped uint64 82 ) 83 for _, v := range statsJSONPost120.Networks { 84 rxBytes += v.RxBytes 85 rxPackets += v.RxPackets 86 rxErrors += v.RxErrors 87 rxDropped += v.RxDropped 88 txBytes += v.TxBytes 89 txPackets += v.TxPackets 90 txErrors += v.TxErrors 91 txDropped += v.TxDropped 92 } 93 statsJSON = &v1p20.StatsJSON{ 94 Stats: statsJSONPost120.Stats, 95 Network: types.NetworkStats{ 96 RxBytes: rxBytes, 97 RxPackets: rxPackets, 98 RxErrors: rxErrors, 99 RxDropped: rxDropped, 100 TxBytes: txBytes, 101 TxPackets: txPackets, 102 TxErrors: txErrors, 103 TxDropped: txDropped, 104 }, 105 } 106 } else { 107 statsJSON = statsJSONPost120 108 } 109 110 if !config.Stream && noStreamFirstFrame { 111 // prime the cpu stats so they aren't 0 in the final output 112 noStreamFirstFrame = false 113 continue 114 } 115 116 if err := enc.Encode(statsJSON); err != nil { 117 return err 118 } 119 120 if !config.Stream { 121 return nil 122 } 123 case <-ctx.Done(): 124 return nil 125 } 126 } 127 } 128 129 func (daemon *Daemon) subscribeToContainerStats(c *container.Container) chan interface{} { 130 return daemon.statsCollector.collect(c) 131 } 132 133 func (daemon *Daemon) unsubscribeToContainerStats(c *container.Container, ch chan interface{}) { 134 daemon.statsCollector.unsubscribe(c, ch) 135 } 136 137 // GetContainerStats collects all the stats published by a container 138 func (daemon *Daemon) GetContainerStats(container *container.Container) (*types.StatsJSON, error) { 139 stats, err := daemon.stats(container) 140 if err != nil { 141 return nil, err 142 } 143 144 // We already have the network stats on Windows directly from HCS. 145 if !container.Config.NetworkDisabled && runtime.GOOS != "windows" { 146 if stats.Networks, err = daemon.getNetworkStats(container); err != nil { 147 return nil, err 148 } 149 } 150 151 return stats, nil 152 }