github.com/grahambrereton-form3/tilt@v0.10.18/integration/stats_server.go (about) 1 package integration 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "log" 8 "net" 9 "net/http" 10 "time" 11 12 "github.com/gorilla/mux" 13 "github.com/windmilleng/wmclient/pkg/analytics" 14 ) 15 16 type MemoryStatsServer struct { 17 ma *analytics.MemoryAnalytics 18 ss StatsServer 19 listener net.Listener 20 } 21 22 func StartMemoryStatsServer() (mss *MemoryStatsServer, port int, err error) { 23 mss = &MemoryStatsServer{} 24 mss.ma = analytics.NewMemoryAnalytics() 25 sr := &MemoryStatsReporter{mss.ma} 26 mss.ss = NewStatsServer(sr) 27 28 mux := http.NewServeMux() 29 mux.Handle("/", mss.ss.Router()) 30 31 mss.listener, err = net.Listen("tcp", ":0") 32 if err != nil { 33 return nil, 0, err 34 } 35 36 go func() { 37 err := http.Serve(mss.listener, mux) 38 if err != nil { 39 fmt.Println(err) 40 } 41 }() 42 43 return mss, mss.listener.Addr().(*net.TCPAddr).Port, nil 44 } 45 46 func (mss *MemoryStatsServer) TearDown() error { 47 return mss.listener.Close() 48 } 49 50 type MemoryStatsReporter struct { 51 a *analytics.MemoryAnalytics 52 } 53 54 var _ StatsReporter = &MemoryStatsReporter{} 55 56 func (sr *MemoryStatsReporter) Close() error { 57 return nil 58 } 59 60 func (sr *MemoryStatsReporter) Timing(name string, value time.Duration, tags map[string]string, rate float64) error { 61 sr.a.Timer(name, value, tags) 62 return nil 63 } 64 65 func (sr *MemoryStatsReporter) Count(name string, value int64, tags map[string]string, rate float64) error { 66 sr.a.Count(name, tags, int(value)) 67 return nil 68 } 69 70 func (sr *MemoryStatsReporter) Incr(name string, tags map[string]string, rate float64) error { 71 sr.a.Incr(name, tags) 72 return nil 73 } 74 75 const keyTime = "time" 76 77 type StatsReporter interface { 78 io.Closer 79 Timing(name string, value time.Duration, tags map[string]string, rate float64) error 80 Count(name string, value int64, tags map[string]string, rate float64) error 81 Incr(name string, tags map[string]string, rate float64) error 82 } 83 84 // A small http server that decodes json and sends it to our metrics services 85 type StatsServer struct { 86 router *mux.Router 87 reporter StatsReporter 88 } 89 90 func NewStatsServer(stats StatsReporter) StatsServer { 91 r := mux.NewRouter().UseEncodedPath() 92 93 s := StatsServer{router: r, reporter: stats} 94 r.HandleFunc("/report", s.Report).Methods("POST") 95 r.HandleFunc("/", s.Index).Methods("GET") 96 return s 97 } 98 99 func (s StatsServer) Router() *mux.Router { 100 return s.router 101 } 102 103 func (s StatsServer) Index(w http.ResponseWriter, r *http.Request) { 104 _, _ = w.Write([]byte("OK")) 105 } 106 107 func (s StatsServer) Report(w http.ResponseWriter, r *http.Request) { 108 now := time.Now() 109 110 events, err := parseJSON(r) 111 if err != nil { 112 http.Error(w, fmt.Sprintf("JSON decode: %v", err), http.StatusBadRequest) 113 return 114 } 115 116 for _, event := range events { 117 name := event.Name() 118 if name == "" { 119 http.Error(w, fmt.Sprintf("Missing name: %v", event), http.StatusBadRequest) 120 return 121 } 122 123 event[keyTime] = now.Format(time.RFC3339) 124 125 dur := event.Duration() 126 if dur == 0 { 127 // TODO: support count 128 129 err := s.reporter.Incr(name, event.Tags(), 1) 130 if err != nil { 131 log.Printf("Report error: %v", err) 132 } 133 } else { 134 err := s.reporter.Timing(name, dur, event.Tags(), 1) 135 if err != nil { 136 log.Printf("Report error: %v", err) 137 } 138 } 139 140 } 141 } 142 143 type Event map[string]interface{} 144 145 func (e Event) Name() string { 146 name, ok := e["name"].(string) 147 if !ok { 148 return "" 149 } 150 return name 151 } 152 153 func (e Event) Duration() time.Duration { 154 // all json numbers are float64 155 dur, ok := e["duration"].(float64) 156 if !ok { 157 return 0 158 } 159 return time.Duration(dur) 160 } 161 162 func (e Event) Tags() map[string]string { 163 tags := make(map[string]string) 164 for k, v := range e { 165 if k == "name" || k == "duration" { 166 continue 167 } 168 if vStr, ok := v.(string); ok { 169 tags[k] = vStr 170 } 171 } 172 return tags 173 } 174 175 func parseJSON(r *http.Request) ([]Event, error) { 176 d := json.NewDecoder(r.Body) 177 result := make([]Event, 0) 178 for d.More() { 179 data := make(map[string]interface{}) 180 err := d.Decode(&data) 181 if err != nil { 182 return nil, err 183 } 184 185 result = append(result, Event(data)) 186 } 187 return result, nil 188 }