├── .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 || {{.Count}} | {{.Name}} 128 | {{end}} 129 | |
| CPU 130 | | |
| 5-second trace 131 | | |
| 30-second trace 132 | |
| cmdline 137 | | |
| symbol 138 | | |
| full goroutine stack dump 139 | |