Merge branch 'master' of github.com:sixt/gomodproxy
This commit is contained in:
commit
2ace4f1c80
@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
@ -13,13 +14,56 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/sixt/gomodproxy/pkg/api"
|
||||
|
||||
_ "expvar"
|
||||
"expvar"
|
||||
_ "net/http/pprof"
|
||||
)
|
||||
|
||||
func prometheusExpose(w io.Writer, name string, v interface{}) {
|
||||
// replace all invalid symbols with underscores
|
||||
name = strings.Map(func(r rune) rune {
|
||||
r = unicode.ToLower(r)
|
||||
if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') {
|
||||
return r
|
||||
}
|
||||
return '_'
|
||||
}, name)
|
||||
// expvar does not have concepts of counters and gauges,
|
||||
// so we tell one from another based on the name suffix.
|
||||
counter := strings.HasSuffix(name, "_total")
|
||||
if f, ok := v.(float64); ok {
|
||||
if counter {
|
||||
fmt.Fprintf(w, "# TYPE %s counter\n", name)
|
||||
} else {
|
||||
fmt.Fprintf(w, "# TYPE %s gauge\n", name)
|
||||
}
|
||||
fmt.Fprintf(w, "%s %f\n", name, f)
|
||||
} else if m, ok := v.(map[string]interface{}); ok {
|
||||
for k, v := range m {
|
||||
// for composite maps we construct metric names by joining the parent map
|
||||
// name and the key name.
|
||||
s := strings.TrimSuffix(name, "_total") + "_" + k
|
||||
if counter {
|
||||
s = s + "_total"
|
||||
}
|
||||
prometheusExpose(w, s, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func prometheusHandler(w http.ResponseWriter, r *http.Request) {
|
||||
expvar.Do(func(kv expvar.KeyValue) {
|
||||
var v interface{}
|
||||
if err := json.Unmarshal([]byte(kv.Value.String()), &v); err != nil {
|
||||
return
|
||||
}
|
||||
prometheusExpose(w, kv.Key, v)
|
||||
})
|
||||
}
|
||||
|
||||
func prettyLog(v ...interface{}) {
|
||||
s := ""
|
||||
msg := ""
|
||||
@ -56,6 +100,7 @@ func main() {
|
||||
|
||||
addr := flag.String("addr", ":0", "http server address")
|
||||
verbose := flag.Bool("v", false, "verbose logging")
|
||||
prometheus := flag.String("prometheus", "", "prometheus address")
|
||||
debug := flag.Bool("debug", false, "enable debug HTTP API (pprof/expvar)")
|
||||
json := flag.Bool("json", false, "json structured logging")
|
||||
dir := flag.String("dir", filepath.Join(os.Getenv("HOME"), ".gomodproxy/cache"), "modules cache directory")
|
||||
@ -103,6 +148,14 @@ func main() {
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/", api.New(options...))
|
||||
if *prometheus != "" {
|
||||
if *prometheus == *addr {
|
||||
mux.HandleFunc("/metrics", prometheusHandler)
|
||||
} else {
|
||||
srv := &http.Server{Handler: http.HandlerFunc(prometheusHandler), Addr: *prometheus}
|
||||
go srv.ListenAndServe()
|
||||
}
|
||||
}
|
||||
if *debug {
|
||||
mux.Handle("/debug/vars", http.DefaultServeMux)
|
||||
mux.Handle("/debug/pprof/heap", http.DefaultServeMux)
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"expvar"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@ -42,6 +43,14 @@ var (
|
||||
apiZip = regexp.MustCompile(`^/(?P<module>.*)/@v/(?P<version>.*).zip$`)
|
||||
)
|
||||
|
||||
var (
|
||||
cacheHits = expvar.NewMap("cache_hits_total")
|
||||
cacheMisses = expvar.NewMap("cache_misses_total")
|
||||
httpRequests = expvar.NewMap("http_requests_total")
|
||||
httpErrors = expvar.NewMap("http_errors_total")
|
||||
httpRequestDurations = expvar.NewMap("http_request_duration_seconds")
|
||||
)
|
||||
|
||||
// New returns a configured http.Handler which implements GOPROXY API.
|
||||
func New(options ...Option) http.Handler {
|
||||
api := &api{log: func(...interface{}) {}}
|
||||
@ -113,13 +122,14 @@ func (api *api) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() { api.log("api.ServeHTTP", "method", r.Method, "url", r.URL, "time", time.Since(now)) }()
|
||||
|
||||
for _, route := range []struct {
|
||||
id string
|
||||
regexp *regexp.Regexp
|
||||
handler func(w http.ResponseWriter, r *http.Request, module, version string)
|
||||
}{
|
||||
{apiList, api.list},
|
||||
{apiInfo, api.info},
|
||||
{apiMod, api.mod},
|
||||
{apiZip, api.zip},
|
||||
{"list", apiList, api.list},
|
||||
{"info", apiInfo, api.info},
|
||||
{"api", apiMod, api.mod},
|
||||
{"zip", apiZip, api.zip},
|
||||
} {
|
||||
if m := route.regexp.FindStringSubmatch(r.URL.Path); m != nil {
|
||||
module, version := m[1], ""
|
||||
@ -127,11 +137,18 @@ func (api *api) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
version = m[2]
|
||||
}
|
||||
module = decodeBangs(module)
|
||||
httpRequests.Add(route.id, 1)
|
||||
defer func() {
|
||||
v := &expvar.Float{}
|
||||
v.Set(time.Since(now).Seconds())
|
||||
httpRequestDurations.Set(route.id, v)
|
||||
}()
|
||||
route.handler(w, r, module, version)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
httpRequests.Add("not_found", 1)
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
|
||||
@ -147,9 +164,11 @@ func (api *api) vcs(ctx context.Context, module string) vcs.VCS {
|
||||
func (api *api) module(ctx context.Context, module string, version vcs.Version) ([]byte, time.Time, error) {
|
||||
for _, store := range api.stores {
|
||||
if snapshot, err := store.Get(ctx, module, version); err == nil {
|
||||
cacheHits.Add(module, 1)
|
||||
return snapshot.Data, snapshot.Timestamp, nil
|
||||
}
|
||||
}
|
||||
cacheMisses.Add(module, 1)
|
||||
|
||||
timestamp, err := api.vcs(ctx, module).Timestamp(ctx, version)
|
||||
if err != nil {
|
||||
@ -187,6 +206,7 @@ func (api *api) list(w http.ResponseWriter, r *http.Request, module, version str
|
||||
list, err := api.vcs(r.Context(), module).List(r.Context())
|
||||
if err != nil {
|
||||
api.log("api.list", "module", module, "error", err)
|
||||
httpErrors.Add(module, 1)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@ -202,6 +222,7 @@ func (api *api) info(w http.ResponseWriter, r *http.Request, module, version str
|
||||
|
||||
if err != nil {
|
||||
api.log("api.info", "module", module, "version", version, "error", err)
|
||||
httpErrors.Add(module, 1)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@ -236,6 +257,7 @@ func (api *api) zip(w http.ResponseWriter, r *http.Request, module, version stri
|
||||
b, _, err := api.module(r.Context(), module, vcs.Version(version))
|
||||
if err != nil {
|
||||
api.log("api.zip", "module", module, "version", version, "error", err)
|
||||
httpErrors.Add(module, 1)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user