aboutsummaryrefslogtreecommitdiff
path: root/expvarhandler/expvar.go
blob: b750261f960a1ae6576ca9fda53783629ea6ca86 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// Package expvarhandler provides fasthttp-compatible request handler
// serving expvars.
package expvarhandler

import (
	"expvar"
	"fmt"
	"regexp"

	"github.com/valyala/fasthttp"
)

var (
	expvarHandlerCalls = expvar.NewInt("expvarHandlerCalls")
	expvarRegexpErrors = expvar.NewInt("expvarRegexpErrors")

	defaultRE = regexp.MustCompile(".")
)

// ExpvarHandler dumps json representation of expvars to http response.
//
// Expvars may be filtered by regexp provided via 'r' query argument.
//
// See https://pkg.go.dev/expvar for details.
func ExpvarHandler(ctx *fasthttp.RequestCtx) {
	expvarHandlerCalls.Add(1)

	ctx.Response.Reset()

	r, err := getExpvarRegexp(ctx)
	if err != nil {
		expvarRegexpErrors.Add(1)
		fmt.Fprintf(ctx, "Error when obtaining expvar regexp: %v", err)
		ctx.SetStatusCode(fasthttp.StatusBadRequest)
		return
	}

	fmt.Fprintf(ctx, "{\n")
	first := true
	expvar.Do(func(kv expvar.KeyValue) {
		if r.MatchString(kv.Key) {
			if !first {
				fmt.Fprintf(ctx, ",\n")
			}
			first = false
			fmt.Fprintf(ctx, "\t%q: %s", kv.Key, kv.Value)
		}
	})
	fmt.Fprintf(ctx, "\n}\n")

	ctx.SetContentType("application/json; charset=utf-8")
}

func getExpvarRegexp(ctx *fasthttp.RequestCtx) (*regexp.Regexp, error) {
	r := string(ctx.QueryArgs().Peek("r"))
	if r == "" {
		return defaultRE, nil
	}
	rr, err := regexp.Compile(r)
	if err != nil {
		return nil, fmt.Errorf("cannot parse r=%q: %w", r, err)
	}
	return rr, nil
}