fix: guard server log lumberjack with mutex (#15582)

(Hopefully) closes https://github.com/coder/internal/issues/213.
This commit is contained in:
Ethan
2024-11-19 19:47:35 +11:00
committed by GitHub
parent 029cd5d064
commit e72d58b4f6
2 changed files with 33 additions and 32 deletions
+3 -30
View File
@@ -12,7 +12,6 @@ import (
"runtime"
"strconv"
"strings"
"sync"
"time"
"cloud.google.com/go/compute/metadata"
@@ -30,6 +29,7 @@ import (
"github.com/coder/coder/v2/agent/agentssh"
"github.com/coder/coder/v2/agent/reaper"
"github.com/coder/coder/v2/buildinfo"
"github.com/coder/coder/v2/cli/clilog"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/agentsdk"
"github.com/coder/serpent"
@@ -110,7 +110,7 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
// Spawn a reaper so that we don't accumulate a ton
// of zombie processes.
if reaper.IsInitProcess() && !noReap && isLinux {
logWriter := &lumberjackWriteCloseFixer{w: &lumberjack.Logger{
logWriter := &clilog.LumberjackWriteCloseFixer{Writer: &lumberjack.Logger{
Filename: filepath.Join(logDir, "coder-agent-init.log"),
MaxSize: 5, // MB
// Without this, rotated logs will never be deleted.
@@ -153,7 +153,7 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
// reaper.
go DumpHandler(ctx, "agent")
logWriter := &lumberjackWriteCloseFixer{w: &lumberjack.Logger{
logWriter := &clilog.LumberjackWriteCloseFixer{Writer: &lumberjack.Logger{
Filename: filepath.Join(logDir, "coder-agent.log"),
MaxSize: 5, // MB
// Per customer incident on November 17th, 2023, its helpful
@@ -478,33 +478,6 @@ func ServeHandler(ctx context.Context, logger slog.Logger, handler http.Handler,
}
}
// lumberjackWriteCloseFixer is a wrapper around an io.WriteCloser that
// prevents writes after Close. This is necessary because lumberjack
// re-opens the file on Write.
type lumberjackWriteCloseFixer struct {
w io.WriteCloser
mu sync.Mutex // Protects following.
closed bool
}
func (c *lumberjackWriteCloseFixer) Close() error {
c.mu.Lock()
defer c.mu.Unlock()
c.closed = true
return c.w.Close()
}
func (c *lumberjackWriteCloseFixer) Write(p []byte) (int, error) {
c.mu.Lock()
defer c.mu.Unlock()
if c.closed {
return 0, io.ErrClosedPipe
}
return c.w.Write(p)
}
// extractPort handles different url strings.
// - localhost:6060
// - http://localhost:6060
+30 -2
View File
@@ -6,6 +6,7 @@ import (
"io"
"regexp"
"strings"
"sync"
"golang.org/x/xerrors"
"gopkg.in/natefinch/lumberjack.v2"
@@ -111,12 +112,12 @@ func (b *Builder) Build(inv *serpent.Invocation) (log slog.Logger, closeLog func
sinks = append(sinks, sinkFn(inv.Stderr))
default:
logWriter := &lumberjack.Logger{
logWriter := &LumberjackWriteCloseFixer{Writer: &lumberjack.Logger{
Filename: loc,
MaxSize: 5, // MB
// Without this, rotated logs will never be deleted.
MaxBackups: 1,
}
}}
closers = append(closers, logWriter.Close)
sinks = append(sinks, sinkFn(logWriter))
}
@@ -210,3 +211,30 @@ func (f *debugFilterSink) Sync() {
sink.Sync()
}
}
// LumberjackWriteCloseFixer is a wrapper around an io.WriteCloser that
// prevents writes after Close. This is necessary because lumberjack
// re-opens the file on Write.
type LumberjackWriteCloseFixer struct {
Writer io.WriteCloser
mu sync.Mutex // Protects following.
closed bool
}
func (c *LumberjackWriteCloseFixer) Close() error {
c.mu.Lock()
defer c.mu.Unlock()
c.closed = true
return c.Writer.Close()
}
func (c *LumberjackWriteCloseFixer) Write(p []byte) (int, error) {
c.mu.Lock()
defer c.mu.Unlock()
if c.closed {
return 0, io.ErrClosedPipe
}
return c.Writer.Write(p)
}