tracer
packagemoduleThis package is not in the latest version of its module.
Details
Validgo.mod file
The Go module system was introduced in Go 1.11 and is the official dependency management solution for Go.
Redistributable license
Redistributable licenses place minimal restrictions on how software can be used, modified, and redistributed.
Tagged version
Modules with tagged versions give importers more predictable builds.
Stable version
When a project reaches major version v1 it is considered stable.
- Learn more about best practices
Repository
Links
README¶
🧶 tracer
Simple, lightweight tracing.
💡 Idea
The tracer provides API to trace execution flow.
func Do(ctx context.Context) {defer tracer.Fetch(ctx).Start().Stop()// do some heavy job}Full description of the idea is availablehere.
🏆 Motivation
AtAvito, we use theJaeger - 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 awrite: broken pipe error and it was difficult to find the appropriate one intheSentry which also has trace related to it in theJaeger.
For that reason, I wrote the simple solution to handle this specific case and found the bottleneck in our code quickly.
🤼♂️ How to
import ("context""io""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, err := FetchData(ctx, req.Body)if err != nil {http.Error(rw, err.Error(), http.StatusInternalServerError)return}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, error) {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}Output:
allocates at call stack: 0, detailed call stack:call Handle [ca7a87c4-58d0-4fdf-857c-ef49fc3bf271]: 14.038083ms, allocates: 2checkpoint [serialize]: 1.163587mscheckpoint [store]: 2.436265mscall FetchData: 1.192829ms, allocates: 0call StoreIntoDatabase: 10.428663ms, allocates: 0🧩 Integration
The library usesSemVer for versioning, and it is notBC-safe through major releases.You can usego modules ordep to manage its version.
$ go get -u github.com/kamilsk/tracer$ dep ensure -add github.com/kamilsk/tracermade with ❤️ for everyone
Documentation¶
Overview¶
Example¶
package mainimport ("bytes""context""encoding/gob""encoding/json""io""net/http""net/http/httptest""os""regexp""strings""time""github.com/kamilsk/tracer")type Data struct {Title string `json:"title"`Subtitle string `json:"subtitle"`}func main() {rec := httptest.NewRecorder()req := httptest.NewRequest(http.MethodPost, "/message",strings.NewReader(`{"title": "tracer", "subtitle": "🧶 Simple, lightweight tracing mechanism."}`))req.Header.Set("X-Request-Id", "ca7a87c4-58d0-4fdf-857c-ef49fc3bf271")handler := InjectTracer(FlushTracer(http.HandlerFunc(Handle)))handler.ServeHTTP(rec, req)_, _ = io.Copy(os.Stdout, strings.NewReader(stabilize(rec.Body.String())))}func FlushTracer(handler http.Handler) http.Handler {return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {handler.ServeHTTP(rw, req)_, _ = rw.Write([]byte(tracer.Fetch(req.Context()).String()))})}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()time.Sleep(time.Millisecond)call.Checkpoint("serialize")data, err := FetchData(ctx, req.Body)if err != nil {http.Error(rw, err.Error(), http.StatusInternalServerError)return}time.Sleep(time.Millisecond)call.Checkpoint("store")if err := StoreIntoDatabase(ctx, data); err != nil {http.Error(rw, err.Error(), http.StatusInternalServerError)return}rw.WriteHeader(http.StatusOK)}func FetchData(ctx context.Context, r io.Reader) (Data, error) {defer tracer.Fetch(ctx).Start().Stop()time.Sleep(time.Millisecond)var data Dataerr := json.NewDecoder(r).Decode(&data)return data, err}func StoreIntoDatabase(ctx context.Context, data Data) error {defer tracer.Fetch(ctx).Start().Stop()time.Sleep(10 * time.Millisecond)return gob.NewEncoder(bytes.NewBuffer(nil)).Encode(data)}func stabilize(raw string) string {raw = strings.Replace(raw, "tracer_test.", "", -1)raw = regexp.MustCompile(`Handle (.+): (\d{2}\.\d+ms)`).ReplaceAllString(raw, "Handle $1: 12.345678ms")raw = regexp.MustCompile(`\[serialize]: (\d\.\d+ms)`).ReplaceAllString(raw, "[serialize]: 1.234567ms")raw = regexp.MustCompile(`\[store]: (\d\.\d+ms)`).ReplaceAllString(raw, "[store]: 1.234567ms")raw = regexp.MustCompile(`FetchData: (\d\.\d+ms)`).ReplaceAllString(raw, "FetchData: 1.234567ms")raw = regexp.MustCompile(`StoreIntoDatabase: (\d{2}\.\d+ms)`).ReplaceAllString(raw, "StoreIntoDatabase: 12.345678ms")return raw}Output:allocates at call stack: 0, detailed call stack:call Handle [ca7a87c4-58d0-4fdf-857c-ef49fc3bf271]: 12.345678ms, allocates: 2checkpoint [serialize]: 1.234567mscheckpoint [store]: 1.234567mscall FetchData: 1.234567ms, allocates: 0call StoreIntoDatabase: 12.345678ms, allocates: 0
Index¶
Examples¶
Constants¶
This section is empty.
Variables¶
This section is empty.
Functions¶
Types¶
typeCall¶
type Call struct {// contains filtered or unexported fields}Call holds information about a current function call.
func (*Call)Checkpoint¶
Checkpoint stores timestamp of a current execution position of the current call.
func Do(ctx context.Context) {call := tracer.Fetch(ctx).Start()defer call.Stop()...call.Checkpoint()...call.Checkpoint("id", "labelX", "labelY")...}typeCallerInfo¶
type CallerInfo struct {Entryuintptr// the entry address of the functionNamestring// the name of the functionFilestring// the file name andLineint// line number of the source code of the function}CallerInfo holds information about a caller.
funcCaller¶
func Caller(skipint)CallerInfo
Caller returns information about a caller at position after the skip steps.
func StoreToDatabase(data Payload) error {defer stats.NewTiming().Send(Caller(2).Name)// do something heavy}typeCheckpoint¶
type Checkpoint struct {// contains filtered or unexported fields}Checkpoint holds information about a current execution position of a current call.
typeTrace¶
type Trace struct {// contains filtered or unexported fields}Trace holds information about a current execution flow.
funcFetch¶
Fetch tries to get the tracer from a context or returns safe nil.
tracer.Fetch(context.Background()).Start().Stop() // won't panic