Skip to content

Commit

Permalink
feature: add logs cli details opt support and add more cli tests
Browse files Browse the repository at this point in the history
Signed-off-by: zhangyue <[email protected]>
  • Loading branch information
zhangyue committed Oct 10, 2018
1 parent 4002e93 commit a31b21e
Show file tree
Hide file tree
Showing 12 changed files with 129 additions and 34 deletions.
5 changes: 1 addition & 4 deletions apis/server/container_bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,17 +340,14 @@ func (s *Server) logsContainer(ctx context.Context, rw http.ResponseWriter, req
Until: req.Form.Get("until"),
Follow: httputils.BoolValue(req, "follow"),
Timestamps: httputils.BoolValue(req, "timestamps"),

// TODO: support the details
// Details: httputils.BoolValue(r, "details"),
Details: httputils.BoolValue(req, "details"),
}

name := mux.Vars(req)["name"]
msgCh, tty, err := s.ContainerMgr.Logs(ctx, name, opts)
if err != nil {
return err
}

writeLogStream(ctx, rw, tty, opts, msgCh)
return nil
}
Expand Down
10 changes: 10 additions & 0 deletions apis/server/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"io"
"strings"

"github.com/alibaba/pouch/apis/types"
"github.com/alibaba/pouch/daemon/logger"
Expand Down Expand Up @@ -73,6 +74,15 @@ func writeLogStream(ctx context.Context, w io.Writer, tty bool, opt *types.Conta
}

logLine := msg.Line

if opt.Details {
var ss []string
for k, v := range msg.Attrs {
ss = append(ss, k+"="+v)
}
logLine = append([]byte(strings.Join(ss, ",")+" "), logLine...)
}

if opt.Timestamps {
logLine = append([]byte(msg.Timestamp.Format(utils.TimeLayout)+" "), logLine...)
}
Expand Down
4 changes: 2 additions & 2 deletions cli/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ func (lc *LogsCommand) addFlags() {
flagSet.StringVarP(&lc.until, "until", "", "", "Show logs before timestamp (e.g. 2013-01-02T13:23:37) or relative (e.g. 42m for 42 minutes)")
flagSet.StringVarP(&lc.tail, "tail", "", "all", "Number of lines to show from the end of the logs default \"all\"")
flagSet.BoolVarP(&lc.timestamps, "timestamps", "t", false, "Show timestamps")

// TODO(fuwei): support the detail functionality
flagSet.BoolVar(&lc.details, "details", false, "Show extra details provided to logs")
}

// runLogs is the entry of LogsCommand command.
Expand All @@ -68,6 +67,7 @@ func (lc *LogsCommand) runLogs(args []string) error {
Timestamps: lc.timestamps,
Follow: lc.follow,
Tail: lc.tail,
Details: lc.details,
}

body, err := apiClient.ContainerLogs(ctx, containerName, opts)
Expand Down
4 changes: 4 additions & 0 deletions client/container_logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ func (client *APIClient) ContainerLogs(ctx context.Context, name string, options
if options.Follow {
query.Set("follow", "1")
}

if options.Details {
query.Set("details", "1")
}
query.Set("tail", options.Tail)

resp, err := client.get(ctx, "/containers/"+name+"/logs", query, nil)
Expand Down
21 changes: 20 additions & 1 deletion daemon/containerio/jsonfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/alibaba/pouch/daemon/logger/jsonfile"

"github.com/sirupsen/logrus"
"encoding/json"
)

func init() {
Expand Down Expand Up @@ -43,7 +44,25 @@ func (jf *jsonFile) Init(opt *Option) error {
}

logPath := filepath.Join(rootDir, jsonFilePathName)
w, err := jsonfile.NewJSONLogFile(logPath, 0644)
attrs, err := opt.info.ExtraAttributes(nil)
if err != nil {
return err
}

var extra []byte
if len(attrs) > 0 {
var err error
extra, err = json.Marshal(attrs)
if err != nil {
return err
}
}

marshalFunc := func(msg *logger.LogMessage) ([]byte, error) {
return jsonfile.Marshal(msg, extra)
}

w, err := jsonfile.NewJSONLogFile(logPath, 0644, marshalFunc)
if err != nil {
return err
}
Expand Down
18 changes: 14 additions & 4 deletions daemon/logger/jsonfile/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import (
)

type jsonLog struct {
Stream string `json:"stream,omitempty"`
Log string `json:"log,omitempty"`
Timestamp time.Time `json:"time"`
Stream string `json:"stream,omitempty"`
Log string `json:"log,omitempty"`
Timestamp time.Time `json:"time"`
Attrs map[string]string `json:"attrs,omitempty"`
}

func newUnmarshal(r io.Reader) func() (*logger.LogMessage, error) {
Expand All @@ -30,11 +31,13 @@ func newUnmarshal(r io.Reader) func() (*logger.LogMessage, error) {
Source: jl.Stream,
Line: []byte(jl.Log),
Timestamp: jl.Timestamp,
Attrs: jl.Attrs,
}, nil
}
}

func marshal(msg *logger.LogMessage) ([]byte, error) {
//Marshal used to marshal LogMessage to byte[]
func Marshal(msg *logger.LogMessage, rawAttrs []byte) ([]byte, error) {
var (
first = true
buf bytes.Buffer
Expand Down Expand Up @@ -62,6 +65,13 @@ func marshal(msg *logger.LogMessage) ([]byte, error) {

buf.WriteString(`"time":`)
buf.WriteString(msg.Timestamp.UTC().Format(`"` + utils.TimeLayout + `"`))

if len(rawAttrs) > 0 {
buf.WriteString(`,`)
buf.WriteString(`"attrs":`)
buf.Write(rawAttrs)
}

buf.WriteString(`}`)

// NOTE: add newline here to make the decoder easier
Expand Down
5 changes: 4 additions & 1 deletion daemon/logger/jsonfile/encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@ import (
)

func TestMarshalAndUnmarshal(t *testing.T) {

attrs := map[string]string{"env": "test"}
expectedMsg := &logger.LogMessage{
Source: "stdout",
Line: []byte("hello pouch"),
Timestamp: time.Now().UTC(),
Attrs: attrs,
}

bs, err := marshal(expectedMsg)
bs, err := Marshal(expectedMsg, attrs)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
Expand Down
21 changes: 13 additions & 8 deletions daemon/logger/jsonfile/jsonfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,37 @@ import (
"github.com/alibaba/pouch/daemon/logger"
)

//MarshalFunc is the function of marshal the logMessage
type MarshalFunc func(message *logger.LogMessage) ([]byte, error)

// JSONLogFile is uses to log the container's stdout and stderr.
type JSONLogFile struct {
mu sync.Mutex

f *os.File
perms os.FileMode
closed bool
f *os.File
perms os.FileMode
closed bool
marshalFunc MarshalFunc
}

// NewJSONLogFile returns new JSONLogFile instance.
func NewJSONLogFile(logPath string, perms os.FileMode) (*JSONLogFile, error) {
func NewJSONLogFile(logPath string, perms os.FileMode, marshalFunc MarshalFunc) (*JSONLogFile, error) {
f, err := os.OpenFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, perms)
if err != nil {
return nil, err
}

return &JSONLogFile{
f: f,
perms: perms,
closed: false,
f: f,
perms: perms,
closed: false,
marshalFunc: marshalFunc,
}, nil
}

// WriteLogMessage will write the LogMessage into the file.
func (lf *JSONLogFile) WriteLogMessage(msg *logger.LogMessage) error {
b, err := marshal(msg)
b, err := lf.marshalFunc(msg)
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions daemon/logger/jsonfile/jsonfile_read_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func TestReadLogMessagesWithRemoveFileInFollowMode(t *testing.T) {
defer f.Close()
defer os.RemoveAll(f.Name())

jf, err := NewJSONLogFile(f.Name(), 0640)
jf, err := NewJSONLogFile(f.Name(), 0640, nil)
if err != nil {
t.Fatalf("unexpected error during create JSONLogFile: %v", err)
}
Expand Down Expand Up @@ -52,7 +52,7 @@ func TestReadLogMessagesForEmptyFileWithoutFollow(t *testing.T) {
defer f.Close()
defer os.RemoveAll(f.Name())

jf, err := NewJSONLogFile(f.Name(), 0644)
jf, err := NewJSONLogFile(f.Name(), 0644, nil)
if err != nil {
t.Fatalf("unexpected error during create JSONLogFile: %v", err)
}
Expand Down
13 changes: 7 additions & 6 deletions daemon/logger/logmessage.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ type LogMessage struct {
Source string // Source means stdin, stdout or stderr
Line []byte // Line means the log content, but it maybe partial
Timestamp time.Time // Timestamp means the created time of line

Err error
Attrs map[string]string
Err error
}

// LogWatcher is used to pass the log message to the reader.
Expand Down Expand Up @@ -48,8 +48,9 @@ func (w *LogWatcher) WatchClose() <-chan struct{} {

// ReadConfig is used to
type ReadConfig struct {
Since time.Time
Until time.Time
Tail int
Follow bool
Since time.Time
Until time.Time
Tail int
Follow bool
Details bool
}
13 changes: 8 additions & 5 deletions daemon/mgr/container_logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ func (mgr *ContainerManager) Logs(ctx context.Context, name string, logOpt *type
}

fileName := filepath.Join(mgr.Store.Path(c.ID), "json.log")
jf, err := jsonfile.NewJSONLogFile(fileName, 0640)

jf, err := jsonfile.NewJSONLogFile(fileName, 0640, nil)

if err != nil {
return nil, false, err
}
Expand Down Expand Up @@ -137,9 +139,10 @@ func convContainerLogsOptionsToReadConfig(logOpt *types.ContainerLogsOptions) (*
}

return &logger.ReadConfig{
Since: since,
Until: until,
Follow: logOpt.Follow,
Tail: lines,
Since: since,
Until: until,
Follow: logOpt.Follow,
Tail: lines,
Details: logOpt.Details,
}, nil
}
45 changes: 44 additions & 1 deletion test/cli_logs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ func init() {
func (suite *PouchLogsSuite) SetUpSuite(c *check.C) {
SkipIfFalse(c, environment.IsLinux)

environment.PruneAllContainers(apiClient)

PullImage(c, busyboxImage)
}

Expand All @@ -39,6 +41,30 @@ func (suite *PouchLogsSuite) TestCreatedContainerLogIsEmpty(c *check.C) {
c.Assert(res.Combined(), check.Equals, "")
}

func (suite *PouchLogsSuite) TestLogsSeparateStderr(c *check.C) {
cname := "TestLogsSeparateStderr"
msg := "stderr_log"
command.PouchRun("run", "-d", "--name", cname, busyboxImage, "sh", "-c", fmt.Sprintf("echo %s 1>&2", msg)).Assert(c, icmd.Success)
defer DelContainerForceMultyTime(c, cname)

command.PouchRun("logs", cname).Assert(c, icmd.Expected{
Out: "",
Err: msg,
})
}

func (suite *PouchLogsSuite) TestLogsStderrInStdout(c *check.C) {
cname := "TestLogsStderrInStdout"
msg := "stderr_log"
command.PouchRun("run", "-d", "-t", "--name", cname, busyboxImage, "sh", "-c", fmt.Sprintf("echo %s 1>&2", msg)).Assert(c, icmd.Success)
defer DelContainerForceMultyTime(c, cname)

command.PouchRun("logs", cname).Assert(c, icmd.Expected{
Out: msg,
Err: "",
})
}

// TestSinceAndUntil tests the since and until.
func (suite *PouchLogsSuite) TestSinceAndUntil(c *check.C) {
cname := "TestCLILogs_Since_and_Until"
Expand Down Expand Up @@ -93,7 +119,7 @@ func (suite *PouchLogsSuite) TestTimestamp(c *check.C) {
// TestTailMode tests follow mode.
func (suite *PouchLogsSuite) TestTailLine(c *check.C) {
cname := "TestCLILogs_tail_line"
DelContainerForceMultyTime(c, cname)

totalLine := 100

command.PouchRun(
Expand All @@ -103,6 +129,7 @@ func (suite *PouchLogsSuite) TestTailLine(c *check.C) {
busyboxImage,
"sh", "-c", fmt.Sprintf("for i in $(seq 1 %v); do echo hello-$i; done;", totalLine),
).Assert(c, icmd.Success)
defer DelContainerForceMultyTime(c, cname)

for _, tc := range []struct {
input string
Expand Down Expand Up @@ -148,6 +175,22 @@ func (suite *PouchLogsSuite) TestFollowMode(c *check.C) {
}
}

// TestLogsWithDetails tests details opt.
func (suite *PouchLogsSuite) TestLogsWithDetails(c *check.C) {
cname := "TestLogsWithDetails"

res := command.PouchRun("run", "--name", cname, "--label", "foo=bar", "-e", "baz=qux", "--log-opt", "labels=foo", "--log-opt", "env=baz", "busybox", "echo", "hello")
res.Assert(c, icmd.Success)
defer DelContainerForceMultyTime(c, cname)

allLogs := suite.syncLogs(c, cname, "--details")
c.Assert(len(allLogs), check.Equals, 1)
logFields := strings.Split(allLogs[0], " ")

c.Assert(logFields[0], check.Equals, "baz=qux")
c.Assert(logFields[1], check.Equals, "foo=bar")
}

func (suite *PouchLogsSuite) syncLogs(c *check.C, cname string, flags ...string) []string {
args := append([]string{"logs"}, flags...)

Expand Down

0 comments on commit a31b21e

Please sign in to comment.