RunStats displays a live stream of resource usage statistics for one or more containers. This shows real-time information on CPU usage, memory usage, and network I/O. nolint:gocyclo
(ctx context.Context, dockerCLI command.Cli, options *StatsOptions)
| 107 | // |
| 108 | //nolint:gocyclo |
| 109 | func RunStats(ctx context.Context, dockerCLI command.Cli, options *StatsOptions) error { |
| 110 | apiClient := dockerCLI.Client() |
| 111 | |
| 112 | // Get the daemonOSType to handle platform-specific stats fields. |
| 113 | // This value is used as a fallback for docker < v29, which did not |
| 114 | // include the OSType field per stats. |
| 115 | daemonOSType = dockerCLI.ServerInfo().OSType |
| 116 | |
| 117 | // waitFirst is a WaitGroup to wait first stat data's reach for each container |
| 118 | waitFirst := &sync.WaitGroup{} |
| 119 | // closeChan is used to collect errors from goroutines. It uses a small buffer |
| 120 | // to avoid blocking sends when sends occur after closeChan is set to nil or |
| 121 | // after the reader has exited, preventing deadlocks. |
| 122 | closeChan := make(chan error, 4) |
| 123 | cStats := stats{} |
| 124 | |
| 125 | showAll := len(options.Containers) == 0 |
| 126 | if showAll { |
| 127 | // If no names were specified, start a long-running goroutine which |
| 128 | // monitors container events. We make sure we're subscribed before |
| 129 | // retrieving the list of running containers to avoid a race where we |
| 130 | // would "miss" a creation. |
| 131 | started := make(chan struct{}) |
| 132 | |
| 133 | if options.Filters == nil { |
| 134 | options.Filters = make(client.Filters) |
| 135 | } |
| 136 | |
| 137 | // FIXME(thaJeztah): any way we can (and should?) validate allowed filters? |
| 138 | for filter := range options.Filters { |
| 139 | if _, ok := acceptedStatsFilters[filter]; !ok { |
| 140 | return errdefs.ErrInvalidArgument.WithMessage("invalid filter '" + filter + "'") |
| 141 | } |
| 142 | } |
| 143 | |
| 144 | eh := newEventHandler() |
| 145 | addEvents := []events.Action{events.ActionStart} |
| 146 | if options.All { |
| 147 | addEvents = append(addEvents, events.ActionCreate) |
| 148 | } |
| 149 | eh.setHandler(addEvents, func(ctx context.Context, e events.Message) { |
| 150 | if s := NewStats(e.Actor.ID); cStats.add(s) { |
| 151 | waitFirst.Add(1) |
| 152 | log.G(ctx).Debug("collecting stats for container") |
| 153 | go collect(ctx, s, apiClient, !options.NoStream, waitFirst) |
| 154 | } |
| 155 | }) |
| 156 | |
| 157 | // Remove containers when they are removed ("destroyed"); containers |
| 158 | // do not emit [events.ActionRemove], only [events.ActionDestroy]. |
| 159 | // |
| 160 | // When running with "--all" we don't remove containers when they die, |
| 161 | // because they may come back, but without "--all" we remove them |
| 162 | // on the first possible occasion (either "die" or "destroy"). |
| 163 | rmEvents := []events.Action{events.ActionDestroy} |
| 164 | if !options.All { |
| 165 | rmEvents = append(rmEvents, events.ActionDie) |
| 166 | } |
no test coverage detected
searching dependent graphs…