github.com/waldiirawan/apm-agent-go/v2@v2.2.2/docs/instrumenting.asciidoc (about)

     1  [[builtin-modules]]
     2  === Built-in instrumentation modules
     3  
     4  For each server instrumentation module, a transaction is reported for each handled
     5  request. The transaction will be stored in the request context, which can be obtained through
     6  that framework's API. The request context can be used for reporting <<custom-instrumentation-spans, custom spans>>.
     7  
     8  * <<builtin-modules-apmhttp>>
     9  * <<builtin-modules-apmfasthttp>>
    10  * <<builtin-modules-apmecho>>
    11  * <<builtin-modules-apmgin>>
    12  * <<builtin-modules-apmfiber>>
    13  * <<builtin-modules-apmbeego>>
    14  * <<builtin-modules-apmgorilla>>
    15  * <<builtin-modules-apmgrpc>>
    16  * <<builtin-modules-apmhttprouter>>
    17  * <<builtin-modules-apmnegroni>>
    18  * <<builtin-modules-apmlambda>>
    19  * <<builtin-modules-apmsql>>
    20  * <<builtin-modules-apmgopg>>
    21  * <<builtin-modules-apmgorm>>
    22  * <<builtin-modules-apmgocql>>
    23  * <<builtin-modules-apmredigo>>
    24  * <<builtin-modules-apmgoredis>>
    25  * <<builtin-modules-apmgoredisv8>>
    26  * <<builtin-modules-apmrestful>>
    27  * <<builtin-modules-apmchi>>
    28  * <<builtin-modules-apmlogrus>>
    29  * <<builtin-modules-apmzap>>
    30  * <<builtin-modules-apmzerolog>>
    31  * <<builtin-modules-apmelasticsearch>>
    32  * <<builtin-modules-apmmongo>>
    33  * <<builtin-modules-apmawssdkgo>>
    34  * <<builtin-modules-apmazure>>
    35  * <<builtin-modules-apmpgx>>
    36  
    37  [[builtin-modules-apmhttp]]
    38  ==== module/apmhttp
    39  Package apmhttp provides a low-level `net/http` middleware handler. Other web middleware should
    40  typically be based off this.
    41  
    42  For each request, a transaction is stored in the request context, which can be obtained via
    43  https://golang.org/pkg/net/http/#Request.Context[http.Request.Context] in your handler.
    44  
    45  [source,go]
    46  ----
    47  import (
    48  	"github.com/waldiirawan/apm-agent-go/module/apmhttp/v2"
    49  )
    50  
    51  func main() {
    52  	var myHandler http.Handler = ...
    53  	tracedHandler := apmhttp.Wrap(myHandler)
    54  }
    55  ----
    56  
    57  The apmhttp handler will recover panics and send them to Elastic APM.
    58  
    59  Package apmhttp also provides functions for instrumenting an `http.Client` or `http.RoundTripper`
    60  such that outgoing requests are traced as spans, if the request context includes a transaction.
    61  When performing the request, the enclosing context should be propagated by using
    62  https://golang.org/pkg/net/http/#Request.WithContext[http.Request.WithContext], or a helper, such as those provided by https://golang.org/x/net/context/ctxhttp.
    63  
    64  Client spans are not ended until the response body is fully consumed or closed.
    65  If you fail to do either, the span will not be sent.
    66  Always close the response body to ensure HTTP connections can be reused; see https://golang.org/pkg/net/http/#Client.Do[`func (*Client) Do`].
    67  
    68  [source,go]
    69  ----
    70  import (
    71  	"net/http"
    72  
    73  	"golang.org/x/net/context/ctxhttp"
    74  
    75  	"github.com/waldiirawan/apm-agent-go/v2"
    76  	"github.com/waldiirawan/apm-agent-go/module/apmhttp/v2"
    77  )
    78  
    79  var tracingClient = apmhttp.WrapClient(http.DefaultClient)
    80  
    81  func serverHandler(w http.ResponseWriter, req *http.Request) {
    82  	// Propagate the transaction context contained in req.Context().
    83  	resp, err := ctxhttp.Get(req.Context(), tracingClient, "http://backend.local/foo")
    84  	if err != nil {
    85  		apm.CaptureError(req.Context(), err).Send()
    86  		http.Error(w, "failed to query backend", 500)
    87  		return
    88  	}
    89  	body, err := ioutil.ReadAll(resp.Body)
    90  	...
    91  }
    92  
    93  func main() {
    94  	http.ListenAndServe(":8080", apmhttp.Wrap(http.HandlerFunc(serverHandler)))
    95  }
    96  ----
    97  
    98  [[builtin-modules-apmfasthttp]]
    99  ==== module/apmfasthttp
   100  Package apmfasthttp provides a low-level https://github.com/valyala/fasthttp[valyala/fasthttp] middleware request handler. Other fasthttp-based web middleware should typically be based off this.
   101  
   102  For each request, a transaction is stored in the request context, which can be obtained via
   103  https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx[fasthttp.RequestCtx] in your handler using `apm.TransactionFromContext`.
   104  
   105  [source,go]
   106  ----
   107  import (
   108  	"github.com/valyala/fasthttp"
   109  
   110  	"github.com/waldiirawan/apm-agent-go/module/apmfasthttp/v2"
   111  )
   112  
   113  func main() {
   114  	var myHandler fasthttp.RequestHandler = func(ctx *fasthttp.RequestCtx) {
   115  		apmCtx := apm.TransactionFromContext(ctx)
   116  		// ...
   117  	}
   118  	tracedHandler := apmfasthttp.Wrap(myHandler)
   119  }
   120  ----
   121  
   122  The apmfasthttp handler will recover panics and send them to Elastic APM.
   123  
   124  [[builtin-modules-apmecho]]
   125  ==== module/apmecho
   126  Packages apmecho and apmechov4 provide middleware for the https://github.com/labstack/echo[Echo]
   127  web framework, versions 3.x and 4.x respectively.
   128  
   129  If you are using Echo 4.x (`github.com/labstack/echo/v4`), use `module/apmechov4`.
   130  For the older Echo 3.x versions (`github.com/labstack/echo`), use `module/apmecho`.
   131  
   132  For each request, a transaction is stored in the request context, which can be obtained via
   133  https://godoc.org/github.com/labstack/echo#Context[echo.Context]`.Request().Context()` in your handler.
   134  
   135  [source,go]
   136  ----
   137  import (
   138  	echo "github.com/labstack/echo/v4"
   139  
   140  	"github.com/waldiirawan/apm-agent-go/module/apmechov4/v2"
   141  )
   142  
   143  func main() {
   144  	e := echo.New()
   145  	e.Use(apmechov4.Middleware())
   146  	...
   147  }
   148  ----
   149  
   150  The middleware will recover panics and send them to Elastic APM, so you do not need to install
   151  the echo/middleware.Recover middleware.
   152  
   153  [[builtin-modules-apmgin]]
   154  ==== module/apmgin
   155  Package apmgin provides middleware for the https://gin-gonic.com/[Gin] web framework.
   156  
   157  For each request, a transaction is stored in the request context, which can be obtained via
   158  https://godoc.org/github.com/gin-gonic/gin#Context[gin.Context]`.Request.Context()` in your handler.
   159  
   160  [source,go]
   161  ----
   162  import (
   163  	"github.com/waldiirawan/apm-agent-go/module/apmgin/v2"
   164  )
   165  
   166  func main() {
   167  	engine := gin.New()
   168  	engine.Use(apmgin.Middleware(engine))
   169  	...
   170  }
   171  ----
   172  
   173  The apmgin middleware will recover panics and send them to Elastic APM, so you do not need to install the gin.Recovery middleware.
   174  
   175  [[builtin-modules-apmfiber]]
   176  ==== module/apmfiber
   177  Package apmfiber provides middleware for the https://gofiber.io/[Fiber] web framework, versions 2.x (v2.18.0 and greater).
   178  
   179  For each request, a transaction is stored in the request context, which can be obtained via
   180  https://pkg.go.dev/github.com/gofiber/fiber/v2#Ctx[fiber.Ctx]`.Context()` in your handler.
   181  
   182  [source,go]
   183  ----
   184  import (
   185  	"github.com/waldiirawan/apm-agent-go/module/apmfiber/v2"
   186  )
   187  
   188  func main() {
   189  	app := fiber.New()
   190  	app.Use(apmfiber.Middleware())
   191  	...
   192  }
   193  ----
   194  
   195  The apmfiber middleware will recover panics and send them to Elastic APM,
   196  so you do not need to install the fiber https://docs.gofiber.io/api/middleware/recover[recover] middleware.
   197  You can disable default behaviour by using `WithPanicPropagation` option.
   198  
   199  
   200  [[builtin-modules-apmbeego]]
   201  ==== module/apmbeego
   202  Package apmbeego provides middleware for the https://beego.me/[Beego] web framework.
   203  
   204  For each request, a transaction is stored in the request context, which can be obtained via
   205  https://godoc.org/github.com/astaxie/beego/context#Context[beego/context.Context]`.Request.Context()`
   206  in your controller.
   207  
   208  [source,go]
   209  ----
   210  import (
   211  	"github.com/astaxie/beego"
   212  
   213  	"github.com/waldiirawan/apm-agent-go/v2"
   214  	"github.com/waldiirawan/apm-agent-go/module/apmbeego/v2"
   215  )
   216  
   217  type thingController struct{beego.Controller}
   218  
   219  func (c *thingController) Get() {
   220  	span, _ := apm.StartSpan(c.Ctx.Request.Context(), "thingController.Get", "controller")
   221  	span.End()
   222  	...
   223  }
   224  
   225  func main() {
   226  	beego.Router("/", &thingController{})
   227  	beego.Router("/thing/:id:int", &thingController{}, "get:Get")
   228  	beego.RunWithMiddleWares("localhost:8080", apmbeego.Middleware())
   229  }
   230  ----
   231  
   232  [[builtin-modules-apmgorilla]]
   233  ==== module/apmgorilla
   234  Package apmgorilla provides middleware for the http://www.gorillatoolkit.org/pkg/mux[Gorilla Mux] router.
   235  
   236  For each request, a transaction is stored in the request context, which can be obtained via
   237  https://golang.org/pkg/net/http/#Request[http.Request]`.Context()` in your handler.
   238  
   239  [source,go]
   240  ----
   241  import (
   242  	"github.com/gorilla/mux"
   243  
   244  	"github.com/waldiirawan/apm-agent-go/module/apmgorilla/v2"
   245  )
   246  
   247  func main() {
   248  	router := mux.NewRouter()
   249  	apmgorilla.Instrument(router)
   250  	...
   251  }
   252  ----
   253  
   254  The apmgorilla middleware will recover panics and send them to Elastic APM, so you do not need to install any other recovery middleware.
   255  
   256  [[builtin-modules-apmgrpc]]
   257  ==== module/apmgrpc
   258  Package apmgrpc provides server and client interceptors for https://github.com/grpc/grpc-go[gRPC-Go].
   259  Server interceptors report transactions for each incoming request, while client interceptors
   260  report spans for each outgoing request. For each RPC served, a transaction is stored in the
   261  context passed into the method.
   262  
   263  [source,go]
   264  ----
   265  import (
   266  	"github.com/waldiirawan/apm-agent-go/module/apmgrpc/v2"
   267  )
   268  
   269  func main() {
   270  	server := grpc.NewServer(
   271  		grpc.UnaryInterceptor(apmgrpc.NewUnaryServerInterceptor()),
   272  		grpc.StreamInterceptor(apmgrpc.NewStreamServerInterceptor()),
   273  	)
   274  	...
   275  	conn, err := grpc.Dial(addr,
   276  		grpc.WithUnaryInterceptor(apmgrpc.NewUnaryClientInterceptor()),
   277  		grpc.WithStreamInterceptor(apmgrpc.NewStreamClientInterceptor()),
   278  	)
   279  	...
   280  }
   281  ----
   282  
   283  The server interceptor can optionally be made to recover panics, in the same way as
   284  https://github.com/grpc-ecosystem/go-grpc-middleware/tree/master/recovery[grpc_recovery].
   285  The apmgrpc server interceptor will always send panics it observes as errors to the Elastic APM server.
   286  If you want to recover panics but also want to continue using grpc_recovery, then you should ensure
   287  that it comes before the apmgrpc interceptor in the interceptor chain, or panics will not be captured
   288  by apmgrpc.
   289  
   290  [source,go]
   291  ----
   292  server := grpc.NewServer(grpc.UnaryInterceptor(
   293  	apmgrpc.NewUnaryServerInterceptor(apmgrpc.WithRecovery()),
   294  ))
   295  ...
   296  ----
   297  
   298  Stream interceptors emit transactions and spans that represent the entire stream,
   299  and not individual messages. For client streams, spans will be ended when the request
   300  fails; when any of `grpc.ClientStream.RecvMsg`, `grpc.ClientStream.SendMsg`, or
   301  `grpc.ClientStream.Header` return with an error; or when `grpc.ClientStream.RecvMsg`
   302  returns for a non-streaming server method.
   303  
   304  [[builtin-modules-apmhttprouter]]
   305  ==== module/apmhttprouter
   306  Package apmhttprouter provides a low-level middleware handler for https://github.com/julienschmidt/httprouter[httprouter].
   307  
   308  For each request, a transaction is stored in the request context, which can be obtained via
   309  https://golang.org/pkg/net/http/#Request[http.Request]`.Context()` in your handler.
   310  
   311  [source,go]
   312  ----
   313  import (
   314  	"github.com/julienschmidt/httprouter"
   315  
   316  	"github.com/waldiirawan/apm-agent-go/module/apmhttprouter/v2"
   317  )
   318  
   319  func main() {
   320  	router := httprouter.New()
   321  
   322  	const route = "/my/route"
   323  	router.GET(route, apmhttprouter.Wrap(h, route))
   324  	...
   325  }
   326  ----
   327  
   328  https://github.com/julienschmidt/httprouter/pull/139[httprouter does not provide a means of obtaining the matched route], hence the route must be passed into the wrapper.
   329  
   330  Alternatively, use the `apmhttprouter.Router` type, which wraps `httprouter.Router`,
   331  providing the same API and instrumenting added routes. To use this wrapper type, rewrite your use of `httprouter.New` to `apmhttprouter.New`; the returned type
   332  is `*apmhttprouter.Router`, and not `*httprouter.Router`.
   333  
   334  [source,go]
   335  ----
   336  import "github.com/waldiirawan/apm-agent-go/module/apmhttprouter/v2"
   337  
   338  func main() {
   339  	router := apmhttprouter.New()
   340  
   341  	router.GET(route, h)
   342  	...
   343  }
   344  ----
   345  
   346  [[builtin-modules-apmnegroni]]
   347  ==== module/apmnegroni
   348  
   349  Package apmnegroni provides middleware for the https://github.com/urfave/negroni/[negroni] library.
   350  
   351  For each request, a transaction is stored in the request context, which can be obtained via
   352  https://golang.org/pkg/net/http/#Request.Context[http.Request.Context] in your handler.
   353  
   354  [source,go]
   355  ----
   356  import (
   357  	"net/http"
   358  
   359  	"github.com/waldiirawan/apm-agent-go/module/apmnegroni/v2"
   360  )
   361  
   362  func serverHandler(w http.ResponseWriter, req *http.Request) {
   363  	...
   364  }
   365  
   366  func main() {
   367  	n := negroni.New()
   368  	n.Use(apmnegroni.Middleware())
   369  	n.UseHandler(serverHandler)
   370  	http.ListenAndServe(":8080", n)
   371  }
   372  ----
   373  
   374  The apmnegroni handler will recover panics and send them to Elastic APM.
   375  
   376  [[builtin-modules-apmlambda]]
   377  ==== module/apmlambda
   378  Package apmlambda intercepts requests to your AWS Lambda function invocations.
   379  
   380  experimental[]
   381  
   382  Importing the package is enough to report the function invocations.
   383  
   384  [source,go]
   385  ----
   386  import (
   387  	_ "github.com/waldiirawan/apm-agent-go/module/apmlambda/v2"
   388  )
   389  ----
   390  
   391  We currently do not expose the transactions via context; when we do, it will be
   392  necessary to make a small change to your code to call apmlambda.Start instead of
   393  lambda.Start.
   394  
   395  [[builtin-modules-apmsql]]
   396  ==== module/apmsql
   397  Package apmsql provides a means of wrapping `database/sql` drivers so that queries and other
   398  executions are reported as spans within the current transaction.
   399  
   400  To trace SQL queries, register drivers using apmsql.Register and obtain connections
   401  with apmsql.Open. The parameters are exactly the same as if you were to call sql.Register
   402  and sql.Open respectively.
   403  
   404  As a convenience, we also provide packages which will automatically register popular drivers
   405  with apmsql.Register:
   406  
   407  - module/apmsql/pq (github.com/lib/pq)
   408  - module/apmsql/pgxv4 (github.com/jackc/pgx/v4/stdlib)
   409  - module/apmsql/mysql (github.com/go-sql-driver/mysql)
   410  - module/apmsql/sqlite3 (github.com/mattn/go-sqlite3)
   411  
   412  [source,go]
   413  ----
   414  import (
   415  	"github.com/waldiirawan/apm-agent-go/module/apmsql/v2"
   416  	_ "github.com/waldiirawan/apm-agent-go/module/apmsql/v2/pq"
   417  	_ "github.com/waldiirawan/apm-agent-go/module/apmsql/v2/sqlite3"
   418  )
   419  
   420  func main() {
   421  	db, err := apmsql.Open("postgres", "postgres://...")
   422  	db, err := apmsql.Open("sqlite3", ":memory:")
   423  }
   424  ----
   425  
   426  Spans will be created for queries and other statement executions if the context methods are
   427  used, and the context includes a transaction.
   428  
   429  [[builtin-modules-apmgopg]]
   430  ==== module/apmgopg
   431  Package apmgopg provides a means of instrumenting http://github.com/go-pg/pg[go-pg] database operations.
   432  
   433  To trace `go-pg` statements, call `apmgopg.Instrument` with the database instance you plan on using and provide
   434  a context that contains an apm transaction.
   435  
   436  [source,go]
   437  ----
   438  import (
   439  	"github.com/go-pg/pg"
   440  
   441  	"github.com/waldiirawan/apm-agent-go/module/apmgopg/v2"
   442  )
   443  
   444  func main() {
   445  	db := pg.Connect(&pg.Options{})
   446  	apmgopg.Instrument(db)
   447  
   448  	db.WithContext(ctx).Model(...)
   449  }
   450  ----
   451  Spans will be created for queries and other statement executions if the context methods are
   452  used, and the context includes a transaction.
   453  
   454  [[builtin-modules-apmgopgv10]]
   455  ==== module/apmgopgv10
   456  Package apmgopgv10 provides a means of instrumenting v10 of http://github.com/go-pg/pg[go-pg] database operations.
   457  
   458  To trace `go-pg` statements, call `apmgopgv10.Instrument` with the database instance you plan on using and provide
   459  a context that contains an apm transaction.
   460  
   461  [source,go]
   462  ----
   463  import (
   464  	"github.com/go-pg/pg/v10"
   465  
   466  	"github.com/waldiirawan/apm-agent-go/module/apmgopgv10/v2"
   467  )
   468  
   469  func main() {
   470  	db := pg.Connect(&pg.Options{})
   471  	apmgopg.Instrument(db)
   472  
   473  	db.WithContext(ctx).Model(...)
   474  }
   475  ----
   476  
   477  [[builtin-modules-apmgorm]]
   478  ==== module/apmgorm
   479  Package apmgorm provides a means of instrumenting http://gorm.io[GORM] database operations.
   480  
   481  To trace `GORM` operations, import the appropriate `apmgorm/dialects` package (instead of the
   482  `gorm/dialects` package), and use `apmgorm.Open` (instead of `gorm.Open`). The parameters are
   483  exactly the same.
   484  
   485  Once you have a `*gorm.DB` from `apmgorm.Open`, you can call `apmgorm.WithContext` to
   486  propagate a context containing a transaction to the operations:
   487  
   488  [source,go]
   489  ----
   490  import (
   491  	"github.com/waldiirawan/apm-agent-go/module/apmgorm/v2"
   492  	_ "github.com/waldiirawan/apm-agent-go/module/apmgorm/v2/dialects/postgres"
   493  )
   494  
   495  func main() {
   496  	db, err := apmgorm.Open("postgres", "")
   497  	...
   498  	db = apmgorm.WithContext(ctx, db)
   499  	db.Find(...) // creates a "SELECT FROM <foo>" span
   500  }
   501  ----
   502  
   503  ==== module/apmgormv2
   504  Package apmgormv2 provides a means of instrumenting http://gorm.io[GORM] database operations.
   505  
   506  To trace `GORM` operations, import the appropriate `apmgormv2/driver` package (instead of the
   507  `gorm.io/driver` package), use these dialects to `gorm.Open` instead of gorm drivers.
   508  
   509  Once you have a `*gorm.DB`, you can call `db.WithContext` to
   510  propagate a context containing a transaction to the operations:
   511  
   512  [source,go]
   513  ----
   514  import (
   515  	"gorm.io/gorm"
   516  	postgres "github.com/waldiirawan/apm-agent-go/module/apmgormv2/v2/driver/postgres"
   517  )
   518  
   519  func main() {
   520  	db, err := gorm.Open(postgres.Open("dsn"), &gorm.Config{})
   521  	...
   522  	db = db.WithContext(ctx)
   523  	db.Find(...) // creates a "SELECT FROM <foo>" span
   524  }
   525  ----
   526  
   527  [[builtin-modules-apmgocql]]
   528  ==== module/apmgocql
   529  Package apmgocql provides a means of instrumenting https://github.com/gocql/gocql[gocql] so
   530  that queries are reported as spans within the current transaction.
   531  
   532  To report `gocql` queries, construct an `apmgocql.Observer` and assign it to
   533  the `QueryObserver` and `BatchObserver` fields of `gocql.ClusterConfig`, or install it
   534  into a specific `gocql.Query` or `gocql.Batch` via their `Observer` methods.
   535  
   536  Spans will be created for queries as long as they have context associated, and the
   537  context includes a transaction.
   538  
   539  [source,go]
   540  ----
   541  import (
   542  	"github.com/gocql/gocql"
   543  
   544  	"github.com/waldiirawan/apm-agent-go/module/apmgocql/v2"
   545  )
   546  
   547  func main() {
   548  	observer := apmgocql.NewObserver()
   549  	config := gocql.NewCluster("cassandra_host")
   550  	config.QueryObserver = observer
   551  	config.BatchObserver = observer
   552  
   553  	session, err := config.CreateSession()
   554  	...
   555  	err = session.Query("SELECT * FROM foo").WithContext(ctx).Exec()
   556  	...
   557  }
   558  ----
   559  
   560  [[builtin-modules-apmredigo]]
   561  ==== module/apmredigo
   562  Package apmredigo provides a means of instrumenting https://github.com/gomodule/redigo[Redigo]
   563  so that Redis commands are reported as spans within the current transaction.
   564  
   565  To report Redis commands, use the top-level `Do` or `DoWithTimeout` functions.
   566  These functions have the same signature as the `redis.Conn` equivalents apart from an
   567  initial `context.Context` parameter. If the context passed in contains a sampled
   568  transaction, a span will be reported for the Redis command.
   569  
   570  Another top-level function, `Wrap`, is provided to wrap a `redis.Conn` such that its
   571  `Do` and `DoWithTimeout` methods call the above mentioned functions. Initially, the
   572  wrapped connection will be associated with the background context; its `WithContext`
   573  method may be used to obtain a shallow copy with another context. For example, in an
   574  HTTP middleware you might bind a connection to the request context, which would
   575  associate spans with the request's APM transaction.
   576  
   577  [source,go]
   578  ----
   579  import (
   580  	"net/http"
   581  
   582  	"github.com/gomodule/redigo/redis"
   583  
   584  	"github.com/waldiirawan/apm-agent-go/module/apmredigo/v2"
   585  )
   586  
   587  var redisPool *redis.Pool // initialized at program startup
   588  
   589  func handleRequest(w http.ResponseWriter, req *http.Request) {
   590  	// Wrap and bind redis.Conn to request context. If the HTTP
   591  	// server is instrumented with Elastic APM (e.g. with apmhttp),
   592  	// Redis commands will be reported as spans within the request's
   593  	// transaction.
   594  	conn := apmredigo.Wrap(redisPool.Get()).WithContext(req.Context())
   595  	defer conn.Close()
   596  	...
   597  }
   598  ----
   599  
   600  [[builtin-modules-apmgoredis]]
   601  ==== module/apmgoredis
   602  Package apmgoredis provides a means of instrumenting https://github.com/go-redis/redis[go-redis/redis]
   603  so that Redis commands are reported as spans within the current transaction.
   604  
   605  To report Redis commands, use the top-level `Wrap` function to wrap a
   606  `redis.Client`, `redis.ClusterClient`, or `redis.Ring`. Initially, the wrapped
   607  client will be associated with the background context; its `WithContext` method
   608  may be used to obtain a shallow copy with another context. For example, in an
   609  HTTP middleware you might bind a client to the request context, which would
   610  associate spans with the request's APM transaction.
   611  
   612  [source,go]
   613  ----
   614  import (
   615  	"net/http"
   616  
   617  	"github.com/go-redis/redis"
   618  
   619  	"github.com/waldiirawan/apm-agent-go/module/apmgoredis/v2"
   620  )
   621  
   622  var redisClient *redis.Client // initialized at program startup
   623  
   624  func handleRequest(w http.ResponseWriter, req *http.Request) {
   625  	// Wrap and bind redisClient to the request context. If the HTTP
   626  	// server is instrumented with Elastic APM (e.g. with apmhttp),
   627  	// Redis commands will be reported as spans within the request's
   628  	// transaction.
   629  	client := apmgoredis.Wrap(redisClient).WithContext(req.Context())
   630  	...
   631  }
   632  ----
   633  
   634  [[builtin-modules-apmgoredisv8]]
   635  ==== module/apmgoredisv8
   636  Package apmgoredisv8 provides a means of instrumenting https://github.com/go-redis/redis[go-redis/redis] for v8
   637  so that Redis commands are reported as spans within the current transaction.
   638  
   639  To report Redis commands, you can call `AddHook(apmgoredis.NewHook())`
   640  from instance of `redis.Client`, `redis.ClusterClient`, or `redis.Ring`.
   641  
   642  [source,go]
   643  ----
   644  import (
   645  	"github.com/go-redis/redis/v8"
   646  
   647  	apmgoredis "github.com/waldiirawan/apm-agent-go/module/apmgoredisv8/v2"
   648  )
   649  
   650  func main() {
   651  	redisClient := redis.NewClient(&redis.Options{})
   652  	// Add apm hook to redisClient.
   653  	// Redis commands will be reported as spans within the current transaction.
   654  	redisClient.AddHook(apmgoredis.NewHook())
   655  
   656  	redisClient.Get(ctx, "key")
   657  }
   658  ----
   659  
   660  [[builtin-modules-apmrestful]]
   661  ==== module/apmrestful
   662  Package apmrestful provides a https://github.com/emicklei/go-restful[go-restful] filter
   663  for tracing requests, and capturing panics.
   664  
   665  For each request, a transaction is stored in the request context, which can be obtained via
   666  https://golang.org/pkg/net/http/#Request[http.Request]`.Context()` in your handler.
   667  
   668  [source,go]
   669  ----
   670  import (
   671  	"github.com/emicklei/go-restful"
   672  
   673  	"github.com/waldiirawan/apm-agent-go/module/apmrestful/v2"
   674  )
   675  
   676  func init() {
   677  	// Trace all requests to web services registered with the default container.
   678  	restful.Filter(apmrestful.Filter())
   679  }
   680  
   681  func main() {
   682  	var ws restful.WebService
   683  	ws.Path("/things").Consumes(restful.MIME_JSON, restful.MIME_XML).Produces(restful.MIME_JSON, restful.MIME_XML)
   684  	ws.Route(ws.GET("/{id:[0-1]+}").To(func(req *restful.Request, resp *restful.Response) {
   685  		// req.Request.Context() should be propagated to downstream operations such as database queries.
   686  	}))
   687  	...
   688  }
   689  ----
   690  
   691  [[builtin-modules-apmchi]]
   692  ==== module/apmchi
   693  Package apmchi provides middleware for https://github.com/go-chi/chi[chi] routers,
   694  for tracing requests and capturing panics.
   695  
   696  For each request, a transaction is stored in the request context, which can be obtained via
   697  https://golang.org/pkg/net/http/#Request[http.Request]`.Context()` in your handler.
   698  
   699  [source,go]
   700  ----
   701  import (
   702  	"github.com/go-chi/chi"
   703  
   704  	"github.com/waldiirawan/apm-agent-go/module/apmchi/v2"
   705  )
   706  
   707  func main() {
   708  	r := chi.NewRouter()
   709  	r.Use(apmchi.Middleware())
   710  	r.Get("/route/{pattern}", routeHandler)
   711  	...
   712  }
   713  ----
   714  
   715  [[builtin-modules-apmlogrus]]
   716  ==== module/apmlogrus
   717  Package apmlogrus provides a https://github.com/sirupsen/logrus[logrus] Hook
   718  implementation for sending error messages to Elastic APM, as well as a function
   719  for adding trace context fields to log records.
   720  
   721  [source,go]
   722  ----
   723  import (
   724  	"github.com/sirupsen/logrus"
   725  
   726  	"github.com/waldiirawan/apm-agent-go/module/apmlogrus/v2"
   727  )
   728  
   729  func init() {
   730  	// apmlogrus.Hook will send "error", "panic", and "fatal" level log messages to Elastic APM.
   731  	logrus.AddHook(&apmlogrus.Hook{})
   732  }
   733  
   734  func handleRequest(w http.ResponseWriter, req *http.Request) {
   735  	// apmlogrus.TraceContext extracts the transaction and span (if any) from the given context,
   736  	// and returns logrus.Fields containing the trace, transaction, and span IDs.
   737  	traceContextFields := apmlogrus.TraceContext(req.Context())
   738  	logrus.WithFields(traceContextFields).Debug("handling request")
   739  
   740  	// Output:
   741  	// {"level":"debug","msg":"handling request","time":"1970-01-01T00:00:00Z","trace.id":"67829ae467e896fb2b87ec2de50f6c0e","transaction.id":"67829ae467e896fb"}
   742  }
   743  ----
   744  
   745  [[builtin-modules-apmzap]]
   746  ==== module/apmzap
   747  Package apmzap provides a https://godoc.org/go.uber.org/zap/zapcore#Core[go.uber.org/zap/zapcore.Core]
   748  implementation for sending error messages to Elastic APM, as well as a function
   749  for adding trace context fields to log records.
   750  
   751  [source,go]
   752  ----
   753  import (
   754  	"go.uber.org/zap"
   755  
   756  	"github.com/waldiirawan/apm-agent-go/module/apmzap/v2"
   757  )
   758  
   759  // apmzap.Core.WrapCore will wrap the core created by zap.NewExample
   760  // such that logs are also sent to the apmzap.Core.
   761  //
   762  // apmzap.Core will send "error", "panic", and "fatal" level log
   763  // messages to Elastic APM.
   764  var logger = zap.NewExample(zap.WrapCore((&apmzap.Core{}).WrapCore))
   765  
   766  func handleRequest(w http.ResponseWriter, req *http.Request) {
   767  	// apmzap.TraceContext extracts the transaction and span (if any)
   768  	// from the given context, and returns zap.Fields containing the
   769  	// trace, transaction, and span IDs.
   770  	traceContextFields := apmzap.TraceContext(req.Context())
   771  	logger.With(traceContextFields...).Debug("handling request")
   772  
   773  	// Output:
   774  	// {"level":"debug","msg":"handling request","trace.id":"67829ae467e896fb2b87ec2de50f6c0e","transaction.id":"67829ae467e896fb"}
   775  }
   776  ----
   777  
   778  [[builtin-modules-apmzerolog]]
   779  ==== module/apmzerolog
   780  Package apmzerolog provides an implementation of https://github.com/rs/zerolog[Zerolog]'s
   781  `LevelWriter` interface for sending error records to Elastic APM, as well as functions
   782  for adding trace context and detailed error stack traces to log records.
   783  
   784  [source,go]
   785  ----
   786  import (
   787  	"net/http"
   788  
   789  	"github.com/rs/zerolog"
   790  
   791  	"github.com/waldiirawan/apm-agent-go/module/apmzerolog/v2"
   792  )
   793  
   794  // apmzerolog.Writer will send log records with the level error or greater to Elastic APM.
   795  var logger = zerolog.New(zerolog.MultiLevelWriter(os.Stdout, &apmzerolog.Writer{}))
   796  
   797  func init() {
   798  	// apmzerolog.MarshalErrorStack will extract stack traces from
   799  	// errors produced by github.com/pkg/errors. The main difference
   800  	// with github.com/rs/zerolog/pkgerrors.MarshalStack is that
   801  	// the apmzerolog implementation records fully-qualified function
   802  	// names, enabling errors reported to Elastic APM to be attributed
   803  	// to the correct package.
   804  	zerolog.ErrorStackMarshaler = apmzerolog.MarshalErrorStack
   805  }
   806  
   807  func traceLoggingMiddleware(h http.Handler) http.Handler {
   808  	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   809  		ctx := req.Context()
   810  		logger := zerolog.Ctx(ctx).Hook(apmzerolog.TraceContextHook(ctx))
   811  		req = req.WithContext(logger.WithContext(ctx))
   812  		h.ServeHTTP(w, req)
   813  	})
   814  }
   815  ----
   816  
   817  [[builtin-modules-apmelasticsearch]]
   818  ==== module/apmelasticsearch
   819  Package apmelasticsearch provides a means of instrumenting the HTTP transport
   820  of Elasticsearch clients, such as https://github.com/elastic/go-elasticsearch[go-elasticsearch]
   821  and https://github.com/olivere/elastic[olivere/elastic], so that Elasticsearch
   822  requests are reported as spans within the current transaction.
   823  
   824  To create spans for an Elasticsearch request, wrap the client's HTTP
   825  transport using the `WrapRoundTripper` function, and then associate the request
   826  with a context containing a transaction.
   827  
   828  [source,go]
   829  ----
   830  import (
   831  	"net/http"
   832  
   833  	"github.com/olivere/elastic"
   834  
   835  	"github.com/waldiirawan/apm-agent-go/module/apmelasticsearch/v2"
   836  )
   837  
   838  var client, _ = elastic.NewClient(elastic.SetHttpClient(&http.Client{
   839  	Transport: apmelasticsearch.WrapRoundTripper(http.DefaultTransport),
   840  }))
   841  
   842  func handleRequest(w http.ResponseWriter, req *http.Request) {
   843  	result, err := client.Search("index").Query(elastic.NewMatchAllQuery()).Do(req.Context())
   844  	...
   845  }
   846  ----
   847  
   848  [[builtin-modules-apmmongo]]
   849  ==== module/apmmongo
   850  Package apmmongo provides a means of instrumenting the
   851  https://github.com/mongodb/mongo-go-driver[MongoDB Go Driver], so that MongoDB
   852  commands are reported as spans within the current transaction.
   853  
   854  To create spans for MongoDB commands, pass in a `CommandMonitor` created
   855  with `apmmongo.CommandMonitor` as an option when constructing a client, and then when
   856  executing commands, pass in a context containing a transaction.
   857  
   858  [source,go]
   859  ----
   860  import (
   861  	"context"
   862  	"net/http"
   863  
   864  	"go.mongodb.org/mongo-driver/bson"
   865  	"go.mongodb.org/mongo-driver/mongo"
   866  	"go.mongodb.org/mongo-driver/mongo/options"
   867  
   868  	"github.com/waldiirawan/apm-agent-go/module/apmmongo/v2"
   869  )
   870  
   871  var client, _ = mongo.Connect(
   872  	context.Background(),
   873  	options.Client().SetMonitor(apmmongo.CommandMonitor()),
   874  )
   875  
   876  func handleRequest(w http.ResponseWriter, req *http.Request) {
   877  	collection := client.Database("db").Collection("coll")
   878  	cur, err := collection.Find(req.Context(), bson.D{})
   879  	...
   880  }
   881  ----
   882  
   883  [[builtin-modules-apmawssdkgo]]
   884  ==== module/apmawssdkgo
   885  Package apmawssdkgo provides a means of instrumenting the
   886  https://github.com/aws/aws-sdk-go[AWS SDK Go] session object, so that
   887  AWS requests are reported as spans within the current transaction.
   888  
   889  To create spans for AWS requests, you should wrap the `session.Session` created
   890  with `session.NewSession` when constructing a client. When executing commands,
   891  pass in a context containing a transaction.
   892  
   893  The following services are supported:
   894  
   895  - S3
   896  - DynamoDB
   897  - SQS
   898  - SNS
   899  
   900  Passing a `session.Session` wrapped with `apmawssdkgo.WrapSession` to these
   901  services from the AWS SDK will report spans within the current transaction.
   902  
   903  [source,go]
   904  ----
   905  import (
   906  	"context"
   907  	"net/http"
   908  
   909  	"github.com/aws/aws-sdk-go/aws"
   910  	"github.com/aws/aws-sdk-go/aws/session"
   911  	"github.com/aws/aws-sdk-go/service/s3/s3manager"
   912  
   913  	"github.com/waldiirawan/apm-agent-go/module/apmawssdkgo/v2"
   914  )
   915  
   916  func main() {
   917    session := session.Must(session.NewSession())
   918    session = apmawssdkgo.WrapSession(session)
   919  
   920    uploader := s3manager.NewUploader(session)
   921    s := &server{uploader}
   922    ...
   923  }
   924  
   925  func (s *server) handleRequest(w http.ResponseWriter, req *http.Request) {
   926    ctx := req.Context()
   927    out, err := s.uploader.UploadWithContext(ctx, &s3manager.UploadInput{
   928      Bucket: aws.String("your-bucket"),
   929      Key:    aws.String("your-key"),
   930      Body:   bytes.NewBuffer([]byte("your-body")),
   931    })
   932    ...
   933  }
   934  ----
   935  
   936  [[builtin-modules-apmazure]]
   937  ==== module/apmazure
   938  Package apmazure provides a means of instrumenting the
   939  https://github.com/Azure/azure-pipeline-go[Azure Pipeline Go] pipeline object,
   940  so that Azure requests are reported as spans within the current transaction.
   941  
   942  To create spans for Azure requests, you should create a new pipeline using the
   943  relevant service's `NewPipeline` function, like `azblob.NewPipeline`, then wrap
   944  the `pipeline.Pipeline` with `apmazure.WrapPipeline`. The returned `Pipeline`
   945  can be used as normal.
   946  
   947  The following services are supported:
   948  
   949  - Blob Storage
   950  - Queue Storage
   951  - File Storage
   952  
   953  
   954  [source,go]
   955  ----
   956  import (
   957    "github.com/Azure/azure-storage-blob-go/azblob"
   958  
   959    "github.com/waldiirawan/apm-agent-go/module/apmazure/v2"
   960  )
   961  
   962  func main() {
   963    p := azblob.NewPipeline(azblob.NewAnonymousCredential(), po)
   964    p = apmazure.WrapPipeline(p)
   965    u, err := url.Parse("https://my-account.blob.core.windows.net")
   966    serviceURL := azblob.NewServiceURL(*u, p)
   967    containerURL := serviceURL.NewContainerURL("mycontainer")
   968    blobURL := containerURL.NewBlobURL("readme.txt")
   969    // Use the blobURL to interact with Blob Storage
   970    ...
   971  }
   972  ----
   973  
   974  [[builtin-modules-apmpgx]]
   975  ==== module/apmpgx
   976  Package apmpgx provides a means of instrumenting the
   977  https://github.com/jackc/pgx[Pgx] for v4.17+,
   978  so that SQL queries are reported as spans within the current transaction.
   979  Also this lib have extended support of pgx, such as COPY FROM queries and BATCH which have their own span types:
   980  `db.postgresql.copy` and `db.postgresql.batch` accordingly.
   981  
   982  To report `pgx` queries, create `pgx` connection, and then provide config to `apmpgx.Instrument()`. If logger is presented in config,
   983  then traces will be written to log. (It's safe to use without logger)
   984  
   985  Spans will be created for queries as long as they have context associated, and the
   986  context includes a transaction.
   987  
   988  Example with pool:
   989  [source,go]
   990  ----
   991  import (
   992      "github.com/jackc/pgx/v4/pgxpool"
   993  
   994      "go.elastic.com/apm/module/apmpgx/v2"
   995  )
   996  
   997  func main() {
   998      c, err := pgxpool.ParseConfig("dsn_string")
   999      ...
  1000      pool, err := pgxpool.ParseConfig("dsn")
  1001      ...
  1002      // set custom logger before instrumenting
  1003      apmpgx.Instrument(pool.ConnConfig)
  1004      ...
  1005  }
  1006  ----
  1007  
  1008  Example without pool:
  1009  [source,go]
  1010  ----
  1011  import (
  1012      "github.com/jackc/pgx/v4"
  1013  
  1014      "go.elastic.com/apm/module/apmpgx/v2"
  1015  )
  1016  
  1017  func main() {
  1018      c, err := pgx.Connect(context.Background, "dsn_string")
  1019      ...
  1020      // set custom logger before instrumenting
  1021      apmpgx.Instrument(c.Config())
  1022      ...
  1023  }
  1024  ----