package tabacco import ( "html/template" "log" "net/http" "net/http/pprof" "time" "" ) var ( headerTpl = ` Tabacco - Debug ` indexDebugTpl = `{{template "header"}}


job status



{{template "footer"}} ` stateManagerDebugTpl = `{{template "header"}} {{define "job_status"}} {{range .}} {{end}}
ID Name Status Started At Completed At Error
{{.ID}} {{.Name}} {{.Status}} {{if not .StartedAt.IsZero}}{{timefmt .StartedAt}}{{end}} {{if not .CompletedAt.IsZero}}{{timefmt .CompletedAt}}{{end}} {{if .Err}}{{.Err}}{{else if not .CompletedAt.IsZero}}ok{{end}}


Running ({{.NumRunning}})

{{template "job_status" .Running}}

Pending ({{.NumPending}})

{{template "job_status" .Pending}}

Done ({{.NumDone}})

{{template "job_status" .Done}} {{template "footer"}} ` schedulerDebugTpl = `{{template "header"}} {{define "schedule_status"}} {{range .}} {{end}}
Name Schedule Last Run Next Run
{{.Name}} {{.Schedule}} {{if not .Prev.IsZero}}{{timefmt .Prev}}{{end}} {{if not .Next.IsZero}}{{timefmt .Next}}{{end}} {{if .LastError}}{{.LastError}}{{else if not .Prev.IsZero}}ok{{end}}


{{template "schedule_status" .Schedule}} {{template "footer"}} ` footerTpl = ` ` debugTpl *template.Template ) func timefmt(t time.Time) string { return t.Format(time.Stamp) } func init() { debugTpl = template.New("").Funcs(template.FuncMap{ "timefmt": timefmt, }) template.Must(debugTpl.New("header").Parse(headerTpl)) template.Must(debugTpl.New("footer").Parse(footerTpl)) template.Must(debugTpl.New("index").Parse(indexDebugTpl)) template.Must(debugTpl.New("state_manager_debug_page").Parse(stateManagerDebugTpl)) template.Must(debugTpl.New("scheduler_debug_page").Parse(schedulerDebugTpl)) } // Job status debug handler. func (a *Agent) handleStateManagerDebug(w http.ResponseWriter, r *http.Request) { pending, running, done := a.mgr.GetStatus() w.Header().Set("Content-Type", "text/html") if err := debugTpl.Lookup("state_manager_debug_page").Execute(w, map[string]interface{}{ "Pending": pending, "NumPending": len(pending), "Running": running, "NumRunning": len(running), "Done": done, "NumDone": len(done), }); err != nil { log.Printf("state_manager_debug_page: %v", err) } } // Scheduler debug handler. func (a *Agent) handleSchedulerDebug(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") if err := debugTpl.Lookup("scheduler_debug_page").Execute(w, map[string]interface{}{ "Schedule": a.sched.GetStatus(), }); err != nil { log.Printf("scheduler_debug_page: %v", err) } } // Agent debug handler page, with links to the other two. func (a *Agent) handleDebugPage(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { http.NotFound(w, r) return } w.Header().Set("Content-Type", "text/html") debugTpl.Lookup("index").Execute(w, nil) // nolint } // StartHTTPServer starts a HTTP server that exports Prometheus // metrics and debug information. func (a *Agent) StartHTTPServer(addr string) { h := http.NewServeMux() h.HandleFunc("/debug/jobs", a.handleStateManagerDebug) h.HandleFunc("/debug/sched", a.handleSchedulerDebug) // Add all the pprof handlers, they're useful for debugging. h.HandleFunc("/debug/pprof/", pprof.Index) h.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline) h.HandleFunc("/debug/pprof/profile", pprof.Profile) h.HandleFunc("/debug/pprof/symbol", pprof.Symbol) h.HandleFunc("/debug/pprof/trace", pprof.Trace) h.Handle("/metrics", promhttp.Handler()) h.HandleFunc("/", a.handleDebugPage) go http.ListenAndServe(addr, h) // nolint }