Skip to content

Commit dd7f12f

Browse files
Add http pprof profiling endpoint
Signed-off-by: Tim Vaillancourt <[email protected]>
1 parent 8ba5d64 commit dd7f12f

File tree

5 files changed

+71
-27
lines changed

5 files changed

+71
-27
lines changed

control.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ import (
1818
// core. This means copying IP objects, slices, de-referencing pointers and taking the actual value, etc
1919

2020
type Control struct {
21-
f *Interface
22-
l *logrus.Logger
23-
cancel context.CancelFunc
24-
sshStart func()
25-
statsStart func()
26-
dnsStart func()
21+
f *Interface
22+
l *logrus.Logger
23+
cancel context.CancelFunc
24+
sshStart func()
25+
httpStart func()
26+
dnsStart func()
2727
}
2828

2929
type ControlHostInfo struct {
@@ -48,8 +48,8 @@ func (c *Control) Start() {
4848
if c.sshStart != nil {
4949
go c.sshStart()
5050
}
51-
if c.statsStart != nil {
52-
go c.statsStart()
51+
if c.httpStart != nil {
52+
go c.httpStart()
5353
}
5454
if c.dnsStart != nil {
5555
go c.dnsStart()

examples/config.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,9 @@ logging:
244244
# As an example, to log as RFC3339 with millisecond precision, set to:
245245
#timestamp_format: "2006-01-02T15:04:05.000Z07:00"
246246

247+
#http:
248+
#listen: 127.0.0.1:8080
249+
247250
#stats:
248251
#type: graphite
249252
#prefix: nebula
@@ -252,7 +255,6 @@ logging:
252255
#interval: 10s
253256

254257
#type: prometheus
255-
#listen: 127.0.0.1:8080
256258
#path: /metrics
257259
#namespace: prometheusns
258260
#subsystem: nebula

http.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package nebula
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
_ "net/http/pprof"
7+
8+
"github.com/sirupsen/logrus"
9+
"github.com/slackhq/nebula/config"
10+
)
11+
12+
func startHttp(l *logrus.Logger, c *config.C, statsHandler statsHandlerFunc, listen string) (f func(), err error) {
13+
if listen == "" {
14+
return nil, nil
15+
}
16+
17+
var statsPath string
18+
if statsHandler != nil {
19+
statsPath = c.GetString("stats.path", "")
20+
if statsPath == "" {
21+
return nil, fmt.Errorf("stats.path should not be empty")
22+
}
23+
}
24+
25+
f = func() {
26+
l.Infof("Go pprof handler listening on %s at /debug/pprof", listen)
27+
if statsHandler != nil {
28+
http.Handle(statsPath, statsHandler(listen, statsPath))
29+
}
30+
l.Fatal(http.ListenAndServe(listen, nil))
31+
}
32+
33+
return f, err
34+
}

main.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -324,14 +324,25 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg
324324
go lightHouse.LhUpdateWorker(ctx, ifce)
325325
}
326326

327+
httpListen := c.GetString("http.listen", "")
328+
if httpListen == "" {
329+
if httpListen = c.GetString("stats.listen", ""); httpListen != "" {
330+
l.Warn("http.listen is undef, falling back to stats.listen. stats.listen will be deprecated in a future release.")
331+
}
332+
}
333+
327334
// TODO - stats third-party modules start uncancellable goroutines. Update those libs to accept
328335
// a context so that they can exit when the context is Done.
329-
statsStart, err := startStats(l, c, buildVersion, configTest)
330-
336+
statsHTTPHandler, err := startStats(l, c, httpListen, buildVersion, configTest)
331337
if err != nil {
332338
return nil, util.NewContextualError("Failed to start stats emitter", nil, err)
333339
}
334340

341+
httpStart, err := startHttp(l, c, statsHTTPHandler, httpListen)
342+
if err != nil {
343+
return nil, util.NewContextualError("Failed to start http server", nil, err)
344+
}
345+
335346
if configTest {
336347
return nil, nil
337348
}
@@ -348,5 +359,5 @@ func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logg
348359
dnsStart = dnsMain(l, hostMap, c)
349360
}
350361

351-
return &Control{ifce, l, cancel, sshStart, statsStart, dnsStart}, nil
362+
return &Control{ifce, l, cancel, sshStart, httpStart, dnsStart}, nil
352363
}

stats.go

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package nebula
33
import (
44
"errors"
55
"fmt"
6-
"log"
76
"net"
87
"net/http"
98
"runtime"
@@ -19,10 +18,12 @@ import (
1918
"github.com/slackhq/nebula/config"
2019
)
2120

21+
type statsHandlerFunc func(listen, path string) http.Handler
22+
2223
// startStats initializes stats from config. On success, if any further work
23-
// is needed to serve stats, it returns a func to handle that work. If no
24+
// is needed to serve stats, it returns an http.Handler for that work. If no
2425
// work is needed, it'll return nil. On failure, it returns nil, error.
25-
func startStats(l *logrus.Logger, c *config.C, buildVersion string, configTest bool) (func(), error) {
26+
func startStats(l *logrus.Logger, c *config.C, listen, buildVersion string, configTest bool) (f statsHandlerFunc, err error) {
2627
mType := c.GetString("stats.type", "")
2728
if mType == "" || mType == "none" {
2829
return nil, nil
@@ -33,16 +34,14 @@ func startStats(l *logrus.Logger, c *config.C, buildVersion string, configTest b
3334
return nil, fmt.Errorf("stats.interval was an invalid duration: %s", c.GetString("stats.interval", ""))
3435
}
3536

36-
var startFn func()
3737
switch mType {
3838
case "graphite":
3939
err := startGraphiteStats(l, interval, c, configTest)
4040
if err != nil {
4141
return nil, err
4242
}
4343
case "prometheus":
44-
var err error
45-
startFn, err = startPrometheusStats(l, interval, c, buildVersion, configTest)
44+
f, err = startPrometheusStats(l, interval, c, listen, buildVersion, configTest)
4645
if err != nil {
4746
return nil, err
4847
}
@@ -56,7 +55,7 @@ func startStats(l *logrus.Logger, c *config.C, buildVersion string, configTest b
5655
go metrics.CaptureDebugGCStats(metrics.DefaultRegistry, interval)
5756
go metrics.CaptureRuntimeMemStats(metrics.DefaultRegistry, interval)
5857

59-
return startFn, nil
58+
return f, nil
6059
}
6160

6261
func startGraphiteStats(l *logrus.Logger, i time.Duration, c *config.C, configTest bool) error {
@@ -79,13 +78,12 @@ func startGraphiteStats(l *logrus.Logger, i time.Duration, c *config.C, configTe
7978
return nil
8079
}
8180

82-
func startPrometheusStats(l *logrus.Logger, i time.Duration, c *config.C, buildVersion string, configTest bool) (func(), error) {
81+
func startPrometheusStats(l *logrus.Logger, i time.Duration, c *config.C, listen, buildVersion string, configTest bool) (statsHandlerFunc, error) {
8382
namespace := c.GetString("stats.namespace", "")
8483
subsystem := c.GetString("stats.subsystem", "")
8584

86-
listen := c.GetString("stats.listen", "")
8785
if listen == "" {
88-
return nil, fmt.Errorf("stats.listen should not be empty")
86+
return nil, fmt.Errorf("http.listen or stats.listen must be defined to use promtheus stats")
8987
}
9088

9189
path := c.GetString("stats.path", "")
@@ -114,14 +112,13 @@ func startPrometheusStats(l *logrus.Logger, i time.Duration, c *config.C, buildV
114112
pr.MustRegister(g)
115113
g.Set(1)
116114

117-
var startFn func()
115+
var f statsHandlerFunc
118116
if !configTest {
119-
startFn = func() {
117+
f = func(listen, path string) http.Handler {
120118
l.Infof("Prometheus stats listening on %s at %s", listen, path)
121-
http.Handle(path, promhttp.HandlerFor(pr, promhttp.HandlerOpts{ErrorLog: l}))
122-
log.Fatal(http.ListenAndServe(listen, nil))
119+
return promhttp.HandlerFor(pr, promhttp.HandlerOpts{ErrorLog: l})
123120
}
124121
}
125122

126-
return startFn, nil
123+
return f, nil
127124
}

0 commit comments

Comments
 (0)