├── .gitignore ├── LICENSE ├── doc.go ├── netbug.go └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Edd Robinson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Package netbug provides an http.Handler for executing the various 2 | // profilers and debug tools in the Go standard library. 3 | // 4 | // netbug provides some advantages over the /net/http/pprof and 5 | // /runtime/pprof packages: 6 | // 7 | // 1. You can register the handler under an arbitrary route or with 8 | // some authentication on the handler, making it easier to to keep 9 | // the profilers and debug information away from prying eyes; 10 | // 2. It pulls together all the handlers from /net/http/pprof and 11 | // runtime/pprof into a single index page, for when you can't 12 | // quite remember the URL for the profile you want; and 13 | // 3. You can register the handler onto an `http.ServeMux` that 14 | // isn't `http.DefaultServeMux`. 15 | // 16 | // 17 | // The simplest integration of netbug looks like: 18 | // 19 | // package main 20 | // 21 | // import ( 22 | // "log" 23 | // "net/http" 24 | // 25 | // "github.com/e-dard/netbug" 26 | // ) 27 | // 28 | // func main() { 29 | // r := http.NewServeMux() 30 | // netbug.Register("/myroute/", r) 31 | // 32 | // if err := http.ListenAndServe(":8080", r); err != nil { 33 | // log.Fatal(err) 34 | // } 35 | // } 36 | // 37 | // You can then access the index page via GET /myroute/ 38 | // 39 | // The netbug.RegisterAuthHandler function lets you register the handler on 40 | // your http.ServeMux and add some simple authentication, in the 41 | // form of a URL parameter: 42 | // 43 | // package main 44 | // 45 | // import ( 46 | // "log" 47 | // "net/http" 48 | // 49 | // "github.com/e-dard/netbug" 50 | // ) 51 | // 52 | // func main() { 53 | // r := http.NewServeMux() 54 | // netbug.RegisterAuthHandler("open sesame", "/myroute/", r) 55 | // 56 | // if err := http.ListenAndServe(":8080", r); err != nil { 57 | // log.Fatal(err) 58 | // } 59 | // } 60 | // 61 | // You can then access the index page via: 62 | // 63 | // GET /myroute/?token=open%20sesame 64 | // 65 | // The package also provides access to the handlers directly, for when 66 | // you want to, say, wrap them in your own logic. Just be sure that 67 | // when you use the handlers that netbug provides, you take care to use 68 | // `http.StripPrefix` to strip the route you registered the handler on. 69 | // This is because the handlers' logic expect them to be registered on 70 | // "/". 71 | // 72 | // package main 73 | // 74 | // import ( 75 | // "log" 76 | // "net/http" 77 | // 78 | // "github.com/e-dard/netbug" 79 | // ) 80 | // 81 | // // h is a handler that you wish to wrap around netbug's handler, 82 | // // allowing you to add your own logic (other types of 83 | // // authentication for example). 84 | // func h(h http.Handler) http.Handler { 85 | // mh := func(w http.ResponseWriter, r *http.Request) { 86 | // // Some logic here. 87 | // h.ServeHTTP(w, r) 88 | // } 89 | // return http.HandlerFunc(mh) 90 | // } 91 | // 92 | // func main() { 93 | // r := http.NewServeMux() 94 | // 95 | // // netbug's handler assumes it's registered on "/", so you need 96 | // // to strip any prefix you actually want to register it on, if 97 | // // you're not using the netbug.RegisterX functions. 98 | // nbH := http.StripPrefix("/myroute/", netbug.Handler()) 99 | // 100 | // // Wrap the netbug handler in your own, and register the result. 101 | // r.Handle("/myroute/", h(nbH)) 102 | // 103 | // if err := http.ListenAndServe(":8080", r); err != nil { 104 | // log.Fatal(err) 105 | // } 106 | // } 107 | // 108 | // As you would expect, netbug works the same way with the go profiler 109 | // tool as /net/http/pprof does. To run a 30 second CPU profile on your 110 | // service for example: 111 | // 112 | // $ go tool pprof https://example.com/myroute/profile 113 | // 114 | package netbug 115 | -------------------------------------------------------------------------------- /netbug.go: -------------------------------------------------------------------------------- 1 | package netbug 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | nhpprof "net/http/pprof" 8 | "net/url" 9 | "runtime/pprof" 10 | "strings" 11 | "text/template" 12 | ) 13 | 14 | func handler(token string) http.Handler { 15 | info := struct { 16 | Profiles []*pprof.Profile 17 | Token string 18 | }{ 19 | Profiles: pprof.Profiles(), 20 | Token: url.QueryEscape(token), 21 | } 22 | 23 | h := func(w http.ResponseWriter, r *http.Request) { 24 | name := strings.TrimPrefix(r.URL.Path, "/") 25 | switch name { 26 | case "": 27 | // Index page. 28 | if err := indexTmpl.Execute(w, info); err != nil { 29 | log.Println(err) 30 | return 31 | } 32 | case "cmdline": 33 | nhpprof.Cmdline(w, r) 34 | case "profile": 35 | nhpprof.Profile(w, r) 36 | case "trace": 37 | nhpprof.Trace(w, r) 38 | case "symbol": 39 | nhpprof.Symbol(w, r) 40 | default: 41 | // Provides access to all profiles under runtime/pprof 42 | nhpprof.Handler(name).ServeHTTP(w, r) 43 | } 44 | } 45 | return http.HandlerFunc(h) 46 | } 47 | 48 | // Handler returns an http.Handler that provides access to the various 49 | // profiler and debug tools in the /net/http/pprof and /runtime/pprof 50 | // packages. 51 | // 52 | // The returned handler assumed it is registered on "/" so if you wish 53 | // to register on any other route, you should strip the route prefix 54 | // before passing a request on to the handler. 55 | // 56 | // This is best done with: 57 | // 58 | // h := http.StripPrefix("/myroute/", netbug.Handler()) 59 | // 60 | // Unless you need to wrap or chain the handler you probably want to use 61 | // netbug.RegisterHandler. 62 | func Handler() http.Handler { 63 | return handler("") 64 | } 65 | 66 | // RegisterHandler registers the netbug handler on the provided 67 | // http.ServeMux, using the provided prefix to form the route. 68 | // 69 | // The provided prefix needs to have a trailing slash. The full list of 70 | // routes registered for available profiles and debug information can 71 | // be examined by visiting prefix. 72 | // 73 | func RegisterHandler(prefix string, mux *http.ServeMux) { 74 | mux.Handle(prefix, http.StripPrefix(prefix, Handler())) 75 | } 76 | 77 | // AuthHandler returns an http.Handler that provides authenticated 78 | // access to the various profiler and debug tools in the 79 | // /net/http/pprof and /runtime/pprof packages. 80 | // 81 | // The token provided is required as a URL parameter called token for 82 | // all requests. The netbug package takes care of injecting the token 83 | // into links in the index page. 84 | // 85 | // The returned handler assumed it is registered on "/" so if you wish 86 | // to register on any other route, you should strip the route prefix 87 | // before passing a request on to the handler. 88 | // 89 | // This is best done with: 90 | // 91 | // h := http.StripPrefix("/myroute/", netbug.AuthHandler("secret")) 92 | // 93 | // Unless you need to wrap or chain the handler you probably want to use 94 | // netbug.RegisterAuthHandler. 95 | func AuthHandler(token string) http.Handler { 96 | h := handler(token) 97 | ah := func(w http.ResponseWriter, r *http.Request) { 98 | if r.FormValue("token") == token { 99 | h.ServeHTTP(w, r) 100 | } else { 101 | w.WriteHeader(http.StatusUnauthorized) 102 | fmt.Fprintln(w, "Unauthorized.") 103 | } 104 | } 105 | return http.HandlerFunc(ah) 106 | } 107 | 108 | // RegisterAuthHandler registers a handler requiring authentication on 109 | // the provided http.ServeMux, using the provided prefix to form the 110 | // route. 111 | // 112 | // The provided prefix needs to have a trailing slash. The full list of 113 | // routes registered can be examined by visiting the root page. 114 | func RegisterAuthHandler(token, prefix string, mux *http.ServeMux) { 115 | mux.Handle(prefix, http.StripPrefix(prefix, AuthHandler(token))) 116 | } 117 | 118 | var indexTmpl = template.Must(template.New("index").Parse(` 119 | 120 | Debug Information 121 | 122 |
123 | 124 | profiles:
125 | 126 | {{range .Profiles}} 127 |
{{.Count}}{{.Name}} 128 | {{end}} 129 |
CPU 130 |
5-second trace 131 |
30-second trace 132 |
133 |
134 | debug information:
135 | 136 |
cmdline 137 |
symbol 138 |
full goroutine stack dump
139 | 140 | 141 | `)) 142 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # netbug 2 | 3 | [![GoDoc](https://godoc.org/github.com/e-dard/netbug?status.svg)](https://godoc.org/github.com/e-dard/netbug) 4 | 5 | Package `netbug` provides access to an `http.Handler` that accesses the profiling and debug tools available in the `/net/http/pprof` and `/runtime/pprof` packages. 6 | 7 | The advantages of using `netbug` over the existing `/net/http/pprof` handlers are: 8 | 9 | 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, rather than `/debug/pprof`; 10 | 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; 11 | 3. You can register the handlers onto `http.ServeMux`'s that aren't `http.DefaultServeMux`; 12 | 4. It provides an optional handler that requires a token URL parameter. This is useful if you want that little bit of extra security (use this over HTTPS connections only). 13 | 14 | **Note**: 15 | It still imports `/net/http/pprof`, which means the `/debug/pprof` routes in that package *still* get registered on `http.DefaultServeMux`. 16 | If you're using this package to avoid those routes being registered, you should use it with your *own* `http.ServeMux`. 17 | 18 | `netbug` 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. 19 | 20 | ## How do I use it? 21 | In the simplest case give `netbug` the `http.ServeMux` you want to register the handlers on, as well as where you want to register the handler and you're away. 22 | 23 | ```go 24 | package main 25 | 26 | import ( 27 | "log" 28 | "net/http" 29 | 30 | "github.com/e-dard/netbug" 31 | ) 32 | 33 | func main() { 34 | r := http.NewServeMux() 35 | netbug.RegisterHandler("/myroute/", r) 36 | if err := http.ListenAndServe(":8080", r); err != nil { 37 | log.Fatal(err) 38 | } 39 | } 40 | ``` 41 | 42 | Visiting [http://localhost:8080/myroute/](http://localhost:8080/myroute/) will then return: 43 | 44 | ![](https://photos-3.dropbox.com/t/2/AABdAn1yRTBvqXeDJygtCRsMu1HMqTohoIJdWAQ7vH_j_g/12/5033766/png/32x32/1/1446145200/0/2/Screen%20Shot%202015-10-29%20at%2017.01.45.png/CKaeswIgASACIAMgBSAHKAEoAigH/vaSYDZEeuTA-8biklDyYORywwvL9SbVYH41Jff_CuBk?size_mode=5) 45 | 46 | `netbug` also provides a simple way of adding some authentication: 47 | 48 | ```go 49 | package main 50 | 51 | import ( 52 | "log" 53 | "net/http" 54 | 55 | "github.com/e-dard/netbug" 56 | ) 57 | 58 | func main() { 59 | r := http.NewServeMux() 60 | netbug.RegisterAuthHandler("password", "/myroute/", r) 61 | if err := http.ListenAndServe(":8080", r); err != nil { 62 | log.Fatal(err) 63 | } 64 | } 65 | ``` 66 | 67 | And visit [http://localhost:8080/myroute/?token=password](http://localhost:8080/myroute/?token=password). 68 | 69 | **Obviously** this form of authentication is pointless if you're not accessing the routes over an HTTPS connection. 70 | If you want to use a different form of authentication, e.g., HTTP Basic Authentication, then you can use the handler returned by `netbug.Handler()`, and wrap it with handlers provided by packages like [github.com/abbot/go-http-auth](https://github.com/abbot/go-http-auth/). 71 | 72 | ### What can you do with it? 73 | 74 | It just wraps the behaviour of the [/net/http/pprof](http://golang.org/pkg/net/http/pprof/) and [/runtime/pprof](http://golang.org/pkg/runtime/pprof/) packages. 75 | Check out their documentation to see what's available. 76 | As an example though, if you want to run a 30-second CPU profile on your running service it's really simple: 77 | 78 | ``` 79 | $ go tool pprof https://example.com/myroute/profile 80 | ``` 81 | 82 | ##### New in Go 1.5 83 | You can now produce [execution traces](https://golang.org/pkg/runtime/trace/) of your remotely running program using netbug. 84 | 85 | To do this run one of the trace profiles, which will result in a file being downloaded. Then use the Go `trace` tool to generate a trace, which will open up in your browser. 86 | 87 | ``` 88 | $ go tool trace binary-being-profiled /path/to/downloaded/trace 89 | ``` 90 | 91 | When compiling `binary-being-profiled`, you will need to have targeted the same architecture as the binary that generated the profile. 92 | 93 | ## Background 94 | The [net/http/pprof](http://golang.org/pkg/net/http/pprof/) package is great. 95 | It let's you access profiling and debug information about your running services, via `HTTP`, and even plugs straight into `go tool pprof`. 96 | 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). 97 | 98 | However, there are a couple of problems with the `net/http/pprof` package. 99 | 100 | 1. It assumes you're cool about the relevant handlers being registered under the `/debug/pprof` route. 101 | 2. It assumes you're cool about handlers being registered on `http.DefaultServeMux`. 102 | 3. You can't wrap the handlers in any way, say to add authentication or other logic. 103 | 104 | 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. 105 | It doesn't really work well. 106 | Also, the index page doesn't provide you with easy links to the debug information that the `net/http/pprof` package has handlers for. 107 | 108 | `netbug` is just a small package to fix these issues. 109 | 110 | --------------------------------------------------------------------------------