Skip to content

Commit c85616a

Browse files
committed
initial commit
1 parent 297eac3 commit c85616a

File tree

2 files changed

+172
-2
lines changed

2 files changed

+172
-2
lines changed

README.md

+66-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,66 @@
1-
# remote-debug
2-
Package rdb allows you to prefix Go's http/net/pprof handlers with your own URL path.
1+
# rdb
2+
3+
Package `rdb` provides an `http.Handler` for accessing the profiling and debug tools available in the `/net/http/pprof` and `/runtime/pprof` packages.
4+
5+
The advantages of using `rdb` over the existing `/net/http/pprof` handlers are:
6+
7+
1. You can register the handler under an arbitrary route-prefix. A use-case might be to have a secret endpoint for keeping this information hidden from prying eyes on production boxes;
8+
2. It pulls together all the handlers from `/net/http/pprof` and `/runtime/pprof` into a single index page, for when you can't quite remember the URL for the profile you want; and
9+
3. You can register the handlers onto `http.ServeMux`'s that aren't `http.DefaultServeMux`.
10+
11+
**Note**:
12+
It still imports `/net/http/pprof`, which means the `/debug/pprof` routes in that package get registered on `http.DefaultServeMux`. If you're using this package you to avoid those routes being registered, you should use it with your own `http.ServeMux`.
13+
14+
`rdb` is trying to cater for the situation where you want all profiling tools available remotely on your running services, but you don't want to expose the `/debug/pprof` routes that `net/http/pprof` forces you to expose.
15+
16+
## How do I use it?
17+
All you have to do is give `rdb` the `http.ServeMux` you want to register the handlers on, and you're away.
18+
19+
```go
20+
package main
21+
22+
import (
23+
"log"
24+
"net/http"
25+
26+
"github.com/e-dard/remote-debug"
27+
)
28+
29+
func main() {
30+
r := http.NewServeMux()
31+
rdb.Register("/some-prefix/", r)
32+
if err := http.ListenAndServe(":8080", r); err != nil {
33+
log.Fatal(err)
34+
}
35+
}
36+
```
37+
38+
Visiting `http://localhost:8080/some-prefix/debug/pprof/` will then return:
39+
40+
![](http://cl.ly/image/3x1U3O1B2L3C/Screen%20Shot%202015-03-07%20at%2019.53.28.png)
41+
42+
### What can you do with it?
43+
44+
It just wraps the behaviour of the `/net/http/pprof` and `/runtime/pprof` packages.
45+
Check out their documentation to see what's available.
46+
As an example though, if you want to run a 30-second CPU profile on your running service it's really simple:
47+
48+
```
49+
$ go tool pprof https://example.com/some-hidden-prefix/debug/pprof/profile
50+
```
51+
52+
## Background
53+
The [net/http/pprof](http://golang.org/pkg/net/http/pprof/) package is great.
54+
It let's you access profiling and debug information about your running services, via `HTTP`, and even plugs straight into `go tool pprof`.
55+
You can find out more about using the `net/http/pprof` package at the bottom of [this blog post](http://blog.golang.org/profiling-go-programs).
56+
57+
However, there are a couple of problems with the `net/http/pprof` package.
58+
59+
1. It assumes you're cool about the relevant handlers being registered under the `/debug/pprof` route.
60+
2. It assumes you're cool about handlers being registered on `http.DefaultServeMux`.
61+
62+
You can sort of fix (1) and (2) by digging around the `net/http/pprof` package and registering all all the exported handlers under different paths on your own `http.ServeMux`, but you still have the problem of the index page—which is useful to visit if you don't profile much—using hard-coded paths. It doesn't quite work well.
63+
Also, the index page doesn't provide you with easy links to the debug information that the `net/http/pprof` has handlers for.
64+
65+
So, `rdb` is just a simple package to fix (1) and (2).
66+

register.go

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Package rdb provide an `http.Handler` that can be registered on an
2+
// `http.ServeMux` of your choice to access remote profiling.
3+
//
4+
// rdb provides some advantages over the `/net/http/pprof` and
5+
// `/runtime/pprof` packages:
6+
// 1. You can register the handler under an arbitrary route-prefix.
7+
// A use-case might be to have a secret endpoint for keeping this
8+
// information hidden from prying eyes on production boxes;
9+
// 2. It pulls together all the handlers from `/net/http/pprof` and
10+
// `runtime/pprof` into a single index page, for when you can't
11+
// quite remember the URL for the profile you want; and
12+
// 3. You can register the handlers onto `http.ServeMux`'s that
13+
// aren't `http.DefaultServeMux`.
14+
//
15+
//
16+
// The simplest integration of `rdb` looks like:
17+
//
18+
// package main
19+
//
20+
// import (
21+
// "log"
22+
// "net/http"
23+
//
24+
// "github.com/e-dard/remote-debug"
25+
// )
26+
//
27+
// func main() {
28+
// r := http.NewServeMux()
29+
// rdb.Register("/some-prefix/", r)
30+
// if err := http.ListenAndServe(":8080", r); err != nil {
31+
// log.Fatal(err)
32+
// }
33+
// }
34+
//
35+
package rdb
36+
37+
import (
38+
"net/http"
39+
nhpprof "net/http/pprof"
40+
"runtime/pprof"
41+
"strings"
42+
"text/template"
43+
)
44+
45+
// Register registers all the available handlers in `/net/http/pprof` on
46+
// the provided `http.ServeMux`, ensuring that the routes are prefixed
47+
// with the provided prefix argument.
48+
//
49+
// The provided prefix needs to have a trailing slash. The full list of
50+
// routes registered can be seen by visiting the index page.
51+
func Register(prefix string, mux *http.ServeMux) {
52+
tmpInfo := struct {
53+
Profiles []*pprof.Profile
54+
Info []string
55+
Prefix string
56+
}{
57+
58+
Profiles: pprof.Profiles(),
59+
Info: []string{"cmdline", "symbol"},
60+
Prefix: prefix,
61+
}
62+
63+
h := func(w http.ResponseWriter, r *http.Request) {
64+
name := strings.TrimPrefix(r.URL.Path, prefix+"debug/pprof/")
65+
switch name {
66+
case "":
67+
// Index page.
68+
indexTmpl.Execute(w, tmpInfo)
69+
case "cmdline":
70+
nhpprof.Cmdline(w, r)
71+
case "profile":
72+
nhpprof.Profile(w, r)
73+
case "symbol":
74+
nhpprof.Symbol(w, r)
75+
default:
76+
// Provides access to all profiles under runtime/pprof
77+
nhpprof.Handler(name).ServeHTTP(w, r)
78+
}
79+
}
80+
mux.HandleFunc(prefix, h)
81+
}
82+
83+
var indexTmpl = template.Must(template.New("index").Parse(`<html>
84+
<head>
85+
<title>{{.Prefix}}debug/pprof/</title>
86+
</head>
87+
{{.Prefix}}debug/pprof/<br>
88+
<br>
89+
<body>
90+
profiles:<br>
91+
<table>
92+
{{range .Profiles}}
93+
<tr><td align=right>{{.Count}}<td><a href="{{$.Prefix}}debug/pprof/{{.Name}}?debug=1">{{.Name}}</a>
94+
{{end}}
95+
<tr><td align=right><td><a href="{{$.Prefix}}debug/pprof/profile">CPU</a>
96+
</table>
97+
<br>
98+
debug information:<br>
99+
<table>
100+
{{range .Info}}
101+
<tr><td align=right><td><a href="{{$.Prefix}}debug/pprof/{{.}}">{{.}}</a>
102+
{{end}}
103+
<tr><td align=right><td><a href="{{.Prefix}}debug/pprof/goroutine?debug=2">full goroutine stack dump</a><br>
104+
<table>
105+
</body>
106+
</html>`))

0 commit comments

Comments
 (0)