// Copyright 2023 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "bytes" "cmp" "fmt" "html/template" "internal/trace" "internal/trace/traceviewer" "log" "net/http" "slices" "strings" "time" ) // UserTasksHandlerFunc returns a HandlerFunc that reports all tasks found in the trace. func UserTasksHandlerFunc(t *parsedTrace) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { tasks := t.summary.Tasks // Summarize groups of tasks with the same name. summary := make(map[string]taskStats) for _, task := range tasks { stats, ok := summary[task.Name] if !ok { stats.Type = task.Name } stats.add(task) summary[task.Name] = stats } // Sort tasks by type. userTasks := make([]taskStats, 0, len(summary)) for _, stats := range summary { userTasks = append(userTasks, stats) } slices.SortFunc(userTasks, func(a, b taskStats) int { return cmp.Compare(a.Type, b.Type) }) // Emit table. err := templUserTaskTypes.Execute(w, userTasks) if err != nil { http.Error(w, fmt.Sprintf("failed to execute template: %v", err), http.StatusInternalServerError) return } } } type taskStats struct { Type string Count int // Complete + incomplete tasks Histogram traceviewer.TimeHistogram // Complete tasks only } func (s *taskStats) UserTaskURL(complete bool) func(min, max time.Duration) string { return func(min, max time.Duration) string { return fmt.Sprintf("/usertask?type=%s&complete=%v&latmin=%v&latmax=%v", template.URLQueryEscaper(s.Type), template.URLQueryEscaper(complete), template.URLQueryEscaper(min), template.URLQueryEscaper(max)) } } func (s *taskStats) add(task *trace.UserTaskSummary) { s.Count++ if task.Complete() { s.Histogram.Add(task.End.Time().Sub(task.Start.Time())) } } var templUserTaskTypes = template.Must(template.New("").Parse(`
Task type | Count | Duration distribution (complete tasks) |
---|---|---|
{{.Type}} | {{.Count}} | {{.Histogram.ToHTML (.UserTaskURL true)}} |