Skip to content

Commit

Permalink
Prefix each output line with command in logs (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
flexoid committed Jul 27, 2020
1 parent 89e4744 commit 8b4138c
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 4 deletions.
71 changes: 71 additions & 0 deletions app/service/log_prefixer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package service

import (
"bufio"
"bytes"
"fmt"
"io"
)

const prefixCommandMaxlen = 16
const prefixCutCommandSuffix = "..."

// LogPrefixer implements `io.writer` interface and adds prefix to each output line.
type LogPrefixer struct {
writer io.Writer
prefix []byte
}

// NewLogPrefixer initializes log prefixer.
func NewLogPrefixer(writer io.Writer, command string) *LogPrefixer {
logPrefixer := &LogPrefixer{writer: writer}
logPrefixer.prefix = logPrefixer.prefixForCommand(command)
return logPrefixer
}

func (p *LogPrefixer) Write(data []byte) (int, error) {
reader := bufio.NewReader(bytes.NewReader(data))

var line []byte
var err error
var bytesWritten int = 0

for {
line, err = reader.ReadBytes('\n')

// There can be data to write in `line` even if `io.EOF` error is returned.
// Exit immediately only in case of unexpected error.
if err != nil && err != io.EOF {
return bytesWritten, err
}

if len(line) > 0 {
_, writeErr := p.writer.Write(p.prefix)
if writeErr != nil {
return bytesWritten, writeErr
}

n, writeErr := p.writer.Write(line)
bytesWritten += n
if writeErr != nil {
return bytesWritten, writeErr
}
}

if err == io.EOF {
break
}
}

return bytesWritten, nil
}

func (p *LogPrefixer) prefixForCommand(command string) []byte {
if len(command) > prefixCommandMaxlen {
command = command[:prefixCommandMaxlen]
command += prefixCutCommandSuffix
}

prefix := fmt.Sprintf("{%s} ", command)
return []byte(prefix)
}
35 changes: 35 additions & 0 deletions app/service/log_prefixer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package service

import (
"bytes"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestLogPrefixer_Write(t *testing.T) {
out := bytes.NewBuffer(nil)
prefixer := NewLogPrefixer(out, "du /var/lib/monitoring")

n, err := prefixer.Write([]byte("first line of the output\n"))
require.NoError(t, err)
assert.Equal(t, 25, n)

n, err = prefixer.Write([]byte("second line of the output\n"))
require.NoError(t, err)
assert.Equal(t, 26, n)

expectedOutput :=
"{du /var/lib/moni...} first line of the output\n" +
"{du /var/lib/moni...} second line of the output\n"
assert.Equal(t, expectedOutput, out.String())
}

func TestLogPrefixer_prefixForCommand(t *testing.T) {
prefixer := &LogPrefixer{}

assert.Equal(t, []byte("{ls -la} "), prefixer.prefixForCommand("ls -la"))
assert.Equal(t, []byte("{cat /var/lib/pid} "), prefixer.prefixForCommand("cat /var/lib/pid"))
assert.Equal(t, []byte("{du /var/lib/moni...} "), prefixer.prefixForCommand("du /var/lib/monitoring"))
}
4 changes: 3 additions & 1 deletion app/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,9 @@ func (s *Scheduler) executeCommand(command string, logWriter io.Writer) error {
cmd := exec.Command("sh", "-c", command) // nolint gosec
serr := NewErrorWriter(s.MaxLogLines)

logWithErr := io.MultiWriter(logWriter, serr)
prefixer := NewLogPrefixer(logWriter, command)

logWithErr := io.MultiWriter(prefixer, serr)
cmd.Stdout = logWithErr
cmd.Stderr = logWithErr
if e := cmd.Run(); e != nil {
Expand Down
6 changes: 3 additions & 3 deletions app/service/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func TestScheduler_DoIntegration(t *testing.T) {

svc.Do(ctx)
t.Log(out.String())
assert.Contains(t, out.String(), "123\n")
assert.Contains(t, out.String(), "{echo 123} 123\n")
notif.AssertExpectations(t)
}

Expand All @@ -85,7 +85,7 @@ func TestScheduler_execute(t *testing.T) {
wr := bytes.NewBuffer(nil)
err := svc.executeCommand("echo 123", wr)
require.NoError(t, err)
assert.Equal(t, "123\n", wr.String())
assert.Equal(t, "{echo 123} 123\n", wr.String())
}

func TestScheduler_executeFailedNotFound(t *testing.T) {
Expand Down Expand Up @@ -118,7 +118,7 @@ func TestScheduler_jobFunc(t *testing.T) {
resmr.On("OnFinish", "resume.file").Return(nil).Once()

svc.jobFunc(crontab.JobSpec{Spec: "@startup", Command: "echo 123"}, scheduleMock).Run()
assert.Equal(t, "123\n", wr.String())
assert.Equal(t, "{echo 123} 123\n", wr.String())
}

func TestScheduler_jobFuncFailed(t *testing.T) {
Expand Down

0 comments on commit 8b4138c

Please sign in to comment.