Reputation: 311
I'm making a web app that serves dynamically generated pdfs. These contain content from the internet, so every time it serves a pdf, it downloads a number of files to a new temporary folder.
I end up with a large number of folders after I load the page once, so it seems that, for some reason, the handler is being called multiple times, which is an issue because I'm downloading multiple times more than I need to of not insubstantial files. I'd like to check at what stage of the process multiple requests are occurring.
Is there a way of working out how many times a function has been called, quite possibly using closures? (I haven't quite got closures into my mental model for programming yet; I don't completely understand them/how they're used). This would preferably be something involving an int in the language rather than printing something at every stage and counting by hand - I'm looking for a more scalable solution than that (for later situations as well as this one).
Thanks!
Upvotes: 3
Views: 2496
Reputation: 4756
Counting Calls
To answer the specific question you asked, here is one quick way to count handler executions:
func countCalls(h http.HandlerFunc) http.HandlerFunc {
var lock sync.Mutex
var count int
return func(w http.ResponseWriter, r *http.Request) {
lock.Lock()
count++
w.Header().Set("X-Call-Count", fmt.Sprintf("%d", count))
lock.Unlock()
h.ServeHTTP(w, r)
}
}
http.Handle("/foobar", countCalls(foobarHandler))
This will add a header that you can inspect with your favorite web developer tools; you could also just log it to standard output or something.
Logging Handlers
To expand upon the answers mentioned above, what you probably want to do to debug this and have in place for future use is to log details of each request.
package main
import (
"flag"
"log"
"net/http"
"os"
"github.com/gorilla/handlers"
)
var (
accessLogFile = flag.String("log", "/var/log/yourapp/access.log", "Access log file")
)
func main() {
accessLog, err := os.OpenFile(*accessLogFile, os.O_CREATE|os.O_WRITE|os.O_APPEND, 0644)
if err != nil {
log.Fatalf("Failed to open access log: %s", err)
}
wrap := func(f http.HandlerFunc) http.Handler {
return handlers.LoggingHandler(accessLog, http.HandlerFunc(foobarHandler))
}
http.Handle("/foobar", wrap(foobarHandler))
...
}
This uses LoggingHandler (or CombinedLoggingHandler) to write a standard Apache format log message that you can either inspect yourself or analyze with various tools.
An example of a log line would be
127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326
which tells you who made the request, when, what the method and URL was, how your server responded, and how long the response was. From this log, you should be able to see exactly what requests are being made, to determine not only how many times your handlers are being called, but exactly what is generating the requests and whether they're to another endpoint (like /favicon.ico
).
Upvotes: 1
Reputation: 77955
Here are two ways you can count function calls, and one for method calls. There are plenty of other ways too, but just to get you started:
Using closure: (not what I would recommended)
package main
import(
"fmt"
"sync/atomic"
)
var Foo = func() (func() uint64) {
var called uint64
return func() uint64 {
atomic.AddUint64(&called, 1)
fmt.Println("Foo!")
return called
}
}()
func main() {
Foo()
c := Foo()
fmt.Printf("Foo() is called %d times\n", c)
}
Playground: http://play.golang.org/p/euKbamdI7h
Using global counter:
package main
import (
"fmt"
"sync/atomic"
)
var called uint64
func Foo() {
atomic.AddUint64(&called, 1)
fmt.Println("Foo!");
}
func main() {
Foo()
Foo()
fmt.Printf("Foo() is called %d times\n", called)
}
Playground: http://play.golang.org/p/3Ib29VCnoF
Counting method calls:
package main
import (
"fmt"
"sync/atomic"
)
type T struct {
Called uint64
}
func (t *T) Foo() {
atomic.AddUint64(&t.Called, 1)
fmt.Println("Foo!")
}
func main() {
var obj T
obj.Foo()
obj.Foo()
fmt.Printf("obj.Foo() is called %d times\n", obj.Called)
}
Playground: http://play.golang.org/p/59eOQdUQU1
Edit:
I just realized that the handler might not be in your own package. In such a case, you might want to write a wrapper:
var called uint64
func Foo() {
atomic.AddUint64(&called, 1)
importedPackage.Foo()
}
Edit 2:
Updated the examples to use atomic +1 operations.
Upvotes: 2