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  }