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 ----