Skip to content

Commit 2202bcf

Browse files
mafredriammario
andauthored
fix: Use SyscallConn for isTTY which is safe during file close (#167)
Co-authored-by: Ammar Bandukwala <[email protected]>
1 parent aa14d1b commit 2202bcf

File tree

2 files changed

+49
-3
lines changed

2 files changed

+49
-3
lines changed

internal/entryhuman/entry.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"reflect"
1313
"strconv"
1414
"strings"
15+
"syscall"
1516
"time"
1617
"unicode"
1718

@@ -225,9 +226,25 @@ func isTTY(w io.Writer) bool {
225226
if w == forceColorWriter {
226227
return true
227228
}
228-
f, ok := w.(interface {
229-
Fd() uintptr
230-
})
229+
// SyscallConn is safe during file close.
230+
if sc, ok := w.(interface {
231+
SyscallConn() (syscall.RawConn, error)
232+
}); ok {
233+
conn, err := sc.SyscallConn()
234+
if err != nil {
235+
return false
236+
}
237+
var isTerm bool
238+
err = conn.Control(func(fd uintptr) {
239+
isTerm = terminal.IsTerminal(int(fd))
240+
})
241+
if err != nil {
242+
return false
243+
}
244+
return isTerm
245+
}
246+
// Fallback to unsafe Fd.
247+
f, ok := w.(interface{ Fd() uintptr })
231248
return ok && terminal.IsTerminal(int(f.Fd()))
232249
}
233250

internal/entryhuman/entry_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"flag"
77
"fmt"
88
"io"
9+
"io/ioutil"
910
"os"
1011
"testing"
1112
"time"
@@ -178,6 +179,34 @@ func TestEntry(t *testing.T) {
178179
assert.Equal(t, "entry matches", string(wantByt), gotBuf.String())
179180
})
180181
}
182+
183+
t.Run("isTTY during file close", func(t *testing.T) {
184+
t.Parallel()
185+
186+
tmpdir := t.TempDir()
187+
f, err := ioutil.TempFile(tmpdir, "slog")
188+
if err != nil {
189+
t.Fatal(err)
190+
}
191+
defer f.Close()
192+
193+
done := make(chan struct{}, 2)
194+
go func() {
195+
entryhuman.Fmt(new(bytes.Buffer), f, slog.SinkEntry{
196+
Level: slog.LevelCritical,
197+
Fields: slog.M(
198+
slog.F("hey", "hi"),
199+
),
200+
})
201+
done <- struct{}{}
202+
}()
203+
go func() {
204+
_ = f.Close()
205+
done <- struct{}{}
206+
}()
207+
<-done
208+
<-done
209+
})
181210
}
182211

183212
func BenchmarkFmt(b *testing.B) {

0 commit comments

Comments
 (0)