Skip to content

Commit d78b586

Browse files
committed
keep containers attached on stop to capture termination logs
Signed-off-by: Nicolas De Loof <[email protected]>
1 parent 7cf7c64 commit d78b586

File tree

18 files changed

+368
-83
lines changed

18 files changed

+368
-83
lines changed

cmd/formatter/ansi.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,73 +28,73 @@ func ansi(code string) string {
2828
return fmt.Sprintf("\033%s", code)
2929
}
3030

31-
func SaveCursor() {
31+
func saveCursor() {
3232
if disableAnsi {
3333
return
3434
}
3535
fmt.Print(ansi("7"))
3636
}
3737

38-
func RestoreCursor() {
38+
func restoreCursor() {
3939
if disableAnsi {
4040
return
4141
}
4242
fmt.Print(ansi("8"))
4343
}
4444

45-
func HideCursor() {
45+
func hideCursor() {
4646
if disableAnsi {
4747
return
4848
}
4949
fmt.Print(ansi("[?25l"))
5050
}
5151

52-
func ShowCursor() {
52+
func showCursor() {
5353
if disableAnsi {
5454
return
5555
}
5656
fmt.Print(ansi("[?25h"))
5757
}
5858

59-
func MoveCursor(y, x int) {
59+
func moveCursor(y, x int) {
6060
if disableAnsi {
6161
return
6262
}
6363
fmt.Print(ansi(fmt.Sprintf("[%d;%dH", y, x)))
6464
}
6565

66-
func MoveCursorX(pos int) {
66+
func carriageReturn() {
6767
if disableAnsi {
6868
return
6969
}
70-
fmt.Print(ansi(fmt.Sprintf("[%dG", pos)))
70+
fmt.Print(ansi(fmt.Sprintf("[%dG", 0)))
7171
}
7272

73-
func ClearLine() {
73+
func clearLine() {
7474
if disableAnsi {
7575
return
7676
}
7777
// Does not move cursor from its current position
7878
fmt.Print(ansi("[2K"))
7979
}
8080

81-
func MoveCursorUp(lines int) {
81+
func moveCursorUp(lines int) {
8282
if disableAnsi {
8383
return
8484
}
8585
// Does not add new lines
8686
fmt.Print(ansi(fmt.Sprintf("[%dA", lines)))
8787
}
8888

89-
func MoveCursorDown(lines int) {
89+
func moveCursorDown(lines int) {
9090
if disableAnsi {
9191
return
9292
}
9393
// Does not add new lines
9494
fmt.Print(ansi(fmt.Sprintf("[%dB", lines)))
9595
}
9696

97-
func NewLine() {
97+
func newLine() {
9898
// Like \n
9999
fmt.Print("\012")
100100
}

cmd/formatter/logs.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,12 @@ func (l *logConsumer) register(name string) *presenter {
7373
} else {
7474
cf := monochrome
7575
if l.color {
76-
if name == api.WatchLogger {
76+
switch name {
77+
case "":
78+
cf = monochrome
79+
case api.WatchLogger:
7780
cf = makeColorFunc("92")
78-
} else {
81+
default:
7982
cf = nextColor()
8083
}
8184
}
@@ -167,15 +170,15 @@ type logDecorator struct {
167170
After func()
168171
}
169172

170-
func (l logDecorator) Log(containerName, message string) {
173+
func (l logDecorator) Err(containerName, message string) {
171174
l.Before()
172-
l.decorated.Log(containerName, message)
175+
l.decorated.Err(containerName, message)
173176
l.After()
174177
}
175178

176-
func (l logDecorator) Err(containerName, message string) {
179+
func (l logDecorator) Log(containerName, message string) {
177180
l.Before()
178-
l.decorated.Err(containerName, message)
181+
l.decorated.Log(containerName, message)
179182
l.After()
180183
}
181184

cmd/formatter/shortcut.go

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ func (ke *KeyboardError) printError(height int, info string) {
4848
if ke.shouldDisplay() {
4949
errMessage := ke.err.Error()
5050

51-
MoveCursor(height-1-extraLines(info)-extraLines(errMessage), 0)
52-
ClearLine()
51+
moveCursor(height-1-extraLines(info)-extraLines(errMessage), 0)
52+
clearLine()
5353

5454
fmt.Print(errMessage)
5555
}
@@ -133,7 +133,7 @@ func (lk *LogKeyboard) createBuffer(lines int) {
133133

134134
if lines > 0 {
135135
allocateSpace(lines)
136-
MoveCursorUp(lines)
136+
moveCursorUp(lines)
137137
}
138138
}
139139

@@ -146,17 +146,17 @@ func (lk *LogKeyboard) printNavigationMenu() {
146146
height := goterm.Height()
147147
menu := lk.navigationMenu()
148148

149-
MoveCursorX(0)
150-
SaveCursor()
149+
carriageReturn()
150+
saveCursor()
151151

152152
lk.kError.printError(height, menu)
153153

154-
MoveCursor(height-extraLines(menu), 0)
155-
ClearLine()
154+
moveCursor(height-extraLines(menu), 0)
155+
clearLine()
156156
fmt.Print(menu)
157157

158-
MoveCursorX(0)
159-
RestoreCursor()
158+
carriageReturn()
159+
restoreCursor()
160160
}
161161
}
162162

@@ -188,15 +188,15 @@ func (lk *LogKeyboard) navigationMenu() string {
188188

189189
func (lk *LogKeyboard) clearNavigationMenu() {
190190
height := goterm.Height()
191-
MoveCursorX(0)
192-
SaveCursor()
191+
carriageReturn()
192+
saveCursor()
193193

194-
// ClearLine()
194+
// clearLine()
195195
for i := 0; i < height; i++ {
196-
MoveCursorDown(1)
197-
ClearLine()
196+
moveCursorDown(1)
197+
clearLine()
198198
}
199-
RestoreCursor()
199+
restoreCursor()
200200
}
201201

202202
func (lk *LogKeyboard) openDockerDesktop(ctx context.Context, project *types.Project) {
@@ -316,13 +316,13 @@ func (lk *LogKeyboard) HandleKeyEvents(ctx context.Context, event keyboard.KeyEv
316316
case keyboard.KeyCtrlC:
317317
_ = keyboard.Close()
318318
lk.clearNavigationMenu()
319-
ShowCursor()
319+
showCursor()
320320

321321
lk.logLevel = NONE
322322
// will notify main thread to kill and will handle gracefully
323323
lk.signalChannel <- syscall.SIGINT
324324
case keyboard.KeyEnter:
325-
NewLine()
325+
newLine()
326326
lk.printNavigationMenu()
327327
}
328328
}
@@ -336,9 +336,9 @@ func (lk *LogKeyboard) EnableWatch(enabled bool, watcher Feature) {
336336

337337
func allocateSpace(lines int) {
338338
for i := 0; i < lines; i++ {
339-
ClearLine()
340-
NewLine()
341-
MoveCursorX(0)
339+
clearLine()
340+
newLine()
341+
carriageReturn()
342342
}
343343
}
344344

cmd/formatter/stopping.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
Copyright 2024 Docker Compose CLI authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package formatter
18+
19+
import (
20+
"fmt"
21+
"strings"
22+
"time"
23+
24+
"github.com/buger/goterm"
25+
"github.com/docker/compose/v2/pkg/api"
26+
"github.com/docker/compose/v2/pkg/progress"
27+
)
28+
29+
type Stopping struct {
30+
api.LogConsumer
31+
enabled bool
32+
spinner *progress.Spinner
33+
ticker *time.Ticker
34+
startedAt time.Time
35+
}
36+
37+
func NewStopping(l api.LogConsumer) *Stopping {
38+
s := &Stopping{}
39+
s.LogConsumer = logDecorator{
40+
decorated: l,
41+
Before: s.clear,
42+
After: s.print,
43+
}
44+
return s
45+
}
46+
47+
func (s *Stopping) ApplicationTermination() {
48+
if progress.Mode != progress.ModeAuto {
49+
// User explicitly opted for output format
50+
return
51+
}
52+
if disableAnsi {
53+
return
54+
}
55+
s.enabled = true
56+
s.spinner = progress.NewSpinner()
57+
hideCursor()
58+
s.startedAt = time.Now()
59+
s.ticker = time.NewTicker(100 * time.Millisecond)
60+
go func() {
61+
for {
62+
<-s.ticker.C
63+
s.print()
64+
}
65+
}()
66+
}
67+
68+
func (s *Stopping) Close() {
69+
showCursor()
70+
if s.ticker != nil {
71+
s.ticker.Stop()
72+
}
73+
s.clear()
74+
}
75+
76+
func (s *Stopping) clear() {
77+
if !s.enabled {
78+
return
79+
}
80+
81+
height := goterm.Height()
82+
carriageReturn()
83+
saveCursor()
84+
85+
// clearLine()
86+
for i := 0; i < height; i++ {
87+
moveCursorDown(1)
88+
clearLine()
89+
}
90+
restoreCursor()
91+
}
92+
93+
const stoppingBanner = "Gracefully Stopping... (press Ctrl+C again to force)"
94+
95+
func (s *Stopping) print() {
96+
if !s.enabled {
97+
return
98+
}
99+
100+
height := goterm.Height()
101+
width := goterm.Width()
102+
carriageReturn()
103+
saveCursor()
104+
105+
moveCursor(height, 0)
106+
clearLine()
107+
elapsed := time.Since(s.startedAt).Seconds()
108+
timer := fmt.Sprintf("%.1fs ", elapsed)
109+
pad := width - len(timer) - len(stoppingBanner) - 5
110+
fmt.Printf("%s %s %s %s",
111+
progress.CountColor(s.spinner.String()),
112+
stoppingBanner,
113+
strings.Repeat(" ", pad),
114+
progress.TimerColor(timer),
115+
)
116+
117+
carriageReturn()
118+
restoreCursor()
119+
}

0 commit comments

Comments
 (0)