github.com/mforkel/docker-ce-i386@v17.12.1-ce-rc2+incompatible/components/engine/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 // Engine 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 either not running or restarting and requires no stream, return an empty stats. 31 if (!container.IsRunning() || container.IsRestarting()) && !config.Stream { 32 return json.NewEncoder(config.OutStream).Encode(&types.StatsJSON{ 33 Name: container.Name, 34 ID: container.ID}) 35 } 36 37 outStream := config.OutStream 38 if config.Stream { 39 wf := ioutils.NewWriteFlusher(outStream) 40 defer wf.Close() 41 wf.Flush() 42 outStream = wf 43 } 44 45 var preCPUStats types.CPUStats 46 var preRead time.Time 47 getStatJSON := func(v interface{}) *types.StatsJSON { 48 ss := v.(types.StatsJSON) 49 ss.Name = container.Name 50 ss.ID = container.ID 51 ss.PreCPUStats = preCPUStats 52 ss.PreRead = preRead 53 preCPUStats = ss.CPUStats 54 preRead = ss.Read 55 return &ss 56 } 57 58 enc := json.NewEncoder(outStream) 59 60 updates := daemon.subscribeToContainerStats(container) 61 defer daemon.unsubscribeToContainerStats(container, updates) 62 63 noStreamFirstFrame := true 64 for { 65 select { 66 case v, ok := <-updates: 67 if !ok { 68 return nil 69 } 70 71 var statsJSON interface{} 72 statsJSONPost120 := getStatJSON(v) 73 if versions.LessThan(apiVersion, "1.21") { 74 if runtime.GOOS == "windows" { 75 return errors.New("API versions pre v1.21 do not support stats on Windows") 76 } 77 var ( 78 rxBytes uint64 79 rxPackets uint64 80 rxErrors uint64 81 rxDropped uint64 82 txBytes uint64 83 txPackets uint64 84 txErrors uint64 85 txDropped uint64 86 ) 87 for _, v := range statsJSONPost120.Networks { 88 rxBytes += v.RxBytes 89 rxPackets += v.RxPackets 90 rxErrors += v.RxErrors 91 rxDropped += v.RxDropped 92 txBytes += v.TxBytes 93 txPackets += v.TxPackets 94 txErrors += v.TxErrors 95 txDropped += v.TxDropped 96 } 97 statsJSON = &v1p20.StatsJSON{ 98 Stats: statsJSONPost120.Stats, 99 Network: types.NetworkStats{ 100 RxBytes: rxBytes, 101 RxPackets: rxPackets, 102 RxErrors: rxErrors, 103 RxDropped: rxDropped, 104 TxBytes: txBytes, 105 TxPackets: txPackets, 106 TxErrors: txErrors, 107 TxDropped: txDropped, 108 }, 109 } 110 } else { 111 statsJSON = statsJSONPost120 112 } 113 114 if !config.Stream && noStreamFirstFrame { 115 // prime the cpu stats so they aren't 0 in the final output 116 noStreamFirstFrame = false 117 continue 118 } 119 120 if err := enc.Encode(statsJSON); err != nil { 121 return err 122 } 123 124 if !config.Stream { 125 return nil 126 } 127 case <-ctx.Done(): 128 return nil 129 } 130 } 131 } 132 133 func (daemon *Daemon) subscribeToContainerStats(c *container.Container) chan interface{} { 134 return daemon.statsCollector.Collect(c) 135 } 136 137 func (daemon *Daemon) unsubscribeToContainerStats(c *container.Container, ch chan interface{}) { 138 daemon.statsCollector.Unsubscribe(c, ch) 139 } 140 141 // GetContainerStats collects all the stats published by a container 142 func (daemon *Daemon) GetContainerStats(container *container.Container) (*types.StatsJSON, error) { 143 stats, err := daemon.stats(container) 144 if err != nil { 145 return nil, err 146 } 147 148 // We already have the network stats on Windows directly from HCS. 149 if !container.Config.NetworkDisabled && runtime.GOOS != "windows" { 150 if stats.Networks, err = daemon.getNetworkStats(container); err != nil { 151 return nil, err 152 } 153 } 154 155 return stats, nil 156 }