diff --git a/callers.go b/callers.go index 5eeeeff..e9cec27 100644 --- a/callers.go +++ b/callers.go @@ -52,13 +52,16 @@ func callerFunc(frames int) string { // extractFuncName splits fully qualified function name: // // Input: -// "github.com/spacemonkeygo/monkit/v3.BenchmarkTask.func1" -// "main.DoThings.func1" -// "main.DoThings" +// +// "github.com/spacemonkeygo/monkit/v3.BenchmarkTask.func1" +// "main.DoThings.func1" +// "main.DoThings" +// // Output: -// funcname: "BenchmarkTask.func1" -// funcname: "DoThings.func1" -// funcname: "DoThings" +// +// funcname: "BenchmarkTask.func1" +// funcname: "DoThings.func1" +// funcname: "DoThings" func extractFuncName(fullyQualifiedName string) (funcname string, ok bool) { lastSlashPos := strings.LastIndexByte(fullyQualifiedName, '/') if lastSlashPos+1 >= len(fullyQualifiedName) { diff --git a/counter.go b/counter.go index 4f6f2be..6587aa2 100644 --- a/counter.go +++ b/counter.go @@ -25,12 +25,11 @@ import ( // may be more convenient to use the Counter accessor on a given Scope. // Expected creation is like: // -// var mon = monkit.Package() -// -// func MyFunc() { -// mon.Counter("beans").Inc(1) -// } +// var mon = monkit.Package() // +// func MyFunc() { +// mon.Counter("beans").Inc(1) +// } type Counter struct { mtx sync.Mutex val, low, high int64 diff --git a/ctx.go b/ctx.go index 33a8534..d0fad0b 100644 --- a/ctx.go +++ b/ctx.go @@ -168,34 +168,34 @@ type Task func(ctx *context.Context, args ...interface{}) func(*error) // It also adds a new Span to the given ctx during execution. Expected usage // like: // -// var mon = monkit.Package() +// var mon = monkit.Package() // -// func MyFunc(ctx context.Context, arg1, arg2 string) (err error) { -// defer mon.Task()(&ctx, arg1, arg2)(&err) -// ... -// } +// func MyFunc(ctx context.Context, arg1, arg2 string) (err error) { +// defer mon.Task()(&ctx, arg1, arg2)(&err) +// ... +// } // // or // -// var ( -// mon = monkit.Package() -// funcTask = mon.Task() -// ) +// var ( +// mon = monkit.Package() +// funcTask = mon.Task() +// ) // -// func MyFunc(ctx context.Context, arg1, arg2 string) (err error) { -// defer funcTask(&ctx, arg1, arg2)(&err) -// ... -// } +// func MyFunc(ctx context.Context, arg1, arg2 string) (err error) { +// defer funcTask(&ctx, arg1, arg2)(&err) +// ... +// } // // Task allows you to include SeriesTags. WARNING: Each unique tag key/value // combination creates a unique Func and a unique series. SeriesTags should // only be used for low-cardinality values that you intentionally wish to // result in a unique series. Example: // -// func MyFunc(ctx context.Context, arg1, arg2 string) (err error) { -// defer mon.Task(monkit.NewSeriesTag("key1", "val1"))(&ctx)(&err) -// ... -// } +// func MyFunc(ctx context.Context, arg1, arg2 string) (err error) { +// defer mon.Task(monkit.NewSeriesTag("key1", "val1"))(&ctx)(&err) +// ... +// } // // Task uses runtime.Caller to determine the associated Func name. See // TaskNamed if you want to supply your own name. See Func.Task if you already @@ -226,13 +226,13 @@ func (s *Scope) Task(tags ...SeriesTag) Task { // Task returns a new Task for use on this Func. It also adds a new Span to // the given ctx during execution. // -// var mon = monkit.Package() +// var mon = monkit.Package() // -// func MyFunc(ctx context.Context, arg1, arg2 string) (err error) { -// f := mon.Func() -// defer f.Task(&ctx, arg1, arg2)(&err) -// ... -// } +// func MyFunc(ctx context.Context, arg1, arg2 string) (err error) { +// f := mon.Func() +// defer f.Task(&ctx, arg1, arg2)(&err) +// ... +// } // // It's more expected for you to use mon.Task directly. See RemoteTrace or // ResetTrace if you want greater control over creating new traces. diff --git a/doc.go b/doc.go index d698f84..f0670bc 100644 --- a/doc.go +++ b/doc.go @@ -75,7 +75,7 @@ Example usage log.Println(DoStuff(context.Background())) } -Metrics +# Metrics We've got tools that capture distribution information (including quantiles) about int64, float64, and bool types. We have tools that capture data about @@ -92,45 +92,45 @@ or any other time-series database. Here's a selection of live stats from one of our storage nodes: - env.os.fds 120.000000 - env.os.proc.stat.Minflt 81155.000000 - env.os.proc.stat.Cminflt 11789.000000 - env.os.proc.stat.Majflt 10.000000 - env.os.proc.stat.Cmajflt 6.000000 - ... - - env.process.control 1.000000 - env.process.crc 3819014369.000000 - env.process.uptime 163225.292925 - env.runtime.goroutines 52.000000 - env.runtime.memory.Alloc 2414080.000000 - ... - - env.rusage.Maxrss 26372.000000 - ... - - sm/flud/csl/client.(*CSLClient).Verify.current 0.000000 - sm/flud/csl/client.(*CSLClient).Verify.success 788.000000 - sm/flud/csl/client.(*CSLClient).Verify.error volume missing 91.000000 - sm/flud/csl/client.(*CSLClient).Verify.error dial error 1.000000 - sm/flud/csl/client.(*CSLClient).Verify.panics 0.000000 - sm/flud/csl/client.(*CSLClient).Verify.success times min 0.102214 - sm/flud/csl/client.(*CSLClient).Verify.success times avg 1.899133 - sm/flud/csl/client.(*CSLClient).Verify.success times max 8.601230 - sm/flud/csl/client.(*CSLClient).Verify.success times recent 2.673128 - sm/flud/csl/client.(*CSLClient).Verify.failure times min 0.682881 - sm/flud/csl/client.(*CSLClient).Verify.failure times avg 3.936571 - sm/flud/csl/client.(*CSLClient).Verify.failure times max 6.102318 - sm/flud/csl/client.(*CSLClient).Verify.failure times recent 2.208020 - sm/flud/csl/server.store.avg 710800.000000 - sm/flud/csl/server.store.count 271.000000 - sm/flud/csl/server.store.max 3354194.000000 - sm/flud/csl/server.store.min 467.000000 - sm/flud/csl/server.store.recent 1661376.000000 - sm/flud/csl/server.store.sum 192626890.000000 - ... - -Call graphs + env.os.fds 120.000000 + env.os.proc.stat.Minflt 81155.000000 + env.os.proc.stat.Cminflt 11789.000000 + env.os.proc.stat.Majflt 10.000000 + env.os.proc.stat.Cmajflt 6.000000 + ... + + env.process.control 1.000000 + env.process.crc 3819014369.000000 + env.process.uptime 163225.292925 + env.runtime.goroutines 52.000000 + env.runtime.memory.Alloc 2414080.000000 + ... + + env.rusage.Maxrss 26372.000000 + ... + + sm/flud/csl/client.(*CSLClient).Verify.current 0.000000 + sm/flud/csl/client.(*CSLClient).Verify.success 788.000000 + sm/flud/csl/client.(*CSLClient).Verify.error volume missing 91.000000 + sm/flud/csl/client.(*CSLClient).Verify.error dial error 1.000000 + sm/flud/csl/client.(*CSLClient).Verify.panics 0.000000 + sm/flud/csl/client.(*CSLClient).Verify.success times min 0.102214 + sm/flud/csl/client.(*CSLClient).Verify.success times avg 1.899133 + sm/flud/csl/client.(*CSLClient).Verify.success times max 8.601230 + sm/flud/csl/client.(*CSLClient).Verify.success times recent 2.673128 + sm/flud/csl/client.(*CSLClient).Verify.failure times min 0.682881 + sm/flud/csl/client.(*CSLClient).Verify.failure times avg 3.936571 + sm/flud/csl/client.(*CSLClient).Verify.failure times max 6.102318 + sm/flud/csl/client.(*CSLClient).Verify.failure times recent 2.208020 + sm/flud/csl/server.store.avg 710800.000000 + sm/flud/csl/server.store.count 271.000000 + sm/flud/csl/server.store.max 3354194.000000 + sm/flud/csl/server.store.min 467.000000 + sm/flud/csl/server.store.recent 1661376.000000 + sm/flud/csl/server.store.sum 192626890.000000 + ... + +# Call graphs This library generates call graphs of your live process for you. @@ -147,7 +147,7 @@ Here's another example of one of our production nodes: https://raw.githubusercontent.com/spacemonkeygo/monkit/master/images/callgraph2.png -Trace graphs +# Trace graphs This library generates trace graphs of your live process for you directly, without requiring standing up some tracing system such as Zipkin (though you @@ -166,7 +166,7 @@ a plugin that sends this data to Zipkin (http://github.com/spacemonkeygo/monkit- https://raw.githubusercontent.com/spacemonkeygo/monkit/master/images/trace.png -History +# History Before our crazy Go rewrite of everything (https://www.spacemonkey.com/blog/posts/go-space-monkey) (and before we had even seen Google's Dapper paper), we were a Python shop, and @@ -182,7 +182,7 @@ contexts, and then realized we could get call graph information. We decided a refactor and then an all-out rethinking of our monitoring package was best, and so now we have this library. -Aside about contexts +# Aside about contexts Sometimes you really want callstack contextual information without having to pass arguments through everything on the call stack. In other languages, many @@ -208,7 +208,7 @@ Painfully so. I hope to write more about it in the future, but Google also wrote up their thoughts about it (https://blog.golang.org/context), which you can go read. For now, just swallow your disgust and let's keep moving. -Motivating program +# Motivating program Let's make a super simple Varnish (https://www.varnish-cache.org/) clone. Open up gedit! (Okay just kidding, open whatever text editor you want.) @@ -218,55 +218,55 @@ comments for where to add it if you'd like. For now, let's just make a barebones system that will proxy HTTP requests. We'll call it VLite, but maybe we should call it VReallyLite. - package main + package main - import ( - "flag" - "net/http" - "net/http/httputil" - "net/url" - ) + import ( + "flag" + "net/http" + "net/http/httputil" + "net/url" + ) - type VLite struct { - target *url.URL - proxy *httputil.ReverseProxy - } + type VLite struct { + target *url.URL + proxy *httputil.ReverseProxy + } + + func NewVLite(target *url.URL) *VLite { + return &VLite{ + target: target, + proxy: httputil.NewSingleHostReverseProxy(target), + } + } - func NewVLite(target *url.URL) *VLite { - return &VLite{ - target: target, - proxy: httputil.NewSingleHostReverseProxy(target), + func (v *VLite) Proxy(w http.ResponseWriter, r *http.Request) { + r.Host = v.target.Host // let the proxied server get the right vhost + v.proxy.ServeHTTP(w, r) } - } - - func (v *VLite) Proxy(w http.ResponseWriter, r *http.Request) { - r.Host = v.target.Host // let the proxied server get the right vhost - v.proxy.ServeHTTP(w, r) - } - - func (v *VLite) ServeHTTP(w http.ResponseWriter, r *http.Request) { - // here's where you'd put caching logic - v.Proxy(w, r) - } - - func main() { - target := flag.String( - "proxy", - "http://hasthelargehadroncolliderdestroyedtheworldyet.com/", - "server to cache") - flag.Parse() - targetURL, err := url.Parse(*target) - if err != nil { - panic(err) + + func (v *VLite) ServeHTTP(w http.ResponseWriter, r *http.Request) { + // here's where you'd put caching logic + v.Proxy(w, r) + } + + func main() { + target := flag.String( + "proxy", + "http://hasthelargehadroncolliderdestroyedtheworldyet.com/", + "server to cache") + flag.Parse() + targetURL, err := url.Parse(*target) + if err != nil { + panic(err) + } + panic(http.ListenAndServe(":8080", NewVLite(targetURL))) } - panic(http.ListenAndServe(":8080", NewVLite(targetURL))) - } Run and build this and open localhost:8080 in your browser. If you use the default proxy target, it should inform you that the world hasn't been destroyed yet. -Adding basic instrumentation +# Adding basic instrumentation The first thing you'll want to do is add the small amount of boilerplate to make the instrumentation we're going to add to your process observable later. @@ -286,7 +286,7 @@ main method to serve debug requests: Rebuild, and then check out localhost:9000/stats (or localhost:9000/stats/json, if you prefer) in your browser! -Request contexts +# Request contexts Remember what I said about Google's contexts (https://blog.golang.org/context)? It might seem a bit overkill for such a small project, but it's time to add @@ -298,149 +298,149 @@ webhelp library (https://godoc.org/github.com/jtolds/webhelp), but here is the code now refactored to receive and pass contexts through our two per-request calls. - package main - - import ( - "context" - "flag" - "net/http" - "net/http/httputil" - "net/url" - - "github.com/jtolds/webhelp" - "github.com/spacemonkeygo/monkit/v3" - "github.com/spacemonkeygo/monkit/v3/environment" - "github.com/spacemonkeygo/monkit/v3/present" - ) - - type VLite struct { - target *url.URL - proxy *httputil.ReverseProxy - } - - func NewVLite(target *url.URL) *VLite { - return &VLite{ - target: target, - proxy: httputil.NewSingleHostReverseProxy(target), + package main + + import ( + "context" + "flag" + "net/http" + "net/http/httputil" + "net/url" + + "github.com/jtolds/webhelp" + "github.com/spacemonkeygo/monkit/v3" + "github.com/spacemonkeygo/monkit/v3/environment" + "github.com/spacemonkeygo/monkit/v3/present" + ) + + type VLite struct { + target *url.URL + proxy *httputil.ReverseProxy + } + + func NewVLite(target *url.URL) *VLite { + return &VLite{ + target: target, + proxy: httputil.NewSingleHostReverseProxy(target), + } } - } - - func (v *VLite) Proxy(ctx context.Context, w http.ResponseWriter, r *http.Request) { - r.Host = v.target.Host // let the proxied server get the right vhost - v.proxy.ServeHTTP(w, r) - } - - func (v *VLite) HandleHTTP(ctx context.Context, w webhelp.ResponseWriter, r *http.Request) error { - // here's where you'd put caching logic - v.Proxy(ctx, w, r) - return nil - } - - func main() { - target := flag.String( - "proxy", - "http://hasthelargehadroncolliderdestroyedtheworldyet.com/", - "server to cache") - flag.Parse() - targetURL, err := url.Parse(*target) - if err != nil { - panic(err) + + func (v *VLite) Proxy(ctx context.Context, w http.ResponseWriter, r *http.Request) { + r.Host = v.target.Host // let the proxied server get the right vhost + v.proxy.ServeHTTP(w, r) + } + + func (v *VLite) HandleHTTP(ctx context.Context, w webhelp.ResponseWriter, r *http.Request) error { + // here's where you'd put caching logic + v.Proxy(ctx, w, r) + return nil + } + + func main() { + target := flag.String( + "proxy", + "http://hasthelargehadroncolliderdestroyedtheworldyet.com/", + "server to cache") + flag.Parse() + targetURL, err := url.Parse(*target) + if err != nil { + panic(err) + } + environment.Register(monkit.Default) + go http.ListenAndServe("localhost:9000", present.HTTP(monkit.Default)) + panic(webhelp.ListenAndServe(":8080", NewVLite(targetURL))) } - environment.Register(monkit.Default) - go http.ListenAndServe("localhost:9000", present.HTTP(monkit.Default)) - panic(webhelp.ListenAndServe(":8080", NewVLite(targetURL))) - } You can create a new context for a request however you want. One reason to use something like webhelp is that the cancelation feature of Contexts is hooked up to the HTTP request getting canceled. -Monitor some requests +# Monitor some requests Let's start to get statistics about how many requests we receive! First, this package (main) will need to get a monitoring Scope. Add this global definition right after all your imports, much like you'd create a logger with many logging libraries: - var mon = monkit.Package() + var mon = monkit.Package() Now, make the error return value of HandleHTTP named (so, (err error)), and add this defer line as the very first instruction of HandleHTTP: - func (v *VLite) HandleHTTP(ctx context.Context, w webhelp.ResponseWriter, r *http.Request) (err error) { - defer mon.Task()(&ctx)(&err) + func (v *VLite) HandleHTTP(ctx context.Context, w webhelp.ResponseWriter, r *http.Request) (err error) { + defer mon.Task()(&ctx)(&err) Let's also add the same line (albeit modified for the lack of error) to Proxy, replacing &err with nil: - func (v *VLite) Proxy(ctx context.Context, w http.ResponseWriter, r *http.Request) { - defer mon.Task()(&ctx)(nil) + func (v *VLite) Proxy(ctx context.Context, w http.ResponseWriter, r *http.Request) { + defer mon.Task()(&ctx)(nil) You should now have something like: - package main + package main - import ( - "context" - "flag" - "net/http" - "net/http/httputil" - "net/url" + import ( + "context" + "flag" + "net/http" + "net/http/httputil" + "net/url" - "github.com/jtolds/webhelp" - "github.com/spacemonkeygo/monkit/v3" - "github.com/spacemonkeygo/monkit/v3/environment" - "github.com/spacemonkeygo/monkit/v3/present" - ) + "github.com/jtolds/webhelp" + "github.com/spacemonkeygo/monkit/v3" + "github.com/spacemonkeygo/monkit/v3/environment" + "github.com/spacemonkeygo/monkit/v3/present" + ) + + var mon = monkit.Package() + + type VLite struct { + target *url.URL + proxy *httputil.ReverseProxy + } - var mon = monkit.Package() + func NewVLite(target *url.URL) *VLite { + return &VLite{ + target: target, + proxy: httputil.NewSingleHostReverseProxy(target), + } + } - type VLite struct { - target *url.URL - proxy *httputil.ReverseProxy - } + func (v *VLite) Proxy(ctx context.Context, w http.ResponseWriter, r *http.Request) { + defer mon.Task()(&ctx)(nil) + r.Host = v.target.Host // let the proxied server get the right vhost + v.proxy.ServeHTTP(w, r) + } - func NewVLite(target *url.URL) *VLite { - return &VLite{ - target: target, - proxy: httputil.NewSingleHostReverseProxy(target), + func (v *VLite) HandleHTTP(ctx context.Context, w webhelp.ResponseWriter, r *http.Request) (err error) { + defer mon.Task()(&ctx)(&err) + // here's where you'd put caching logic + v.Proxy(ctx, w, r) + return nil } - } - - func (v *VLite) Proxy(ctx context.Context, w http.ResponseWriter, r *http.Request) { - defer mon.Task()(&ctx)(nil) - r.Host = v.target.Host // let the proxied server get the right vhost - v.proxy.ServeHTTP(w, r) - } - - func (v *VLite) HandleHTTP(ctx context.Context, w webhelp.ResponseWriter, r *http.Request) (err error) { - defer mon.Task()(&ctx)(&err) - // here's where you'd put caching logic - v.Proxy(ctx, w, r) - return nil - } - - func main() { - target := flag.String( - "proxy", - "http://hasthelargehadroncolliderdestroyedtheworldyet.com/", - "server to cache") - flag.Parse() - targetURL, err := url.Parse(*target) - if err != nil { - panic(err) + + func main() { + target := flag.String( + "proxy", + "http://hasthelargehadroncolliderdestroyedtheworldyet.com/", + "server to cache") + flag.Parse() + targetURL, err := url.Parse(*target) + if err != nil { + panic(err) + } + environment.Register(monkit.Default) + go http.ListenAndServe("localhost:9000", present.HTTP(monkit.Default)) + panic(webhelp.ListenAndServe(":8080", NewVLite(targetURL))) } - environment.Register(monkit.Default) - go http.ListenAndServe("localhost:9000", present.HTTP(monkit.Default)) - panic(webhelp.ListenAndServe(":8080", NewVLite(targetURL))) - } We'll unpack what's going on here, but for now: - * Rebuild and restart! - * Trigger a full refresh at localhost:8080 to make sure your new HTTP - handler runs - * Visit localhost:9000/stats and then localhost:9000/funcs + - Rebuild and restart! + - Trigger a full refresh at localhost:8080 to make sure your new HTTP + handler runs + - Visit localhost:9000/stats and then localhost:9000/funcs For this new funcs dataset, if you want a graph, you can download a dot graph at localhost:9000/funcs/dot and json information from @@ -448,29 +448,29 @@ localhost:9000/funcs/json. You should see something like: - [3693964236144930897] main.(*VLite).HandleHTTP - parents: entry - current: 0, highwater: 1, success: 2, errors: 0, panics: 0 - success times: - 0.00: 63.930436ms - 0.10: 70.482159ms - 0.25: 80.309745ms - 0.50: 96.689054ms - 0.75: 113.068363ms - 0.90: 122.895948ms - 0.95: 126.17181ms - 1.00: 129.447675ms - avg: 96.689055ms - failure times: - 0.00: 0 - 0.10: 0 - 0.25: 0 - 0.50: 0 - 0.75: 0 - 0.90: 0 - 0.95: 0 - 1.00: 0 - avg: 0 + [3693964236144930897] main.(*VLite).HandleHTTP + parents: entry + current: 0, highwater: 1, success: 2, errors: 0, panics: 0 + success times: + 0.00: 63.930436ms + 0.10: 70.482159ms + 0.25: 80.309745ms + 0.50: 96.689054ms + 0.75: 113.068363ms + 0.90: 122.895948ms + 0.95: 126.17181ms + 1.00: 129.447675ms + avg: 96.689055ms + failure times: + 0.00: 0 + 0.10: 0 + 0.25: 0 + 0.50: 0 + 0.75: 0 + 0.90: 0 + 0.95: 0 + 1.00: 0 + avg: 0 with a similar report for the Proxy method, or a graph like: @@ -491,7 +491,7 @@ Cool, eh? How it works - defer mon.Task()(&ctx)(&err) + defer mon.Task()(&ctx)(&err) is an interesting line of code - there's three function calls. If you look at the Go spec, all of the function calls will run at the time the function starts @@ -504,17 +504,17 @@ are inspecting runtime.Caller to determine the name of the function. Because this is a heavy operation, you can actually store the result of mon.Task() and reuse it somehow else if you prefer, so instead of - func MyFunc(ctx context.Context) (err error) { - defer mon.Task()(&ctx)(&err) - } + func MyFunc(ctx context.Context) (err error) { + defer mon.Task()(&ctx)(&err) + } you could instead use - var myFuncMon = mon.Task() + var myFuncMon = mon.Task() - func MyFunc(ctx context.Context) (err error) { - defer myFuncMon(&ctx)(&err) - } + func MyFunc(ctx context.Context) (err error) { + defer myFuncMon(&ctx)(&err) + } which is more performant every time after the first time. runtime.Caller only gets called once. @@ -531,7 +531,7 @@ don't want a context. You just lose callgraph information. The last function call stops all the stop watches ad makes a note of any observed errors or panics (it repanics after observing them). -Tracing +# Tracing Turns out, we don't even need to change our program anymore to get rich tracing information! @@ -545,7 +545,7 @@ you should see a relatively uninteresting but super promising svg. Let's make the trace more interesting. Add a - time.Sleep(200 * time.Millisecond) + time.Sleep(200 * time.Millisecond) to your HandleHTTP method, rebuild, and restart. Load localhost:8080, then start a new request to your trace URL, then reload localhost:8080 again. Flip @@ -567,7 +567,7 @@ next! Make sure to check out what the addition of the time.Sleep call did to the other reports. -Plugins +# Plugins It's easy to write plugins for monkit! Check out our first one that exports data to Zipkin (http://zipkin.io/)'s Scribe API: @@ -575,6 +575,5 @@ data to Zipkin (http://zipkin.io/)'s Scribe API: https://github.com/spacemonkeygo/monkit-zipkin We plan to have more (for HTrace, OpenTracing, etc, etc), soon! - */ package monkit // import "github.com/spacemonkeygo/monkit/v3" diff --git a/func.go b/func.go index bf791ff..e7fe93e 100644 --- a/func.go +++ b/func.go @@ -23,13 +23,12 @@ import ( // (Func/FuncNamed) on a Scope. If you want to manage installation bookkeeping // yourself, create a FuncStats directly. Expected Func creation like: // -// var mon = monkit.Package() -// -// func MyFunc() { -// f := mon.Func() -// ... -// } +// var mon = monkit.Package() // +// func MyFunc() { +// f := mon.Func() +// ... +// } type Func struct { // sync/atomic things FuncStats diff --git a/funcstats.go b/funcstats.go index d95a3b9..0d24efc 100644 --- a/funcstats.go +++ b/funcstats.go @@ -25,13 +25,12 @@ import ( // Should be created with NewFuncStats, though expected creation is through a // Func object: // -// var mon = monkit.Package() -// -// func MyFunc() { -// f := mon.Func() -// ... -// } +// var mon = monkit.Package() // +// func MyFunc() { +// f := mon.Func() +// ... +// } type FuncStats struct { // sync/atomic things current int64 @@ -195,11 +194,10 @@ func (f *FuncStats) FailureTimes() *DurationDist { // function to be called at the end of the function execution. Expected usage // like: // -// func MyFunc() (err error) { -// defer funcStats.Observe()(&err) -// ... -// } -// +// func MyFunc() (err error) { +// defer funcStats.Observe()(&err) +// ... +// } func (f *FuncStats) Observe() func(errptr *error) { f.start(nil) start := monotime.Now() diff --git a/meter.go b/meter.go index be0eecb..19bbc9b 100644 --- a/meter.go +++ b/meter.go @@ -39,17 +39,16 @@ type meterBucket struct { // Implements the StatSource interface. You should construct using NewMeter, // though expected usage is like: // -// var ( -// mon = monkit.Package() -// meter = mon.Meter("meter") -// ) -// -// func MyFunc() { -// ... -// meter.Mark(4) // 4 things happened -// ... -// } +// var ( +// mon = monkit.Package() +// meter = mon.Meter("meter") +// ) // +// func MyFunc() { +// ... +// meter.Mark(4) // 4 things happened +// ... +// } type Meter struct { mtx sync.Mutex total int64 @@ -156,13 +155,12 @@ func (e *Meter) Stats(cb func(key SeriesKey, field string, val float64)) { // DiffMeter is a StatSource that shows the difference between // the rates of two meters. Expected usage like: // -// var ( -// mon = monkit.Package() -// herps = mon.Meter("herps") -// derps = mon.Meter("derps") -// herpToDerp = mon.DiffMeter("herp_to_derp", herps, derps) -// ) -// +// var ( +// mon = monkit.Package() +// herps = mon.Meter("herps") +// derps = mon.Meter("derps") +// herpToDerp = mon.DiffMeter("herp_to_derp", herps, derps) +// ) type DiffMeter struct { meter1, meter2 *Meter key SeriesKey diff --git a/present/path.go b/present/path.go index c293039..29f7198 100644 --- a/present/path.go +++ b/present/path.go @@ -51,28 +51,29 @@ func curry(reg *monkit.Registry, // path, and optional query parameters, and returns a Result if possible. // // FromRequest understands the following paths: -// * /ps, /ps/text - returns the result of SpansText -// * /ps/dot - returns the result of SpansDot -// * /ps/json - returns the result of SpansJSON -// * /funcs, /funcs/text - returns the result of FuncsText -// * /funcs/dot - returns the result of FuncsDot -// * /funcs/json - returns the result of FuncsJSON -// * /stats, /stats/text - returns the result of StatsText -// * /stats/json - returns the result of StatsJSON -// * /trace/svg - returns the result of TraceQuerySVG -// * /trace/json - returns the result of TraceQueryJSON -// * /trace/remote - returns trace id or redirect +// - /ps, /ps/text - returns the result of SpansText +// - /ps/dot - returns the result of SpansDot +// - /ps/json - returns the result of SpansJSON +// - /funcs, /funcs/text - returns the result of FuncsText +// - /funcs/dot - returns the result of FuncsDot +// - /funcs/json - returns the result of FuncsJSON +// - /stats, /stats/text - returns the result of StatsText +// - /stats/json - returns the result of StatsJSON +// - /trace/svg - returns the result of TraceQuerySVG +// - /trace/json - returns the result of TraceQueryJSON +// - /trace/remote - returns trace id or redirect // // The last two paths are worth discussing in more detail, as they take // query parameters. All trace endpoints require at least one of the following // two query parameters: -// * regex - If provided, the very next Span that crosses a Func that has -// a name that matches this regex will start a trace until that -// triggering Span ends, provided the trace_id matches. -// * trace_id - If provided, the very next Span on a trace with the given -// trace id will start a trace until the triggering Span ends, -// provided the regex matches. NOTE: the trace_id will be parsed -// in hex. +// - regex - If provided, the very next Span that crosses a Func that has +// a name that matches this regex will start a trace until that +// triggering Span ends, provided the trace_id matches. +// - trace_id - If provided, the very next Span on a trace with the given +// trace id will start a trace until the triggering Span ends, +// provided the regex matches. NOTE: the trace_id will be parsed +// in hex. +// // By default, regular expressions are matched ahead of time against all known // Funcs, but perhaps the Func you want to trace hasn't been observed by the // process yet, in which case the regex will fail to match anything. You can diff --git a/present/utils.go b/present/utils.go index 73b4b8e..a2d3d7d 100644 --- a/present/utils.go +++ b/present/utils.go @@ -27,9 +27,10 @@ const keepAliveInterval = 30 * time.Second // from ctx and a stop method. The derived rctx is canceled if ping returns // a non-nil error. In the background after return, keepAlive will call ping // with ctx every keepAliveInterval until: -// 1) ping returns an error, at which point rctx is canceled. -// 2) stop is called. in this case rctx is left alone. -// 3) ctx is canceled. rctx is also canceled as a consequence. +// 1. ping returns an error, at which point rctx is canceled. +// 2. stop is called. in this case rctx is left alone. +// 3. ctx is canceled. rctx is also canceled as a consequence. +// // stop is a no-op if the keepAlive loop has already been stopped. stop returns // the first error that ping returned, if ping returned an error before stop // was called. diff --git a/registry.go b/registry.go index 9a66e52..01de124 100644 --- a/registry.go +++ b/registry.go @@ -76,7 +76,7 @@ func (r *Registry) WithTransformers(t ...CallbackTransformer) *Registry { // Package creates a new monitoring Scope, named after the top level package. // It's expected that you'll have something like // -// var mon = monkit.Package() +// var mon = monkit.Package() // // at the top of each package. func (r *Registry) Package() *Scope { diff --git a/timer.go b/timer.go index 69c2f42..9ed4144 100644 --- a/timer.go +++ b/timer.go @@ -25,15 +25,15 @@ import ( // construct with NewTimer(), though the expected usage is from a Scope like // so: // -// var mon = monkit.Package() +// var mon = monkit.Package() // -// func MyFunc() { -// ... -// timer := mon.Timer("event") -// // perform event -// timer.Stop() -// ... -// } +// func MyFunc() { +// ... +// timer := mon.Timer("event") +// // perform event +// timer.Stop() +// ... +// } // // Timers implement StatSource. type Timer struct { diff --git a/val.go b/val.go index 53b13ba..2ca255f 100644 --- a/val.go +++ b/val.go @@ -23,14 +23,13 @@ import ( // IntVal is a convenience wrapper around an IntDist. Constructed using // NewIntVal, though its expected usage is like: // -// var mon = monkit.Package() -// -// func MyFunc() { -// ... -// mon.IntVal("size").Observe(val) -// ... -// } +// var mon = monkit.Package() // +// func MyFunc() { +// ... +// mon.IntVal("size").Observe(val) +// ... +// } type IntVal struct { mtx sync.Mutex dist IntDist @@ -71,14 +70,13 @@ func (v *IntVal) Quantile(quantile float64) (rv int64) { // FloatVal is a convenience wrapper around an FloatDist. Constructed using // NewFloatVal, though its expected usage is like: // -// var mon = monkit.Package() -// -// func MyFunc() { -// ... -// mon.FloatVal("size").Observe(val) -// ... -// } +// var mon = monkit.Package() // +// func MyFunc() { +// ... +// mon.FloatVal("size").Observe(val) +// ... +// } type FloatVal struct { mtx sync.Mutex dist FloatDist @@ -120,14 +118,13 @@ func (v *FloatVal) Quantile(quantile float64) (rv float64) { // number of falses, and the disposition (number of trues minus number of // falses). Constructed using NewBoolVal, though its expected usage is like: // -// var mon = monkit.Package() -// -// func MyFunc() { -// ... -// mon.BoolVal("flipped").Observe(bool) -// ... -// } +// var mon = monkit.Package() // +// func MyFunc() { +// ... +// mon.BoolVal("flipped").Observe(bool) +// ... +// } type BoolVal struct { trues int64 falses int64 @@ -165,14 +162,13 @@ func (v *BoolVal) Stats(cb func(key SeriesKey, field string, val float64)) { // StructVal keeps track of a structure of data. Constructed using // NewStructVal, though its expected usage is like: // -// var mon = monkit.Package() -// -// func MyFunc() { -// ... -// mon.StructVal("stats").Observe(stats) -// ... -// } +// var mon = monkit.Package() // +// func MyFunc() { +// ... +// mon.StructVal("stats").Observe(stats) +// ... +// } type StructVal struct { mtx sync.Mutex recent interface{} @@ -207,14 +203,13 @@ func (v *StructVal) Stats(cb func(key SeriesKey, field string, val float64)) { // DurationVal is a convenience wrapper around an DurationVal. Constructed using // NewDurationVal, though its expected usage is like: // -// var mon = monkit.Package() -// -// func MyFunc() { -// ... -// mon.DurationVal("time").Observe(val) -// ... -// } +// var mon = monkit.Package() // +// func MyFunc() { +// ... +// mon.DurationVal("time").Observe(val) +// ... +// } type DurationVal struct { mtx sync.Mutex dist DurationDist