Skip to content
This repository has been archived by the owner on May 8, 2023. It is now read-only.

Commit

Permalink
Show a scrolling output for each in progress task.
Browse files Browse the repository at this point in the history
  • Loading branch information
Tom Elliott committed Nov 8, 2018
1 parent 1af0c53 commit 36487b2
Show file tree
Hide file tree
Showing 17 changed files with 374 additions and 58 deletions.
19 changes: 17 additions & 2 deletions builder/build.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package builder

import (
"bufio"
"io"
"strings"

"github.com/pkg/errors"
"github.com/yext/edward/home"
"github.com/yext/edward/instance"
Expand Down Expand Up @@ -64,9 +68,20 @@ func (b *builder) BuildWithTracker(dirConfig *home.EdwardConfiguration, task tra
if err != nil {
return errors.WithStack(err)
}
out, err := builder.Build(b.Cfg.WorkingDir, c.Getenv)

r, w := io.Pipe()
defer r.Close()
defer w.Close()
go func() {
scanner := bufio.NewScanner(r)
for scanner.Scan() {
job.AddOutput(scanner.Text())
}
}()
err = builder.Build(b.Cfg.WorkingDir, c.Getenv, w)

if err != nil {
job.SetState(tracker.TaskStateFailed, err.Error(), string(out))
job.SetState(tracker.TaskStateFailed, err.Error(), strings.Join(job.Output(), "\n"))
return errors.WithMessage(err, "running build command")
}
job.SetState(tracker.TaskStateSuccess)
Expand Down
8 changes: 4 additions & 4 deletions edward/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import (
"time"

"github.com/yext/edward/home"
"github.com/yext/edward/instance/servicelogs"

"github.com/pkg/errors"
"github.com/yext/edward/instance"
"github.com/yext/edward/runner"
"github.com/yext/edward/services"
)

Expand All @@ -26,8 +26,8 @@ func (c *Client) Log(names []string, cancelChannel <-chan struct{}) error {
return errors.WithStack(err)
}

var tailChannel = make(chan runner.LogLine)
var lines []runner.LogLine
var tailChannel = make(chan servicelogs.LogLine)
var lines []servicelogs.LogLine
for _, sg := range sgs {
switch v := sg.(type) {
case *services.ServiceConfig:
Expand Down Expand Up @@ -69,7 +69,7 @@ func (c *Client) Log(names []string, cancelChannel <-chan struct{}) error {
}
}()

var logChannel = make(chan runner.LogLine)
var logChannel = make(chan servicelogs.LogLine)
c.UI.ShowLog(logChannel, services.CountServices(sgs) > 1)

// Sort initial lines
Expand Down
20 changes: 10 additions & 10 deletions edward/tail.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ import (

"github.com/hpcloud/tail"
"github.com/pkg/errors"
"github.com/yext/edward/runner"
"github.com/yext/edward/instance/servicelogs"
"github.com/yext/edward/services"
)

type byTime []runner.LogLine
type byTime []servicelogs.LogLine

func (a byTime) Len() int { return len(a) }
func (a byTime) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byTime) Less(i, j int) bool { return a[i].Time.Before(a[j].Time) }

func followGroupLog(logDir string, group *services.ServiceGroupConfig, logChannel chan runner.LogLine) ([]runner.LogLine, error) {
var lines []runner.LogLine
func followGroupLog(logDir string, group *services.ServiceGroupConfig, logChannel chan servicelogs.LogLine) ([]servicelogs.LogLine, error) {
var lines []servicelogs.LogLine
for _, group := range group.Groups {
newLines, err := followGroupLog(logDir, group, logChannel)
lines = append(lines, newLines...)
Expand All @@ -35,7 +35,7 @@ func followGroupLog(logDir string, group *services.ServiceGroupConfig, logChanne
return lines, nil
}

func followServiceLog(logDir string, service *services.ServiceConfig, logChannel chan runner.LogLine) ([]runner.LogLine, error) {
func followServiceLog(logDir string, service *services.ServiceConfig, logChannel chan servicelogs.LogLine) ([]servicelogs.LogLine, error) {
// Skip services that don't include a launch step
if !service.Backend().HasLaunchStep() {
return nil, nil
Expand All @@ -47,15 +47,15 @@ func followServiceLog(logDir string, service *services.ServiceConfig, logChannel
if err != nil {
return nil, errors.WithStack(err)
}
var initialLines []runner.LogLine
var initialLines []servicelogs.LogLine
// create a new scanner and read the file line by line
scanner := bufio.NewScanner(logFile)
var lineCount int
for scanner.Scan() {
text := scanner.Text()
lineCount++
var line runner.LogLine
line, err = runner.ParseLogLine(text)
var line servicelogs.LogLine
line, err = servicelogs.ParseLogLine(text)
if err != nil {
return nil, errors.WithStack(err)
}
Expand All @@ -71,7 +71,7 @@ func followServiceLog(logDir string, service *services.ServiceConfig, logChannel
return initialLines, nil
}

func doFollowServiceLog(logDir string, service *services.ServiceConfig, skipLines int, logChannel chan runner.LogLine) error {
func doFollowServiceLog(logDir string, service *services.ServiceConfig, skipLines int, logChannel chan servicelogs.LogLine) error {
runLog := service.GetRunLog(logDir)
t, err := tail.TailFile(runLog, tail.Config{
Follow: true,
Expand All @@ -86,7 +86,7 @@ func doFollowServiceLog(logDir string, service *services.ServiceConfig, skipLine
linesSkipped++
continue
}
lineData, err := runner.ParseLogLine(line.Text)
lineData, err := servicelogs.ParseLogLine(line.Text)
if err != nil {
return errors.WithStack(err)
}
Expand Down
1 change: 1 addition & 0 deletions examples/store/carts/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ func main() {
http.HandleFunc("/", handler)
fmt.Println("Starting to listen on port", os.Args[1])
http.ListenAndServe(":"+os.Args[1], nil)
broken
}
93 changes: 93 additions & 0 deletions instance/servicelogs/follow.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package servicelogs

import (
"log"
"os"
"time"

"github.com/hpcloud/tail"
"github.com/pkg/errors"
)

type LogFollower struct {
logs chan LogLine
done chan struct{}

runLog string
}

// NewLogFollower creates a log follower that tails a log file for the specified service
func NewLogFollower(runLog string) *LogFollower {
return &LogFollower{
runLog: runLog,
done: make(chan struct{}),
}
}

func (f *LogFollower) Start() <-chan LogLine {
logs := make(chan LogLine)
go f.doStart(logs)
return logs
}

func (f *LogFollower) doStart(logs chan<- LogLine) {
// Wait for file to exist
var exists bool
for !exists {
_, err := os.Stat(f.runLog)
exists = !os.IsNotExist(err)
select {
case <-f.done:
close(logs)
return
default:
time.Sleep(time.Millisecond * 100)
}
}

err := doFollowServiceLog(f.runLog, 0, logs, f.done)
if err != nil {
log.Print("error", err)
return
}
}

func (f *LogFollower) Stop() {
close(f.done)
}

func doFollowServiceLog(file string, skipLines int, logChannel chan<- LogLine, done <-chan struct{}) error {
t, err := tail.TailFile(file, tail.Config{
Follow: true,
Logger: tail.DiscardingLogger,
Location: &tail.SeekInfo{
Offset: 0,
Whence: 0,
},
})
if err != nil {
return errors.WithStack(err)
}
defer func() {
close(logChannel)
}()
var linesSkipped int
for line := range t.Lines {
if linesSkipped < skipLines {
linesSkipped++
continue
}
lineData, err := ParseLogLine(line.Text)
if err != nil {
t.Err()
}
logChannel <- lineData

select {
case <-done:
return nil
default:
}
}
return nil
}
149 changes: 149 additions & 0 deletions instance/servicelogs/follow_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package servicelogs_test

import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
"time"

"github.com/yext/edward/instance/servicelogs"
)

func TestFollowLogsExisting(t *testing.T) {
dir, err := ioutil.TempDir("", "example")
if err != nil {
t.Fatal(err)
}

defer os.RemoveAll(dir) // clean up

tmpfn := filepath.Join(dir, "tmpfile")
f, err := os.Create(tmpfn)
if err != nil {
t.Fatal(err)
}

for i := 0; i < 10; i++ {
lineData := servicelogs.LogLine{
Name: "MyService",
Time: time.Now(),
Stream: "stream",
Message: fmt.Sprint(i),
}

jsonContent, err := json.Marshal(lineData)
if err != nil {
t.Fatal(err)
}

fmt.Fprintln(f, string(jsonContent))
}

lf := servicelogs.NewLogFollower(tmpfn)
lc := lf.Start()
defer lf.Stop()

success := make(chan struct{})
var count int
go func() {
for range lc {
fmt.Println(count)
count++
if count == 20 {
close(success)
}
}
}()

for i := 10; i < 20; i++ {
lineData := servicelogs.LogLine{
Name: "MyService",
Time: time.Now(),
Stream: "stream",
Message: fmt.Sprint(i),
}

jsonContent, err := json.Marshal(lineData)
if err != nil {
t.Fatal(err)
}

fmt.Fprintln(f, string(jsonContent))
}

select {
case <-success:
return
case <-time.After(time.Second):
t.Errorf("Timed out waiting for results")
}

if t.Failed() {
t.Logf("Got %d results", count)
}

}

func TestFollowLogsWaitForCreation(t *testing.T) {
dir, err := ioutil.TempDir("", "example")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir) // clean up

tmpfn := filepath.Join(dir, "tmpfile")

lf := servicelogs.NewLogFollower(tmpfn)
lc := lf.Start()
defer lf.Stop()

f, err := os.Create(tmpfn)
if err != nil {
t.Fatal(err)
}

success := make(chan struct{})
var count int
go func() {
for range lc {
fmt.Println(count)
count++
if count == 20 {
close(success)
return
}
}
}()

for i := 0; i < 20; i++ {
lineData := servicelogs.LogLine{
Name: "MyService",
Time: time.Now(),
Stream: "stream",
Message: fmt.Sprint(i),
}

jsonContent, err := json.Marshal(lineData)
if err != nil {
t.Fatal(err)
}

fmt.Fprintln(f, string(jsonContent))
time.Sleep(time.Millisecond)
}

select {
case <-success:
return
case <-time.After(time.Second):
t.Errorf("Timed out waiting for results")
}

if t.Failed() {
t.Logf("Got %d results", count)
}

}
Loading

0 comments on commit 36487b2

Please sign in to comment.