Adding lite and full tracking modes; Using atomic variables; Resetting the map only by explicit command using safe operation
This commit is contained in:
parent
421eeb3750
commit
cbd23c5903
110
res_tracker.go
110
res_tracker.go
|
@ -7,15 +7,20 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const maxStackLen = 1000
|
const maxStackLen = 1000
|
||||||
|
|
||||||
//NOTE: for simplicity not making it an atomic variable
|
const OFF = 0
|
||||||
var enabled bool = false
|
const LITE = 1
|
||||||
|
const FULL = 2
|
||||||
|
|
||||||
|
var mode int32 = OFF
|
||||||
var res2track = &sync.Map{}
|
var res2track = &sync.Map{}
|
||||||
|
|
||||||
type TrackedInfo struct {
|
type TrackedInfo struct {
|
||||||
|
@ -38,12 +43,19 @@ func InitHandlers() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func onHandler(w http.ResponseWriter, r *http.Request) {
|
func onHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
Enable(true)
|
query := r.URL.Query()
|
||||||
fmt.Fprintf(w, "ON\n")
|
m := query.Get("m")
|
||||||
|
if len(m) == 0 || m == "lite" {
|
||||||
|
Enable(LITE)
|
||||||
|
fmt.Fprintf(w, "ON(lite)\n")
|
||||||
|
} else if m == "full" {
|
||||||
|
Enable(FULL)
|
||||||
|
fmt.Fprintf(w, "ON(full)\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func offHandler(w http.ResponseWriter, r *http.Request) {
|
func offHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
Enable(false)
|
Enable(OFF)
|
||||||
fmt.Fprintf(w, "OFF\n")
|
fmt.Fprintf(w, "OFF\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,31 +71,27 @@ func resetHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func statusHandler(w http.ResponseWriter, r *http.Request) {
|
func statusHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
fmt.Fprintf(w, "enabled: %v, entries: %d\n", enabled, Count())
|
fmt.Fprintf(w, "mode: %v, entries: %d\n", atomic.LoadInt32(&mode), Count())
|
||||||
}
|
}
|
||||||
|
|
||||||
func Enable(flag bool) {
|
func Enable(m int32) {
|
||||||
enabled = flag
|
atomic.StoreInt32(&mode, m)
|
||||||
if enabled {
|
|
||||||
Reset()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Reset() {
|
func Reset() {
|
||||||
res2track = &sync.Map{}
|
res2track.Range(func(k, v interface{}) bool {
|
||||||
|
res2track.Delete(k)
|
||||||
|
return true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsOn() bool {
|
func IsOn() bool {
|
||||||
return enabled
|
return atomic.LoadInt32(&mode) != OFF
|
||||||
}
|
}
|
||||||
|
|
||||||
func Count() int {
|
func Count() int {
|
||||||
return count(res2track)
|
|
||||||
}
|
|
||||||
|
|
||||||
func count(m *sync.Map) int {
|
|
||||||
var i int
|
var i int
|
||||||
m.Range(func(k, v interface{}) bool {
|
res2track.Range(func(k, v interface{}) bool {
|
||||||
i++
|
i++
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
@ -91,55 +99,75 @@ func count(m *sync.Map) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Track(o interface{}) {
|
func Track(o interface{}) {
|
||||||
if !enabled {
|
m := atomic.LoadInt32(&mode)
|
||||||
|
if m == OFF {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
stack := debug.Stack()
|
var stack []byte = nil
|
||||||
|
if m == FULL {
|
||||||
|
stack = debug.Stack()
|
||||||
|
}
|
||||||
res2track.Store(o, TrackedInfo{time.Now(), stack})
|
res2track.Store(o, TrackedInfo{time.Now(), stack})
|
||||||
}
|
}
|
||||||
|
|
||||||
func Untrack(o interface{}) {
|
func Untrack(o interface{}) {
|
||||||
if !enabled {
|
if atomic.LoadInt32(&mode) == OFF {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
res2track.Delete(o)
|
res2track.Delete(o)
|
||||||
}
|
}
|
||||||
|
|
||||||
func FormatReportItems() []TrackReportItem {
|
func FormatReportItems() []TrackReportItem {
|
||||||
var items []TrackReportItem
|
|
||||||
|
|
||||||
parensReg := regexp.MustCompile(`\([^\)]+\)`)
|
parensReg := regexp.MustCompile(`\([^\)]+\)`)
|
||||||
fileReg := regexp.MustCompile(`at\s+.*?/([^/]+\.go:\d+)`)
|
fileReg := regexp.MustCompile(`at\s+.*?/([^/]+\.go:\d+)`)
|
||||||
|
|
||||||
|
//1. let's get a quick snapshot of the map
|
||||||
|
var snapshot []interface{}
|
||||||
res2track.Range(func(k, v interface{}) bool {
|
res2track.Range(func(k, v interface{}) bool {
|
||||||
|
snapshot = append(snapshot, k)
|
||||||
|
snapshot = append(snapshot, v)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
//2. let's add formatted items
|
||||||
|
var items []TrackReportItem
|
||||||
|
for i := 0; i < len(snapshot); i += 2 {
|
||||||
|
k := snapshot[i]
|
||||||
|
v := snapshot[i+1]
|
||||||
|
|
||||||
t := reflect.TypeOf(k)
|
t := reflect.TypeOf(k)
|
||||||
info, _ := v.(TrackedInfo)
|
info, _ := v.(TrackedInfo)
|
||||||
stackStr := string(info.Stack)
|
stackStr := string(info.Stack)
|
||||||
|
|
||||||
//let's remove first stack entries
|
if len(stackStr) > 0 {
|
||||||
for i := 0; i < 5; i++ {
|
//let's remove first stack entries
|
||||||
newLineIdx := strings.Index(stackStr, "\n")
|
for i := 0; i < 5; i++ {
|
||||||
if newLineIdx != -1 {
|
newLineIdx := strings.Index(stackStr, "\n")
|
||||||
stackStr = stackStr[newLineIdx+1:]
|
if newLineIdx != -1 {
|
||||||
} else {
|
stackStr = stackStr[newLineIdx+1:]
|
||||||
break
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stackStr = strings.ReplaceAll(stackStr, " ", " ")
|
||||||
|
stackStr = strings.ReplaceAll(stackStr, "\n", " < ")
|
||||||
|
stackStr = strings.ReplaceAll(stackStr, ") <", ") at")
|
||||||
|
stackStr = parensReg.ReplaceAllString(stackStr, "(..)")
|
||||||
|
stackStr = fileReg.ReplaceAllString(stackStr, "../$1")
|
||||||
|
if len(stackStr) > maxStackLen {
|
||||||
|
stackStr = stackStr[:maxStackLen] + "..."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stackStr = strings.ReplaceAll(stackStr, " ", " ")
|
|
||||||
stackStr = strings.ReplaceAll(stackStr, "\n", " < ")
|
|
||||||
stackStr = strings.ReplaceAll(stackStr, ") <", ") at")
|
|
||||||
stackStr = parensReg.ReplaceAllString(stackStr, "(..)")
|
|
||||||
stackStr = fileReg.ReplaceAllString(stackStr, "../$1")
|
|
||||||
if len(stackStr) > maxStackLen {
|
|
||||||
stackStr = stackStr[:maxStackLen] + "..."
|
|
||||||
}
|
|
||||||
|
|
||||||
items = append(items, TrackReportItem{T: t, Duration: time.Now().Sub(info.Time), Stack: stackStr})
|
items = append(items, TrackReportItem{T: t, Duration: time.Now().Sub(info.Time), Stack: stackStr})
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
//3. let's sort items by duration descending
|
||||||
|
sort.Slice(items, func(i, j int) bool {
|
||||||
|
return items[i].Duration > items[j].Duration
|
||||||
})
|
})
|
||||||
|
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,8 +177,10 @@ func ReportLines() []string {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
items := FormatReportItems()
|
items := FormatReportItems()
|
||||||
|
|
||||||
lines = append(lines, fmt.Sprintf("=== Tracked connections report %s (total: %d) ===", now.Format("01-02-2006 15:04:05"), len(items)))
|
lines = append(lines, fmt.Sprintf("=== Tracked connections report %s (total: %d) ===", now.Format("01-02-2006 15:04:05"), len(items)))
|
||||||
for idx, item := range items {
|
for idx, item := range items {
|
||||||
|
//TODO: obtain somehow an actual pointer of the object or provide a hook for a custom formatter
|
||||||
lines = append(lines, fmt.Sprintf("#%d Tracked (%v, duration %v) : %s", idx+1, item.T, item.Duration, item.Stack))
|
lines = append(lines, fmt.Sprintf("#%d Tracked (%v, duration %v) : %s", idx+1, item.T, item.Duration, item.Stack))
|
||||||
}
|
}
|
||||||
return lines
|
return lines
|
||||||
|
|
Loading…
Reference in New Issue