github.com/anakojm/hugo-katex@v0.0.0-20231023141351-42d6f5de9c0b/tpl/debug/debug.go (about) 1 // Copyright 2020 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 // Package debug provides template functions to help debugging templates. 15 package debug 16 17 import ( 18 "sort" 19 "sync" 20 "time" 21 22 "github.com/bep/logg" 23 "github.com/sanity-io/litter" 24 "github.com/spf13/cast" 25 "github.com/yuin/goldmark/util" 26 27 "github.com/gohugoio/hugo/deps" 28 ) 29 30 // New returns a new instance of the debug-namespaced template functions. 31 func New(d *deps.Deps) *Namespace { 32 var timers map[string][]*timer 33 if d.Log.Level() <= logg.LevelInfo { 34 timers = make(map[string][]*timer) 35 } 36 ns := &Namespace{ 37 timers: timers, 38 } 39 40 if ns.timers == nil { 41 return ns 42 } 43 44 l := d.Log.InfoCommand("timer") 45 46 d.BuildEndListeners.Add(func() { 47 type data struct { 48 Name string 49 Count int 50 Average time.Duration 51 Median time.Duration 52 Duration time.Duration 53 } 54 55 var timersSorted []data 56 57 for k, v := range timers { 58 var total time.Duration 59 var median time.Duration 60 sort.Slice(v, func(i, j int) bool { 61 return v[i].elapsed < v[j].elapsed 62 }) 63 if len(v) > 0 { 64 median = v[len(v)/2].elapsed 65 } 66 for _, t := range v { 67 // Stop any running timers. 68 t.Stop() 69 total += t.elapsed 70 71 } 72 average := total / time.Duration(len(v)) 73 timersSorted = append(timersSorted, data{k, len(v), average, median, total}) 74 } 75 76 sort.Slice(timersSorted, func(i, j int) bool { 77 // Sort it so the slowest gets printed last. 78 return timersSorted[i].Duration < timersSorted[j].Duration 79 }) 80 81 for _, t := range timersSorted { 82 l.WithField("name", t.Name).WithField("count", t.Count). 83 WithField("duration", t.Duration). 84 WithField("average", t.Average). 85 WithField("median", t.Median).Logf("") 86 } 87 88 ns.timers = make(map[string][]*timer) 89 }) 90 91 return ns 92 } 93 94 // Namespace provides template functions for the "debug" namespace. 95 type Namespace struct { 96 timersMu sync.Mutex 97 timers map[string][]*timer 98 } 99 100 // Dump returns a object dump of val as a string. 101 // Note that not every value passed to Dump will print so nicely, but 102 // we'll improve on that. 103 // 104 // We recommend using the "go" Chroma lexer to format the output 105 // nicely. 106 // 107 // Also note that the output from Dump may change from Hugo version to the next, 108 // so don't depend on a specific output. 109 func (ns *Namespace) Dump(val any) string { 110 return litter.Sdump(val) 111 } 112 113 // VisualizeSpaces returns a string with spaces replaced by a visible string. 114 func (ns *Namespace) VisualizeSpaces(val any) string { 115 s := cast.ToString(val) 116 return string(util.VisualizeSpaces([]byte(s))) 117 } 118 119 func (ns *Namespace) Timer(name string) Timer { 120 if ns.timers == nil { 121 return nopTimer 122 } 123 ns.timersMu.Lock() 124 defer ns.timersMu.Unlock() 125 t := &timer{start: time.Now()} 126 ns.timers[name] = append(ns.timers[name], t) 127 return t 128 } 129 130 var nopTimer = nopTimerImpl{} 131 132 type nopTimerImpl struct{} 133 134 func (nopTimerImpl) Stop() string { 135 return "" 136 } 137 138 // Timer is a timer that can be stopped. 139 type Timer interface { 140 // Stop stops the timer and returns an empty string. 141 // Stop can be called multiple times, but only the first call will stop the timer. 142 // If Stop is not called, the timer will be stopped when the build ends. 143 Stop() string 144 } 145 146 type timer struct { 147 start time.Time 148 elapsed time.Duration 149 stopOnce sync.Once 150 } 151 152 func (t *timer) Stop() string { 153 t.stopOnce.Do(func() { 154 t.elapsed = time.Since(t.start) 155 }) 156 // This is used in templates, we need to return something. 157 return "" 158 }