First commit
This commit is contained in:
commit
077086ec59
|
@ -0,0 +1,175 @@
|
|||
package res_tracker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const maxStackLen = 1000
|
||||
|
||||
//NOTE: for simplicity not making it an atomic variable
|
||||
var enabled bool = false
|
||||
var res2track = sync.Map{}
|
||||
|
||||
type TrackedInfo struct {
|
||||
Time time.Time
|
||||
Stack []byte
|
||||
}
|
||||
|
||||
type TrackReportItem struct {
|
||||
T reflect.Type
|
||||
Duration time.Duration
|
||||
Stack string
|
||||
}
|
||||
|
||||
func InitHandlers() {
|
||||
http.HandleFunc("/res_tracker/on", onHandler)
|
||||
http.HandleFunc("/res_tracker/off", offHandler)
|
||||
http.HandleFunc("/res_tracker/report", reportHandler)
|
||||
http.HandleFunc("/res_tracker/reset", resetHandler)
|
||||
http.HandleFunc("/res_tracker/status", statusHandler)
|
||||
}
|
||||
|
||||
func onHandler(w http.ResponseWriter, r *http.Request) {
|
||||
Enable(true)
|
||||
fmt.Fprintf(w, "ON\n")
|
||||
}
|
||||
|
||||
func offHandler(w http.ResponseWriter, r *http.Request) {
|
||||
Enable(false)
|
||||
fmt.Fprintf(w, "OFF\n")
|
||||
}
|
||||
|
||||
func reportHandler(w http.ResponseWriter, r *http.Request) {
|
||||
for _, line := range ReportLines() {
|
||||
fmt.Fprintf(w, line+"\n")
|
||||
}
|
||||
}
|
||||
|
||||
func resetHandler(w http.ResponseWriter, r *http.Request) {
|
||||
Reset()
|
||||
fmt.Fprintf(w, "Reset\n")
|
||||
}
|
||||
|
||||
func statusHandler(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "enabled: %v, entries: %d\n", enabled, Count())
|
||||
}
|
||||
|
||||
func Enable(flag bool) {
|
||||
enabled = flag
|
||||
if enabled {
|
||||
Reset()
|
||||
}
|
||||
}
|
||||
|
||||
func Reset() {
|
||||
res2track = sync.Map{}
|
||||
}
|
||||
|
||||
func IsOn() bool {
|
||||
return enabled
|
||||
}
|
||||
|
||||
func Count() int {
|
||||
return count(res2track)
|
||||
}
|
||||
|
||||
func count(m sync.Map) int {
|
||||
var i int
|
||||
m.Range(func(k, v interface{}) bool {
|
||||
i++
|
||||
return true
|
||||
})
|
||||
return i
|
||||
}
|
||||
|
||||
func Track(o interface{}) {
|
||||
if !enabled {
|
||||
return
|
||||
}
|
||||
stack := debug.Stack()
|
||||
res2track.Store(o, TrackedInfo{time.Now(), stack})
|
||||
}
|
||||
|
||||
func Untrack(o interface{}) {
|
||||
if !enabled {
|
||||
return
|
||||
}
|
||||
res2track.Delete(o)
|
||||
}
|
||||
|
||||
func FormatReportItems() []TrackReportItem {
|
||||
var items []TrackReportItem
|
||||
|
||||
parensReg := regexp.MustCompile(`\([^\)]+\)`)
|
||||
fileReg := regexp.MustCompile(`at\s+.*?/([^/]+\.go:\d+)`)
|
||||
|
||||
res2track.Range(func(k, v interface{}) bool {
|
||||
|
||||
t := reflect.TypeOf(k)
|
||||
info, _ := v.(TrackedInfo)
|
||||
stackStr := string(info.Stack)
|
||||
|
||||
//let's remove first stack entries
|
||||
for i := 0; i < 5; i++ {
|
||||
newLineIdx := strings.Index(stackStr, "\n")
|
||||
if newLineIdx != -1 {
|
||||
stackStr = stackStr[newLineIdx+1:]
|
||||
} 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] + "..."
|
||||
}
|
||||
|
||||
items = append(items, TrackReportItem{T: t, Duration: time.Now().Sub(info.Time), Stack: stackStr})
|
||||
|
||||
return true
|
||||
})
|
||||
return items
|
||||
}
|
||||
|
||||
func ReportLines() []string {
|
||||
var lines []string
|
||||
|
||||
now := time.Now()
|
||||
|
||||
items := FormatReportItems()
|
||||
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 {
|
||||
lines = append(lines, fmt.Sprintf("#%d Tracked (%v, duration %v) : %s", idx+1, item.T, item.Duration, item.Stack))
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
func ReportToFile(file string) error {
|
||||
f, err := os.OpenFile(file, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, line := range ReportLines() {
|
||||
if _, err := f.WriteString(line + "\n"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReportToTmpFile() (string, error) {
|
||||
file := fmt.Sprintf("/tmp/track_report.%d", os.Getpid())
|
||||
return file, ReportToFile(file)
|
||||
}
|
Loading…
Reference in New Issue