Simple, lightweight tracing mechanism.
func Do(ctx context.Context) {
defer tracer.Fetch(ctx).Start().Stop()
// do some heavy job
}Full description of the idea is available here.
In Avito, we use the Jaeger - a distributed tracing platform. It is handy in most cases, but at production, we also use sampling. So, what is a problem, you say?
I had 0.02% requests with a write: broken pipe error and it was difficult to find the appropriate one in
the Sentry which also has trace related to it in the Jaeger.
For that reason, I wrote the simple solution to handle this specific case and found the bottleneck in our code quickly.
import (
"context"
"net/http"
"time"
"github.com/kamilsk/tracer"
)
func InjectTracer(handler http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
req = req.WithContext(tracer.Inject(req.Context(), make([]*tracer.Call, 0, 10)))
handler.ServeHTTP(rw, req)
})
}
func Handle(rw http.ResponseWriter, req *http.Request) {
ctx, cancel := context.WithTimeout(req.Context(), time.Second)
defer cancel()
call := tracer.Fetch(req.Context()).Start(req.Header.Get("X-Request-Id"))
defer call.Stop()
...
call.Checkpoint("serialize")
data := FetchData(ctx, req.Body)
call.Checkpoint("store")
if err := StoreIntoDatabase(ctx, data); err != nil {
http.Error(rw,
http.StatusText(http.StatusInternalServerError),
http.StatusInternalServerError)
return
}
rw.WriteHeader(http.StatusOK)
}
func FetchData(ctx context.Context, r io.Reader) Data {
defer tracer.Fetch(ctx).Start().Stop()
// fetch a data into a struct
}
func StoreIntoDatabase(ctx context.Context, data Data) error {
defer tracer.Fetch(ctx).Start().Stop()
// store the data into a database
}This library uses SemVer for versioning, and it is not BC-safe through major releases. You can use dep or go modules to manage its version.
$ dep ensure -add github.com/kamilsk/tracer
$ go get -u github.com/kamilsk/tracermade with ❤️ for everyone