Skip to content

Commit

Permalink
Merge pull request containerd#2926 from yankay/fix-tail-rotate-log
Browse files Browse the repository at this point in the history
Fix the nerdctl stop printing after rotating the json log file
  • Loading branch information
fahedouch authored Apr 13, 2024
2 parents 6abca74 + 9b8e1dd commit f25ce7e
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 4 deletions.
28 changes: 28 additions & 0 deletions cmd/nerdctl/container_logs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"time"

"github.com/containerd/nerdctl/v2/pkg/testutil"
"gotest.tools/v3/assert"
)

func TestLogs(t *testing.T) {
Expand Down Expand Up @@ -206,3 +207,30 @@ func TestLogsWithForegroundContainers(t *testing.T) {
}(t)
}
}

func TestTailFollowRotateLogs(t *testing.T) {
t.Parallel()
if runtime.GOOS == "windows" {
t.Skip("tail log is not supported on Windows")
}
base := testutil.NewBase(t)
containerName := testutil.Identifier(t)

const sampleJSONLog = `{"log":"A\n","stream":"stdout","time":"2024-04-11T12:01:09.800288974Z"}`
const linesPerFile = 100

defer base.Cmd("rm", "-f", containerName).Run()
base.Cmd("run", "-d", "--log-driver", "json-file",
"--log-opt", fmt.Sprintf("max-size=%d", len(sampleJSONLog)*linesPerFile),
"--log-opt", "max-file=10",
"--name", containerName, testutil.CommonImage,
"sh", "-euc", "while true; do echo A; done").AssertOK()

tailLogCmd := base.Cmd("logs", "-f", containerName)
tailLogCmd.Timeout = 100 * time.Millisecond
tailLogs := strings.Split(strings.TrimSpace(tailLogCmd.Run().Combined()), "\n")
for _, line := range tailLogs {
assert.Equal(t, "A", line)
}
assert.Equal(t, true, len(tailLogs) > linesPerFile)
}
39 changes: 35 additions & 4 deletions pkg/logging/json_logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@
package logging

import (
"bufio"
"errors"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"time"

"github.com/containerd/containerd/runtime/v2/logging"
Expand Down Expand Up @@ -208,25 +210,54 @@ func viewLogsJSONFileThroughTailExec(lvopts LogViewOptions, jsonLogFilePath stri
}

if lvopts.Follow {
args = append(args, "-f")
// using the `-F` to follow the file name instead of descriptor and retry if inaccessible
args = append(args, "-F")
}
args = append(args, jsonLogFilePath)
cmd := exec.Command("tail", args...)
cmd.Stderr = os.Stderr
r, err := cmd.StdoutPipe()

cmdStdout, err := cmd.StdoutPipe()
if err != nil {
return err
}

cmdStderr, err := cmd.StderrPipe()
if err != nil {
return err
}

if err := cmd.Start(); err != nil {
return err
}

// filter the unwanted error message of the tail
go filterTailStderr(cmdStderr)

// Setup killing goroutine:
go func() {
<-stopChannel
log.L.Debugf("killing tail logs process with PID: %d", cmd.Process.Pid)
cmd.Process.Kill()
}()

return jsonfile.Decode(stdout, stderr, r, lvopts.Timestamps, lvopts.Since, lvopts.Until, 0)
return jsonfile.Decode(stdout, stderr, cmdStdout, lvopts.Timestamps, lvopts.Since, lvopts.Until, 0)
}

func filterTailStderr(reader io.Reader) error {
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
line := scanner.Text()
if strings.HasSuffix(line, "has appeared; following new file") ||
strings.HasSuffix(line, "has become inaccessible: No such file or directory") ||
strings.HasSuffix(line, "has been replaced; following new file") ||
strings.HasSuffix(line, ": No such file or directory") {
continue
}
fmt.Fprintln(os.Stderr, line)
}

if err := scanner.Err(); err != nil {
return err
}
return nil
}

0 comments on commit f25ce7e

Please sign in to comment.