@@ -21,6 +21,7 @@ import (
2121 "fmt"
2222 "os"
2323 "os/signal"
24+ "slices"
2425 "sync/atomic"
2526 "syscall"
2627
@@ -34,6 +35,7 @@ import (
3435 "github.com/eiannone/keyboard"
3536 "github.com/hashicorp/go-multierror"
3637 "github.com/sirupsen/logrus"
38+ "golang.org/x/sync/errgroup"
3739)
3840
3941func (s * composeService ) Up (ctx context.Context , project * types.Project , options api.UpOptions ) error { //nolint:gocyclo
@@ -205,29 +207,44 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options
205207 })
206208 }
207209
210+ // use an independent context tied to the errgroup for background attach operations
211+ // the primary context is still used for other operations
212+ // this means that once any attach operation fails, all other attaches are cancelled,
213+ // but an attach failing won't interfere with the rest of the start
214+ _ , attachCtx := errgroup .WithContext (ctx )
215+ containers , err := s .attach (attachCtx , project , printer .HandleEvent , options .Start .AttachTo )
216+ if err != nil {
217+ return err
218+ }
219+ attached := make ([]string , len (containers ))
220+ for i , ctr := range containers {
221+ attached [i ] = ctr .ID
222+ }
223+
208224 monitor .withListener (func (event api.ContainerEvent ) {
209225 if event .Type != api .ContainerEventStarted {
210226 return
211227 }
212- if event .Restarting || event .Container .Labels [api .ContainerReplaceLabel ] != "" {
213- eg .Go (func () error {
214- ctr , err := s .apiClient ().ContainerInspect (ctx , event .ID )
215- if err != nil {
216- return err
217- }
218-
219- err = s .doLogContainer (ctx , options .Start .Attach , event .Source , ctr , api.LogOptions {
220- Follow : true ,
221- Since : ctr .State .StartedAt ,
222- })
223- if errdefs .IsNotImplemented (err ) {
224- // container may be configured with logging_driver: none
225- // as container already started, we might miss the very first logs. But still better than none
226- return s .doAttachContainer (ctx , event .Service , event .ID , event .Source , printer .HandleEvent )
227- }
228+ if slices .Contains (attached , event .ID ) {
229+ return
230+ }
231+ eg .Go (func () error {
232+ ctr , err := s .apiClient ().ContainerInspect (ctx , event .ID )
233+ if err != nil {
228234 return err
235+ }
236+
237+ err = s .doLogContainer (ctx , options .Start .Attach , event .Source , ctr , api.LogOptions {
238+ Follow : true ,
239+ Since : ctr .State .StartedAt ,
229240 })
230- }
241+ if errdefs .IsNotImplemented (err ) {
242+ // container may be configured with logging_driver: none
243+ // as container already started, we might miss the very first logs. But still better than none
244+ return s .doAttachContainer (ctx , event .Service , event .ID , event .Source , printer .HandleEvent )
245+ }
246+ return err
247+ })
231248 })
232249
233250 eg .Go (func () error {
0 commit comments