add logs to stage output

pull/1/head
Brad Rydzewski 5 years ago
parent 48f68a1e1b
commit 0c4ca16947

@ -51,18 +51,30 @@ func HandleIndex(t *history.History) http.HandlerFunc {
// HandleStage returns a http.HandlerFunc that displays the // HandleStage returns a http.HandlerFunc that displays the
// stage details. // stage details.
func HandleStage(t *history.History) http.HandlerFunc { func HandleStage(hist *history.History, logger *hook.Hook) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
id, _ := strconv.ParseInt(r.FormValue("id"), 10, 64) id, _ := strconv.ParseInt(r.FormValue("id"), 10, 64)
for _, e := range t.Entries() {
if e.Stage.ID == id { // filter logs by stage id.
nocache(w) logs := logger.Filter(func(entry *hook.Entry) bool {
render(w, "stage.tmpl", e) return entry.Data["stage.id"] == id
return })
}
// find pipeline by stage id
entry := hist.Entry(id)
if entry == nil {
w.WriteHeader(404)
return
} }
// TODO(bradrydzewski) we need an error template.
w.WriteHeader(404) nocache(w)
render(w, "stage.tmpl", struct {
*history.Entry
Logs []*hook.Entry
}{
Entry: entry,
Logs: logs,
})
} }
} }

@ -43,7 +43,7 @@ func New(tracer *history.History, history *hook.Hook, config Config) http.Handle
// dashboard handles. // dashboard handles.
mux.Handle("/static/", http.StripPrefix("/static/", fs)) mux.Handle("/static/", http.StripPrefix("/static/", fs))
mux.Handle("/logs", auth(handler.HandleLogHistory(history))) mux.Handle("/logs", auth(handler.HandleLogHistory(history)))
mux.Handle("/view", auth(handler.HandleStage(tracer))) mux.Handle("/view", auth(handler.HandleStage(tracer, history)))
mux.Handle("/", auth(handler.HandleIndex(tracer))) mux.Handle("/", auth(handler.HandleIndex(tracer)))
return mux return mux
} }

@ -173,7 +173,7 @@ a.card {
.steps .step { .steps .step {
display: grid; display: grid;
grid-gap: 10px 0px; grid-gap: 10px 0px;
grid-template-columns: 30px auto; grid-template-columns: 30px auto 100px;
padding: 10px 15px; padding: 10px 15px;
} }
@ -218,6 +218,16 @@ a.card {
display: none; display: none;
} }
.steps .status-name {
align-items: center;
color: rgba(30,55,90,.6);
display: flex;
font-size: 14px;
font-style: italic;
justify-content: flex-end;
text-transform: capitalize;
}
/** /**
* breadcrumb component * breadcrumb component
*/ */

@ -228,8 +228,8 @@ var files = map[string]file{
data: file11, data: file11,
FileInfo: &fileInfo{ FileInfo: &fileInfo{
name: "style.css", name: "style.css",
size: 8275, size: 8488,
modTime: time.Unix(1563076316, 0), modTime: time.Unix(1563131883, 0),
}, },
}, },
"/timeago.js": { "/timeago.js": {
@ -1337,7 +1337,7 @@ a.card {
.steps .step { .steps .step {
display: grid; display: grid;
grid-gap: 10px 0px; grid-gap: 10px 0px;
grid-template-columns: 30px auto; grid-template-columns: 30px auto 100px;
padding: 10px 15px; padding: 10px 15px;
} }
@ -1382,6 +1382,16 @@ a.card {
display: none; display: none;
} }
.steps .status-name {
align-items: center;
color: rgba(30,55,90,.6);
display: flex;
font-size: 14px;
font-style: italic;
justify-content: flex-end;
text-transform: capitalize;
}
/** /**
* breadcrumb component * breadcrumb component
*/ */

@ -2,7 +2,9 @@
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="refresh" content="10"> {{- if not (done .Stage.Status) }}
<meta http-equiv="refresh" content="30">
{{- end }}
<title>Dashboard</title> <title>Dashboard</title>
<link rel="stylesheet" type="text/css" href="/static/reset.css"> <link rel="stylesheet" type="text/css" href="/static/reset.css">
<link rel="stylesheet" type="text/css" href="/static/style.css"> <link rel="stylesheet" type="text/css" href="/static/style.css">
@ -61,20 +63,34 @@
{{ if .Stage.Steps }} {{ if .Stage.Steps }}
<div class="card steps"> <div class="card steps">
<header>
<span class="status {{ .Stage.Status }}"></span>
<span class="name">{{ .Stage.Name }}</span>
</header>
<div class="body"> <div class="body">
{{ range .Stage.Steps }} {{ range .Stage.Steps }}
<div class="step"> <div class="step">
<span class="status {{ .Status }}"></span> <span class="status {{ .Status }}"></span>
<span class="name"> {{ .Name }}</span> <span class="name">{{ .Name }}</span>
<span class="status-name">{{ .Status }}</span>
</div> </div>
{{ end }} {{ end }}
</div> </div>
</div> </div>
{{ end }} {{ end }}
{{ if .Logs }}
<div class="logs">
{{ range .Logs }}
<div class="entry">
<span class="level {{ .Level }}">{{ .Level }}</span>
<span class="message">{{ .Message }}</span>
<span class="fields">
{{ range $key, $val := .Data }}
<span><em>{{ $key }}</em>{{ $val }}</span>
{{ end }}
</span>
<span class="time" datetime="{{ timestamp .Unix }}"></span>
</div>
{{ end }}
</div>
{{ end }}
</section> </section>
</main> </main>

@ -103,4 +103,7 @@ var funcMap = map[string]interface{}{
"tag": func(s string) string { "tag": func(s string) string {
return strings.TrimPrefix(s, "refs/tags/") return strings.TrimPrefix(s, "refs/tags/")
}, },
"done": func(s string) bool {
return s != "pending" && s != "running"
},
} }

@ -33,4 +33,7 @@ var funcMap = map[string]interface{}{
"tag": func(s string) string { "tag": func(s string) string {
return strings.TrimPrefix(s, "refs/tags/") return strings.TrimPrefix(s, "refs/tags/")
}, },
"done": func(s string) bool {
return s != "pending" && s != "running"
},
} }

@ -164,7 +164,9 @@ var stage = `<!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="refresh" content="10"> {{- if not (done .Stage.Status) }}
<meta http-equiv="refresh" content="30">
{{- end }}
<title>Dashboard</title> <title>Dashboard</title>
<link rel="stylesheet" type="text/css" href="/static/reset.css"> <link rel="stylesheet" type="text/css" href="/static/reset.css">
<link rel="stylesheet" type="text/css" href="/static/style.css"> <link rel="stylesheet" type="text/css" href="/static/style.css">
@ -223,20 +225,34 @@ var stage = `<!DOCTYPE html>
{{ if .Stage.Steps }} {{ if .Stage.Steps }}
<div class="card steps"> <div class="card steps">
<header>
<span class="status {{ .Stage.Status }}"></span>
<span class="name">{{ .Stage.Name }}</span>
</header>
<div class="body"> <div class="body">
{{ range .Stage.Steps }} {{ range .Stage.Steps }}
<div class="step"> <div class="step">
<span class="status {{ .Status }}"></span> <span class="status {{ .Status }}"></span>
<span class="name"> {{ .Name }}</span> <span class="name">{{ .Name }}</span>
<span class="status-name">{{ .Status }}</span>
</div> </div>
{{ end }} {{ end }}
</div> </div>
</div> </div>
{{ end }} {{ end }}
{{ if .Logs }}
<div class="logs">
{{ range .Logs }}
<div class="entry">
<span class="level {{ .Level }}">{{ .Level }}</span>
<span class="message">{{ .Message }}</span>
<span class="fields">
{{ range $key, $val := .Data }}
<span><em>{{ $key }}</em>{{ $val }}</span>
{{ end }}
</span>
<span class="time" datetime="{{ timestamp .Unix }}"></span>
</div>
{{ end }}
</div>
{{ end }}
</section> </section>
</main> </main>

@ -15,7 +15,7 @@
}, },
"Stage": { "Stage": {
"Name": "test", "Name": "test",
"Status": "success", "Status": "running",
"Started": 1563059000, "Started": 1563059000,
"Created": 1563059000, "Created": 1563059000,
"Steps": [ "Steps": [
@ -24,5 +24,71 @@
{ "Name": "test", "Status": "success" }, { "Name": "test", "Status": "success" },
{ "Name": "deploy", "Status": "success" } { "Name": "deploy", "Status": "success" }
] ]
} },
"Logs": [
{
"Level": "debug",
"Message": "updated stage to running",
"Data": {
"build.id": 110,
"build.number": 110,
"repo.id": 48,
"repo.name": "hello-world",
"repo.namespace": "octocat",
"stage.id": 110,
"stage.name": "test",
"stage.number": 1,
"thread": 1
},
"Unix": 1563058875
},
{
"Level": "debug",
"Message": "process started",
"Data": {
"build.id": 110,
"build.number": 110,
"repo.id": 48,
"repo.name": "hello-world",
"repo.namespace": "octocat",
"stage.id": 110,
"stage.name": "test",
"stage.number": 1,
"thread": 1
},
"Unix": 1563058875
},
{
"Level": "debug",
"Message": "process finished",
"Data": {
"build.id": 110,
"build.number": 110,
"repo.id": 48,
"repo.name": "hello-world",
"repo.namespace": "octocat",
"stage.id": 110,
"stage.name": "test",
"stage.number": 1,
"thread": 1
},
"Unix": 1563058975
},
{
"Level": "debug",
"Message": "updated stage to complete",
"Data": {
"build.id": 110,
"build.number": 110,
"repo.id": 48,
"repo.name": "hello-world",
"repo.namespace": "octocat",
"stage.id": 110,
"stage.name": "test",
"stage.number": 1,
"thread": 1
},
"Unix": 1563058977
}
]
} }

@ -85,6 +85,20 @@ func (h *Hook) Entries() []*Entry {
return entries return entries
} }
// Filter returns a list of all entries for which the filter
// function returns true.
func (h *Hook) Filter(filter func(*Entry) bool) []*Entry {
h.RLock()
defer h.RUnlock()
var entries []*Entry
for _, entry := range h.entries {
if filter(entry) {
entries = append(entries, copyEntry(entry))
}
}
return entries
}
// helper funtion copies an entry for threadsafe access. // helper funtion copies an entry for threadsafe access.
func copyEntry(src *Entry) *Entry { func copyEntry(src *Entry) *Entry {
dst := new(Entry) dst := new(Entry)

@ -107,3 +107,38 @@ func TestHistory(t *testing.T) {
t.Log(diff) t.Log(diff)
} }
} }
func TestFilter(t *testing.T) {
hook := New()
now := time.Now()
hook.Fire(&logrus.Entry{
Level: logrus.DebugLevel,
Message: "foo",
Data: logrus.Fields{"foo": "bar"},
Time: now,
})
hook.Fire(&logrus.Entry{
Level: logrus.InfoLevel,
Message: "bar",
Data: logrus.Fields{"baz": "qux"},
Time: now,
})
expect := []*Entry{
{
Level: LevelDebug,
Message: "foo",
Data: logrus.Fields{"foo": "bar"},
Unix: now.Unix(),
},
}
entries := hook.Filter(func(entry *Entry) bool {
return entry.Data["foo"] == "bar"
})
if diff := cmp.Diff(entries, expect); diff != "" {
t.Errorf("Entries should return an exact copy of all entries")
t.Log(diff)
}
}

@ -64,6 +64,20 @@ func (h *History) Entries() []*Entry {
return entries return entries
} }
// Entry returns the entry by id.
func (h *History) Entry(id int64) *Entry {
h.Lock()
defer h.Unlock()
for _, src := range h.items {
if src.Stage.ID == id {
dst := new(Entry)
*dst = *src
return dst
}
}
return nil
}
// Limit returns the history limit. // Limit returns the history limit.
func (h *History) Limit() int { func (h *History) Limit() int {
if h.limit == 0 { if h.limit == 0 {

@ -116,6 +116,21 @@ func TestEntries(t *testing.T) {
} }
} }
func TestEntry(t *testing.T) {
s1 := &drone.Stage{ID: 1}
s2 := &drone.Stage{ID: 2}
v := History{}
v.items = append(v.items, &Entry{Stage: s1}, &Entry{Stage: s2})
if got := v.Entry(99); got != nil {
t.Errorf("Want nil when stage not found")
}
if got := v.Entry(s1.ID); got == nil {
t.Errorf("Want entry by stage ID, got nil")
return
}
}
func TestLimit(t *testing.T) { func TestLimit(t *testing.T) {
v := History{} v := History{}
if got, want := v.Limit(), defaultLimit; got != want { if got, want := v.Limit(), defaultLimit; got != want {

Loading…
Cancel
Save