mirror of
https://github.com/chubin/wttr.in
synced 2024-11-15 00:27:09 +00:00
Add /:stats support
This commit is contained in:
parent
8fd712f790
commit
ec264850a4
2 changed files with 116 additions and 40 deletions
|
@ -28,6 +28,8 @@ type RequestProcessor struct {
|
|||
peakRequest30 sync.Map
|
||||
peakRequest60 sync.Map
|
||||
lruCache *lru.Cache
|
||||
stats *Stats
|
||||
router Router
|
||||
}
|
||||
|
||||
// NewRequestProcessor returns new RequestProcessor.
|
||||
|
@ -37,9 +39,15 @@ func NewRequestProcessor() (*RequestProcessor, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
return &RequestProcessor{
|
||||
rp := &RequestProcessor{
|
||||
lruCache: lruCache,
|
||||
}, nil
|
||||
stats: NewStats(),
|
||||
}
|
||||
|
||||
// Initialize routes.
|
||||
rp.router.AddPath("/:stats", rp.stats)
|
||||
|
||||
return rp, nil
|
||||
}
|
||||
|
||||
// Start starts async request processor jobs, such as peak handling.
|
||||
|
@ -53,11 +61,23 @@ func (rp *RequestProcessor) ProcessRequest(r *http.Request) (*ResponseWithHeader
|
|||
err error
|
||||
)
|
||||
|
||||
rp.stats.Inc("total")
|
||||
|
||||
// Main routing logic.
|
||||
if rh := rp.router.Route(r); rh != nil {
|
||||
result := rh.Response(r)
|
||||
if result != nil {
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
if resp, ok := redirectInsecure(r); ok {
|
||||
rp.stats.Inc("redirects")
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
if dontCache(r) {
|
||||
rp.stats.Inc("uncached")
|
||||
return get(r)
|
||||
}
|
||||
|
||||
|
@ -69,6 +89,7 @@ func (rp *RequestProcessor) ProcessRequest(r *http.Request) (*ResponseWithHeader
|
|||
|
||||
cacheBody, ok := rp.lruCache.Get(cacheDigest)
|
||||
if ok {
|
||||
rp.stats.Inc("cache1")
|
||||
cacheEntry := cacheBody.(ResponseWithHeader)
|
||||
|
||||
// if after all attempts we still have no answer,
|
||||
|
@ -93,7 +114,17 @@ func (rp *RequestProcessor) ProcessRequest(r *http.Request) (*ResponseWithHeader
|
|||
}
|
||||
|
||||
if !foundInCache {
|
||||
// Handling query.
|
||||
format := r.URL.Query().Get("format")
|
||||
if len(format) != 0 {
|
||||
rp.stats.Inc("format")
|
||||
if format == "j1" {
|
||||
rp.stats.Inc("format=j1")
|
||||
}
|
||||
}
|
||||
|
||||
rp.lruCache.Add(cacheDigest, ResponseWithHeader{InProgress: true})
|
||||
|
||||
response, err = get(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
121
cmd/stat.go
121
cmd/stat.go
|
@ -1,40 +1,85 @@
|
|||
package main
|
||||
|
||||
// import (
|
||||
// "log"
|
||||
// "sync"
|
||||
// "time"
|
||||
// )
|
||||
//
|
||||
// type safeCounter struct {
|
||||
// v map[int]int
|
||||
// mux sync.Mutex
|
||||
// }
|
||||
//
|
||||
// func (c *safeCounter) inc(key int) {
|
||||
// c.mux.Lock()
|
||||
// c.v[key]++
|
||||
// c.mux.Unlock()
|
||||
// }
|
||||
//
|
||||
// // func (c *safeCounter) val(key int) int {
|
||||
// // c.mux.Lock()
|
||||
// // defer c.mux.Unlock()
|
||||
// // return c.v[key]
|
||||
// // }
|
||||
// //
|
||||
// // func (c *safeCounter) reset(key int) int {
|
||||
// // c.mux.Lock()
|
||||
// // defer c.mux.Unlock()
|
||||
// // result := c.v[key]
|
||||
// // c.v[key] = 0
|
||||
// // return result
|
||||
// // }
|
||||
//
|
||||
// var queriesPerMinute safeCounter
|
||||
//
|
||||
// func printStat() {
|
||||
// _, min, _ := time.Now().Clock()
|
||||
// queriesPerMinute.inc(min)
|
||||
// log.Printf("Processed %d requests\n", min)
|
||||
// }
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Stats holds processed requests statistics.
|
||||
type Stats struct {
|
||||
m sync.Mutex
|
||||
v map[string]int
|
||||
startTime time.Time
|
||||
}
|
||||
|
||||
// NewStats returns new Stats.
|
||||
func NewStats() *Stats {
|
||||
return &Stats{
|
||||
v: map[string]int{},
|
||||
startTime: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// Inc key by one.
|
||||
func (c *Stats) Inc(key string) {
|
||||
c.m.Lock()
|
||||
c.v[key]++
|
||||
c.m.Unlock()
|
||||
}
|
||||
|
||||
// Get current key counter value.
|
||||
func (c *Stats) Get(key string) int {
|
||||
c.m.Lock()
|
||||
defer c.m.Unlock()
|
||||
return c.v[key]
|
||||
}
|
||||
|
||||
// Reset key counter.
|
||||
func (c *Stats) Reset(key string) int {
|
||||
c.m.Lock()
|
||||
defer c.m.Unlock()
|
||||
result := c.v[key]
|
||||
c.v[key] = 0
|
||||
return result
|
||||
}
|
||||
|
||||
// Show returns current statistics formatted as []byte.
|
||||
func (c *Stats) Show() []byte {
|
||||
var (
|
||||
b bytes.Buffer
|
||||
)
|
||||
|
||||
c.m.Lock()
|
||||
defer c.m.Unlock()
|
||||
|
||||
uptime := time.Since(c.startTime) / time.Second
|
||||
|
||||
fmt.Fprintf(&b, "%-20s: %v\n", "Running since", c.startTime.Format(time.RFC3339))
|
||||
fmt.Fprintf(&b, "%-20s: %d\n", "Uptime (min)", uptime/60)
|
||||
|
||||
fmt.Fprintf(&b, "%-20s: %d\n", "Total queries", c.v["total"])
|
||||
if uptime != 0 {
|
||||
fmt.Fprintf(&b, "%-20s: %d\n", "Throughput (QpM)", c.v["total"]*60/int(uptime))
|
||||
}
|
||||
|
||||
fmt.Fprintf(&b, "%-20s: %d\n", "Cache L1 queries", c.v["cache1"])
|
||||
if c.v["total"] != 0 {
|
||||
fmt.Fprintf(&b, "%-20s: %d\n", "Cache L1 queries (%)", (100*c.v["cache1"])/c.v["total"])
|
||||
}
|
||||
|
||||
fmt.Fprintf(&b, "%-20s: %d\n", "Upstream queries", c.v["total"]-c.v["cache1"])
|
||||
fmt.Fprintf(&b, "%-20s: %d\n", "Queries with format", c.v["format"])
|
||||
fmt.Fprintf(&b, "%-20s: %d\n", "Queries with format=j1", c.v["format=j1"])
|
||||
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
func (c *Stats) Response(*http.Request) *ResponseWithHeader {
|
||||
return &ResponseWithHeader{
|
||||
Body: c.Show(),
|
||||
StatusCode: 200,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue