github.com/jiasir/docker@v1.3.3-0.20170609024000-252e610103e7/daemon/stats.go (about) 1 package daemon 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "runtime" 8 "time" 9 10 "golang.org/x/net/context" 11 12 "github.com/docker/docker/api/types" 13 "github.com/docker/docker/api/types/backend" 14 "github.com/docker/docker/api/types/versions" 15 "github.com/docker/docker/api/types/versions/v1p20" 16 "github.com/docker/docker/container" 17 "github.com/docker/docker/pkg/ioutils" 18 ) 19 20 // ContainerStats writes information about the container to the stream 21 // given in the config object. 22 func (daemon *Daemon) ContainerStats(ctx context.Context, prefixOrName string, config *backend.ContainerStatsConfig) error { 23 if runtime.GOOS == "solaris" { 24 return fmt.Errorf("%+v does not support stats", runtime.GOOS) 25 } 26 // Engine API version (used for backwards compatibility) 27 apiVersion := config.Version 28 29 container, err := daemon.GetContainer(prefixOrName) 30 if err != nil { 31 return err 32 } 33 34 // If the container is either not running or restarting and requires no stream, return an empty stats. 35 if (!container.IsRunning() || container.IsRestarting()) && !config.Stream { 36 return json.NewEncoder(config.OutStream).Encode(&types.StatsJSON{ 37 Name: container.Name, 38 ID: container.ID}) 39 } 40 41 outStream := config.OutStream 42 if config.Stream { 43 wf := ioutils.NewWriteFlusher(outStream) 44 defer wf.Close() 45 wf.Flush() 46 outStream = wf 47 } 48 49 var preCPUStats types.CPUStats 50 var preRead time.Time 51 getStatJSON := func(v interface{}) *types.StatsJSON { 52 ss := v.(types.StatsJSON) 53 ss.Name = container.Name 54 ss.ID = container.ID 55 ss.PreCPUStats = preCPUStats 56 ss.PreRead = preRead 57 preCPUStats = ss.CPUStats 58 preRead = ss.Read 59 return &ss 60 } 61 62 enc := json.NewEncoder(outStream) 63 64 updates := daemon.subscribeToContainerStats(container) 65 defer daemon.unsubscribeToContainerStats(container, updates) 66 67 noStreamFirstFrame := true 68 for { 69 select { 70 case v, ok := <-updates: 71 if !ok { 72 return nil 73 } 74 75 var statsJSON interface{} 76 statsJSONPost120 := getStatJSON(v) 77 if versions.LessThan(apiVersion, "1.21") { 78 if runtime.GOOS == "windows" { 79 return errors.New("API versions pre v1.21 do not support stats on Windows") 80 } 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 <-ctx.Done(): 132 return nil 133 } 134 } 135 } 136 137 func (daemon *Daemon) subscribeToContainerStats(c *container.Container) chan interface{} { 138 return daemon.statsCollector.Collect(c) 139 } 140 141 func (daemon *Daemon) unsubscribeToContainerStats(c *container.Container, ch chan interface{}) { 142 daemon.statsCollector.Unsubscribe(c, ch) 143 } 144 145 // GetContainerStats collects all the stats published by a container 146 func (daemon *Daemon) GetContainerStats(container *container.Container) (*types.StatsJSON, error) { 147 stats, err := daemon.stats(container) 148 if err != nil { 149 return nil, err 150 } 151 152 // We already have the network stats on Windows directly from HCS. 153 if !container.Config.NetworkDisabled && runtime.GOOS != "windows" { 154 if stats.Networks, err = daemon.getNetworkStats(container); err != nil { 155 return nil, err 156 } 157 } 158 159 return stats, nil 160 }