github.com/waldiirawan/apm-agent-go/v2@v2.2.2/example_test.go (about) 1 // Licensed to Elasticsearch B.V. under one or more contributor 2 // license agreements. See the NOTICE file distributed with 3 // this work for additional information regarding copyright 4 // ownership. Elasticsearch B.V. licenses this file to you under 5 // the Apache License, Version 2.0 (the "License"); you may 6 // not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, 12 // software distributed under the License is distributed on an 13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 // KIND, either express or implied. See the License for the 15 // specific language governing permissions and limitations 16 // under the License. 17 18 package apm_test 19 20 import ( 21 "compress/zlib" 22 "context" 23 "encoding/json" 24 "fmt" 25 "io" 26 "log" 27 "net/http" 28 "net/http/httptest" 29 "os" 30 "sync" 31 "time" 32 33 "github.com/waldiirawan/apm-agent-go/v2" 34 ) 35 36 // ExampleTracer shows how to use the Tracer API 37 func ExampleTracer() { 38 var r recorder 39 server := httptest.NewServer(&r) 40 defer server.Close() 41 42 os.Setenv("ELASTIC_APM_SERVER_URL", server.URL) 43 defer os.Unsetenv("ELASTIC_APM_SERVER_URL") 44 45 const serviceName = "service-name" 46 const serviceVersion = "1.0.0" 47 tracer, err := apm.NewTracer(serviceName, serviceVersion) 48 if err != nil { 49 log.Fatal(err) 50 } 51 defer tracer.Close() 52 53 // api is a very basic API handler, to demonstrate the usage 54 // of the tracer. api.handlerOrder creates a transaction for 55 // every call; api.handleOrder calls through to storeOrder, 56 // which adds a span to the transaction. 57 api := &api{tracer: tracer} 58 api.handleOrder(context.Background(), "fish fingers") 59 api.handleOrder(context.Background(), "detergent") 60 61 // The tracer will stream events to the APM server, and will 62 // close the request when it reaches a given size in bytes 63 // (ELASTIC_APM_API_REQUEST_SIZE) or a given duration has 64 // elapsed (ELASTIC_APM_API_REQUEST_TIME). Even so, we flush 65 // here to ensure the data reaches the server. 66 tracer.Flush(nil) 67 68 fmt.Println("number of payloads:", len(r.payloads)) 69 metadata := r.payloads[0]["metadata"].(map[string]interface{}) 70 service := metadata["service"].(map[string]interface{}) 71 agent := service["agent"].(map[string]interface{}) 72 language := service["language"].(map[string]interface{}) 73 runtime := service["runtime"].(map[string]interface{}) 74 fmt.Println(" service name:", service["name"]) 75 fmt.Println(" service version:", service["version"]) 76 fmt.Println(" agent name:", agent["name"]) 77 fmt.Println(" language name:", language["name"]) 78 fmt.Println(" runtime name:", runtime["name"]) 79 80 var transactions []map[string]interface{} 81 var spans []map[string]interface{} 82 for _, p := range r.payloads[1:] { 83 t, ok := p["transaction"].(map[string]interface{}) 84 if ok { 85 transactions = append(transactions, t) 86 continue 87 } 88 s, ok := p["span"].(map[string]interface{}) 89 if ok { 90 spans = append(spans, s) 91 continue 92 } 93 } 94 if len(transactions) != len(spans) { 95 fmt.Printf("%d transaction(s), %d span(s)\n", len(transactions), len(spans)) 96 return 97 } 98 for i, t := range transactions { 99 s := spans[i] 100 fmt.Printf(" transaction %d:\n", i) 101 fmt.Println(" name:", t["name"]) 102 fmt.Println(" type:", t["type"]) 103 fmt.Println(" context:", t["context"]) 104 fmt.Printf(" span %d:\n", i) 105 fmt.Println(" name:", s["name"]) 106 fmt.Println(" type:", s["type"]) 107 } 108 109 // Output: 110 // number of payloads: 5 111 // service name: service-name 112 // service version: 1.0.0 113 // agent name: go 114 // language name: go 115 // runtime name: gc 116 // transaction 0: 117 // name: order 118 // type: request 119 // context: map[tags:map[product:fish fingers]] 120 // span 0: 121 // name: store_order 122 // type: rpc 123 // transaction 1: 124 // name: order 125 // type: request 126 // context: map[tags:map[product:detergent]] 127 // span 1: 128 // name: store_order 129 // type: rpc 130 } 131 132 type api struct { 133 tracer *apm.Tracer 134 } 135 136 func (api *api) handleOrder(ctx context.Context, product string) { 137 tx := api.tracer.StartTransaction("order", "request") 138 defer tx.End() 139 ctx = apm.ContextWithTransaction(ctx, tx) 140 141 tx.Context.SetLabel("product", product) 142 143 time.Sleep(10 * time.Millisecond) 144 storeOrder(ctx, product) 145 time.Sleep(20 * time.Millisecond) 146 } 147 148 func storeOrder(ctx context.Context, product string) { 149 span, _ := apm.StartSpan(ctx, "store_order", "rpc") 150 defer span.End() 151 152 time.Sleep(50 * time.Millisecond) 153 } 154 155 type recorder struct { 156 mu sync.Mutex 157 payloads []map[string]interface{} 158 } 159 160 func (r *recorder) count() int { 161 r.mu.Lock() 162 defer r.mu.Unlock() 163 return len(r.payloads) 164 } 165 166 func (r *recorder) ServeHTTP(w http.ResponseWriter, req *http.Request) { 167 if req.URL.Path != "/intake/v2/events" { 168 // Ignore config requests. 169 return 170 } 171 body, err := zlib.NewReader(req.Body) 172 if err != nil { 173 panic(err) 174 } 175 decoder := json.NewDecoder(body) 176 var payloads []map[string]interface{} 177 for { 178 var m map[string]interface{} 179 if err := decoder.Decode(&m); err != nil { 180 if err == io.EOF { 181 break 182 } 183 http.Error(w, err.Error(), http.StatusInternalServerError) 184 return 185 } 186 payloads = append(payloads, m) 187 } 188 r.mu.Lock() 189 r.payloads = append(r.payloads, payloads...) 190 r.mu.Unlock() 191 }