github.com/newrelic/go-agent@v3.26.0+incompatible/examples/server/main.go (about)

     1  // Copyright 2020 New Relic Corporation. All rights reserved.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  // +build go1.7
     5  
     6  package main
     7  
     8  import (
     9  	"errors"
    10  	"fmt"
    11  	"io"
    12  	"log"
    13  	"math/rand"
    14  	"net/http"
    15  	"os"
    16  	"sync"
    17  	"time"
    18  
    19  	newrelic "github.com/newrelic/go-agent"
    20  )
    21  
    22  func index(w http.ResponseWriter, r *http.Request) {
    23  	io.WriteString(w, "hello world")
    24  }
    25  
    26  func versionHandler(w http.ResponseWriter, r *http.Request) {
    27  	io.WriteString(w, "New Relic Go Agent Version: "+newrelic.Version)
    28  }
    29  
    30  func noticeError(w http.ResponseWriter, r *http.Request) {
    31  	io.WriteString(w, "noticing an error")
    32  
    33  	if txn := newrelic.FromContext(r.Context()); txn != nil {
    34  		txn.NoticeError(errors.New("my error message"))
    35  	}
    36  }
    37  
    38  func noticeErrorWithAttributes(w http.ResponseWriter, r *http.Request) {
    39  	io.WriteString(w, "noticing an error")
    40  
    41  	if txn := newrelic.FromContext(r.Context()); txn != nil {
    42  		txn.NoticeError(newrelic.Error{
    43  			Message: "uh oh. something went very wrong",
    44  			Class:   "errors are aggregated by class",
    45  			Attributes: map[string]interface{}{
    46  				"important_number": 97232,
    47  				"relevant_string":  "zap",
    48  			},
    49  		})
    50  	}
    51  }
    52  
    53  func customEvent(w http.ResponseWriter, r *http.Request) {
    54  	txn := newrelic.FromContext(r.Context())
    55  
    56  	io.WriteString(w, "recording a custom event")
    57  
    58  	if nil != txn {
    59  		txn.Application().RecordCustomEvent("my_event_type", map[string]interface{}{
    60  			"myString": "hello",
    61  			"myFloat":  0.603,
    62  			"myInt":    123,
    63  			"myBool":   true,
    64  		})
    65  	}
    66  }
    67  
    68  func setName(w http.ResponseWriter, r *http.Request) {
    69  	io.WriteString(w, "changing the transaction's name")
    70  
    71  	if txn := newrelic.FromContext(r.Context()); txn != nil {
    72  		txn.SetName("other-name")
    73  	}
    74  }
    75  
    76  func addAttribute(w http.ResponseWriter, r *http.Request) {
    77  	io.WriteString(w, "adding attributes")
    78  
    79  	if txn := newrelic.FromContext(r.Context()); txn != nil {
    80  		txn.AddAttribute("myString", "hello")
    81  		txn.AddAttribute("myInt", 123)
    82  	}
    83  }
    84  
    85  func ignore(w http.ResponseWriter, r *http.Request) {
    86  	if coinFlip := (0 == rand.Intn(2)); coinFlip {
    87  		if txn := newrelic.FromContext(r.Context()); txn != nil {
    88  			txn.Ignore()
    89  		}
    90  		io.WriteString(w, "ignoring the transaction")
    91  	} else {
    92  		io.WriteString(w, "not ignoring the transaction")
    93  	}
    94  }
    95  
    96  func segments(w http.ResponseWriter, r *http.Request) {
    97  	txn := newrelic.FromContext(r.Context())
    98  
    99  	func() {
   100  		defer newrelic.StartSegment(txn, "f1").End()
   101  
   102  		func() {
   103  			defer newrelic.StartSegment(txn, "f2").End()
   104  
   105  			io.WriteString(w, "segments!")
   106  			time.Sleep(10 * time.Millisecond)
   107  		}()
   108  		time.Sleep(15 * time.Millisecond)
   109  	}()
   110  	time.Sleep(20 * time.Millisecond)
   111  }
   112  
   113  func mysql(w http.ResponseWriter, r *http.Request) {
   114  	txn := newrelic.FromContext(r.Context())
   115  	s := newrelic.DatastoreSegment{
   116  		StartTime: newrelic.StartSegmentNow(txn),
   117  		// Product, Collection, and Operation are the most important
   118  		// fields to populate because they are used in the breakdown
   119  		// metrics.
   120  		Product:    newrelic.DatastoreMySQL,
   121  		Collection: "users",
   122  		Operation:  "INSERT",
   123  
   124  		ParameterizedQuery: "INSERT INTO users (name, age) VALUES ($1, $2)",
   125  		QueryParameters: map[string]interface{}{
   126  			"name": "Dracula",
   127  			"age":  439,
   128  		},
   129  		Host:         "mysql-server-1",
   130  		PortPathOrID: "3306",
   131  		DatabaseName: "my_database",
   132  	}
   133  	defer s.End()
   134  
   135  	time.Sleep(20 * time.Millisecond)
   136  	io.WriteString(w, `performing fake query "INSERT * from users"`)
   137  }
   138  
   139  func message(w http.ResponseWriter, r *http.Request) {
   140  	txn := newrelic.FromContext(r.Context())
   141  	s := newrelic.MessageProducerSegment{
   142  		StartTime:       newrelic.StartSegmentNow(txn),
   143  		Library:         "RabbitMQ",
   144  		DestinationType: newrelic.MessageQueue,
   145  		DestinationName: "myQueue",
   146  	}
   147  	defer s.End()
   148  
   149  	time.Sleep(20 * time.Millisecond)
   150  	io.WriteString(w, `producing a message queue message`)
   151  }
   152  
   153  func external(w http.ResponseWriter, r *http.Request) {
   154  	txn := newrelic.FromContext(r.Context())
   155  	req, _ := http.NewRequest("GET", "http://example.com", nil)
   156  
   157  	// Using StartExternalSegment is recommended because it does distributed
   158  	// tracing header setup, but if you don't have an *http.Request and
   159  	// instead only have a url string then you can start the external
   160  	// segment like this:
   161  	//
   162  	// es := newrelic.ExternalSegment{
   163  	// 	StartTime: newrelic.StartSegmentNow(txn),
   164  	// 	URL:       urlString,
   165  	// }
   166  	//
   167  	es := newrelic.StartExternalSegment(txn, req)
   168  	resp, err := http.DefaultClient.Do(req)
   169  	es.End()
   170  
   171  	if nil != err {
   172  		io.WriteString(w, err.Error())
   173  		return
   174  	}
   175  	defer resp.Body.Close()
   176  	io.Copy(w, resp.Body)
   177  }
   178  
   179  func roundtripper(w http.ResponseWriter, r *http.Request) {
   180  	// NewRoundTripper allows you to instrument external calls without
   181  	// calling StartExternalSegment by modifying the http.Client's Transport
   182  	// field.  If the Transaction parameter is nil, the RoundTripper
   183  	// returned will look for a Transaction in the request's context (using
   184  	// FromContext). This is recommended because it allows you to reuse the
   185  	// same client for multiple transactions.
   186  	client := &http.Client{}
   187  	client.Transport = newrelic.NewRoundTripper(nil, client.Transport)
   188  
   189  	request, _ := http.NewRequest("GET", "http://example.com", nil)
   190  	// Since the transaction is already added to the inbound request's
   191  	// context by WrapHandleFunc, we just need to copy the context from the
   192  	// inbound request to the external request.
   193  	request = request.WithContext(r.Context())
   194  	// Alternatively, if you don't want to copy entire context, and instead
   195  	// wanted just to add the transaction to the external request's context,
   196  	// you could do that like this:
   197  	//
   198  	//	txn := newrelic.FromContext(r.Context())
   199  	//	request = newrelic.RequestWithTransactionContext(request, txn)
   200  
   201  	resp, err := client.Do(request)
   202  	if nil != err {
   203  		io.WriteString(w, err.Error())
   204  		return
   205  	}
   206  	defer resp.Body.Close()
   207  	io.Copy(w, resp.Body)
   208  }
   209  
   210  func async(w http.ResponseWriter, r *http.Request) {
   211  	txn := newrelic.FromContext(r.Context())
   212  	wg := &sync.WaitGroup{}
   213  	wg.Add(1)
   214  	go func(txn newrelic.Transaction) {
   215  		defer wg.Done()
   216  		defer newrelic.StartSegment(txn, "async").End()
   217  		time.Sleep(100 * time.Millisecond)
   218  	}(txn.NewGoroutine())
   219  
   220  	segment := newrelic.StartSegment(txn, "wg.Wait")
   221  	wg.Wait()
   222  	segment.End()
   223  	w.Write([]byte("done!"))
   224  }
   225  
   226  func customMetric(w http.ResponseWriter, r *http.Request) {
   227  	txn := newrelic.FromContext(r.Context())
   228  	for _, vals := range r.Header {
   229  		for _, v := range vals {
   230  			// This custom metric will have the name
   231  			// "Custom/HeaderLength" in the New Relic UI.
   232  			if nil != txn {
   233  				txn.Application().RecordCustomMetric("HeaderLength", float64(len(v)))
   234  			}
   235  		}
   236  	}
   237  	io.WriteString(w, "custom metric recorded")
   238  }
   239  
   240  func browser(w http.ResponseWriter, r *http.Request) {
   241  	txn := newrelic.FromContext(r.Context())
   242  	hdr, err := txn.BrowserTimingHeader()
   243  	if nil != err {
   244  		log.Printf("unable to create browser timing header: %v", err)
   245  	}
   246  	// BrowserTimingHeader() will always return a header whose methods can
   247  	// be safely called.
   248  	if js := hdr.WithTags(); js != nil {
   249  		w.Write(js)
   250  	}
   251  	io.WriteString(w, "browser header page")
   252  }
   253  
   254  func mustGetEnv(key string) string {
   255  	if val := os.Getenv(key); "" != val {
   256  		return val
   257  	}
   258  	panic(fmt.Sprintf("environment variable %s unset", key))
   259  }
   260  
   261  func main() {
   262  	cfg := newrelic.NewConfig("Example App", mustGetEnv("NEW_RELIC_LICENSE_KEY"))
   263  	cfg.Logger = newrelic.NewDebugLogger(os.Stdout)
   264  
   265  	app, err := newrelic.NewApplication(cfg)
   266  	if nil != err {
   267  		fmt.Println(err)
   268  		os.Exit(1)
   269  	}
   270  
   271  	http.HandleFunc(newrelic.WrapHandleFunc(app, "/", index))
   272  	http.HandleFunc(newrelic.WrapHandleFunc(app, "/version", versionHandler))
   273  	http.HandleFunc(newrelic.WrapHandleFunc(app, "/notice_error", noticeError))
   274  	http.HandleFunc(newrelic.WrapHandleFunc(app, "/notice_error_with_attributes", noticeErrorWithAttributes))
   275  	http.HandleFunc(newrelic.WrapHandleFunc(app, "/custom_event", customEvent))
   276  	http.HandleFunc(newrelic.WrapHandleFunc(app, "/set_name", setName))
   277  	http.HandleFunc(newrelic.WrapHandleFunc(app, "/add_attribute", addAttribute))
   278  	http.HandleFunc(newrelic.WrapHandleFunc(app, "/ignore", ignore))
   279  	http.HandleFunc(newrelic.WrapHandleFunc(app, "/segments", segments))
   280  	http.HandleFunc(newrelic.WrapHandleFunc(app, "/mysql", mysql))
   281  	http.HandleFunc(newrelic.WrapHandleFunc(app, "/external", external))
   282  	http.HandleFunc(newrelic.WrapHandleFunc(app, "/roundtripper", roundtripper))
   283  	http.HandleFunc(newrelic.WrapHandleFunc(app, "/custommetric", customMetric))
   284  	http.HandleFunc(newrelic.WrapHandleFunc(app, "/browser", browser))
   285  	http.HandleFunc(newrelic.WrapHandleFunc(app, "/async", async))
   286  	http.HandleFunc(newrelic.WrapHandleFunc(app, "/message", message))
   287  
   288  	http.HandleFunc("/background", func(w http.ResponseWriter, req *http.Request) {
   289  		// Transactions started without an http.Request are classified as
   290  		// background transactions.
   291  		txn := app.StartTransaction("background", nil, nil)
   292  		defer txn.End()
   293  
   294  		io.WriteString(w, "background transaction")
   295  		time.Sleep(150 * time.Millisecond)
   296  	})
   297  
   298  	http.ListenAndServe(":8000", nil)
   299  }