内置检测模块

编辑

对于每个服务器检测模块,都会为每个处理的请求报告一个事务。事务将存储在请求上下文中,可以通过该框架的 API 获取。请求上下文可用于报告自定义跨度

module/apmhttp

编辑

apmhttp 包提供了一个低级的 net/http 中间件处理程序。其他 Web 中间件通常应该基于此构建。

对于每个请求,事务都存储在请求上下文中,可以在您的处理程序中通过 http.Request.Context 获取。

import (
	"go.elastic.co/apm/module/apmhttp/v2"
)

func main() {
	var myHandler http.Handler = ...
	tracedHandler := apmhttp.Wrap(myHandler)
}

apmhttp 处理程序将恢复恐慌并将其发送到 Elastic APM。

apmhttp 包还提供了用于检测 http.Clienthttp.RoundTripper 的函数,以便在请求上下文中包含事务时,将传出请求跟踪为跨度。执行请求时,应使用 http.Request.WithContext 或辅助函数(例如 https://golang.ac.cn/x/net/context/ctxhttp 提供的函数)传播封闭上下文。

客户端跨度在响应主体完全使用或关闭之前不会结束。如果未执行这两者之一,则不会发送跨度。始终关闭响应主体以确保可以重用 HTTP 连接;请参阅 func (*Client) Do

import (
	"net/http"

	"golang.org/x/net/context/ctxhttp"

	"go.elastic.co/apm/v2"
	"go.elastic.co/apm/module/apmhttp/v2"
)

var tracingClient = apmhttp.WrapClient(http.DefaultClient)

func serverHandler(w http.ResponseWriter, req *http.Request) {
	// Propagate the transaction context contained in req.Context().
	resp, err := ctxhttp.Get(req.Context(), tracingClient, "http://backend.local/foo")
	if err != nil {
		apm.CaptureError(req.Context(), err).Send()
		http.Error(w, "failed to query backend", 500)
		return
	}
	body, err := ioutil.ReadAll(resp.Body)
	...
}

func main() {
	http.ListenAndServe(":8080", apmhttp.Wrap(http.HandlerFunc(serverHandler)))
}

module/apmfasthttp

编辑

apmfasthttp 包提供了一个低级的 valyala/fasthttp 中间件请求处理程序。其他基于 fasthttp 的 Web 中间件通常应该基于此构建。

对于每个请求,事务都存储在请求上下文中,可以通过您的处理程序中的 fasthttp.RequestCtx 使用 apm.TransactionFromContext 获取。

import (
	"github.com/valyala/fasthttp"

	"go.elastic.co/apm/module/apmfasthttp/v2"
)

func main() {
	var myHandler fasthttp.RequestHandler = func(ctx *fasthttp.RequestCtx) {
		apmCtx := apm.TransactionFromContext(ctx)
		// ...
	}
	tracedHandler := apmfasthttp.Wrap(myHandler)
}

apmfasthttp 处理程序将恢复恐慌并将其发送到 Elastic APM。

module/apmecho

编辑

apmecho 和 apmechov4 包分别为 Echo Web 框架的 3.x 和 4.x 版本提供中间件。

如果您使用的是 Echo 4.x(github.com/labstack/echo/v4),请使用 module/apmechov4。对于旧版本的 Echo 3.x(github.com/labstack/echo),请使用 module/apmecho

对于每个请求,事务都存储在请求上下文中,可以通过您的处理程序中的 echo.Context.Request().Context() 获取。

import (
	echo "github.com/labstack/echo/v4"

	"go.elastic.co/apm/module/apmechov4/v2"
)

func main() {
	e := echo.New()
	e.Use(apmechov4.Middleware())
	...
}

中间件将恢复恐慌并将其发送到 Elastic APM,因此您无需安装 echo/middleware.Recover 中间件。

module/apmgin

编辑

apmgin 包为 Gin Web 框架提供中间件。

对于每个请求,事务都存储在请求上下文中,可以通过您的处理程序中的 gin.Context.Request.Context() 获取。

import (
	"go.elastic.co/apm/module/apmgin/v2"
)

func main() {
	engine := gin.New()
	engine.Use(apmgin.Middleware(engine))
	...
}

apmgin 中间件将恢复恐慌并将其发送到 Elastic APM,因此您无需安装 gin.Recovery 中间件。

module/apmfiber

编辑

apmfiber 包为 Fiber Web 框架的 2.x 版本(v2.18.0 及更高版本)提供中间件。

对于每个请求,事务都存储在请求上下文中,可以通过您的处理程序中的 fiber.Ctx.Context() 获取。

import (
	"go.elastic.co/apm/module/apmfiber/v2"
)

func main() {
	app := fiber.New()
	app.Use(apmfiber.Middleware())
	...
}

apmfiber 中间件将恢复恐慌并将其发送到 Elastic APM,因此您无需安装 fiber recover 中间件。您可以使用 WithPanicPropagation 选项禁用默认行为。

module/apmbeego

编辑

apmbeego 包为 Beego Web 框架提供中间件。

对于每个请求,事务都存储在请求上下文中,可以通过您的控制器中的 beego/context.Context.Request.Context() 获取。

import (
	"github.com/astaxie/beego"

	"go.elastic.co/apm/v2"
	"go.elastic.co/apm/module/apmbeego/v2"
)

type thingController struct{beego.Controller}

func (c *thingController) Get() {
	span, _ := apm.StartSpan(c.Ctx.Request.Context(), "thingController.Get", "controller")
	span.End()
	...
}

func main() {
	beego.Router("/", &thingController{})
	beego.Router("/thing/:id:int", &thingController{}, "get:Get")
	beego.RunWithMiddleWares("localhost:8080", apmbeego.Middleware())
}

module/apmgorilla

编辑

apmgorilla 包为 Gorilla Mux 路由器提供中间件。

对于每个请求,事务都存储在请求上下文中,可以通过您的处理程序中的 http.Request.Context() 获取。

import (
	"github.com/gorilla/mux"

	"go.elastic.co/apm/module/apmgorilla/v2"
)

func main() {
	router := mux.NewRouter()
	apmgorilla.Instrument(router)
	...
}

apmgorilla 中间件将恢复恐慌并将其发送到 Elastic APM,因此您无需安装任何其他恢复中间件。

module/apmgrpc

编辑

apmgrpc 包为 gRPC-Go 提供服务器和客户端拦截器。服务器拦截器为每个传入请求报告事务,而客户端拦截器为每个传出请求报告跨度。对于每个提供的 RPC,事务都存储在传递到方法的上下文中。

import (
	"go.elastic.co/apm/module/apmgrpc/v2"
)

func main() {
	server := grpc.NewServer(
		grpc.UnaryInterceptor(apmgrpc.NewUnaryServerInterceptor()),
		grpc.StreamInterceptor(apmgrpc.NewStreamServerInterceptor()),
	)
	...
	conn, err := grpc.Dial(addr,
		grpc.WithUnaryInterceptor(apmgrpc.NewUnaryClientInterceptor()),
		grpc.WithStreamInterceptor(apmgrpc.NewStreamClientInterceptor()),
	)
	...
}

服务器拦截器可以选择性地使其恢复恐慌,方式与 grpc_recovery 相同。apmgrpc 服务器拦截器将始终将其观察到的恐慌作为错误发送到 Elastic APM 服务器。如果您想恢复恐慌,但还想继续使用 grpc_recovery,则应确保它在拦截器链中位于 apmgrpc 拦截器之前,否则 apmgrpc 不会捕获恐慌。

server := grpc.NewServer(grpc.UnaryInterceptor(
	apmgrpc.NewUnaryServerInterceptor(apmgrpc.WithRecovery()),
))
...

流拦截器发出代表整个流而不是单个消息的事务和跨度。对于客户端流,当请求失败时,跨度将结束;当任何 grpc.ClientStream.RecvMsggrpc.ClientStream.SendMsggrpc.ClientStream.Header 返回错误时;或者当 grpc.ClientStream.RecvMsg 返回非流式服务器方法时。

module/apmhttprouter

编辑

apmhttprouter 包为 httprouter 提供了一个低级的中间件处理程序。

对于每个请求,事务都存储在请求上下文中,可以通过您的处理程序中的 http.Request.Context() 获取。

import (
	"github.com/julienschmidt/httprouter"

	"go.elastic.co/apm/module/apmhttprouter/v2"
)

func main() {
	router := httprouter.New()

	const route = "/my/route"
	router.GET(route, apmhttprouter.Wrap(h, route))
	...
}

httprouter 不提供获取匹配路由的方法,因此必须将路由传递到包装器中。

或者,使用 apmhttprouter.Router 类型,它包装了 httprouter.Router,提供相同的 API 并检测添加的路由。要使用此包装器类型,请将您对 httprouter.New 的使用重写为 apmhttprouter.New;返回的类型是 *apmhttprouter.Router,而不是 *httprouter.Router

import "go.elastic.co/apm/module/apmhttprouter/v2"

func main() {
	router := apmhttprouter.New()

	router.GET(route, h)
	...
}

module/apmnegroni

编辑

apmnegroni 包为 negroni 库提供中间件。

对于每个请求,事务都存储在请求上下文中,可以在您的处理程序中通过 http.Request.Context 获取。

import (
	"net/http"

	"go.elastic.co/apm/module/apmnegroni/v2"
)

func serverHandler(w http.ResponseWriter, req *http.Request) {
	...
}

func main() {
	n := negroni.New()
	n.Use(apmnegroni.Middleware())
	n.UseHandler(serverHandler)
	http.ListenAndServe(":8080", n)
}

apmnegroni 处理程序将恢复恐慌并将其发送到 Elastic APM。

module/apmlambda

编辑

apmlambda 包拦截对您的 AWS Lambda 函数调用的请求。

此功能处于技术预览阶段,可能会在将来的版本中更改或删除。Elastic 将致力于解决任何问题,但技术预览中的功能不受正式 GA 功能的支持 SLA 的约束。

导入包足以报告函数调用。

import (
	_ "go.elastic.co/apm/module/apmlambda/v2"
)

我们目前不通过上下文公开事务;当我们这样做时,需要对您的代码进行少量更改以调用 apmlambda.Start 而不是 lambda.Start。

module/apmsql

编辑

apmsql 包提供了一种包装 database/sql 驱动程序的方法,以便将查询和其他执行报告为当前事务中的跨度。

要跟踪 SQL 查询,请使用 apmsql.Register 注册驱动程序,并使用 apmsql.Open 获取连接。参数与您分别调用 sql.Register 和 sql.Open 完全相同。

为了方便起见,我们还提供了将自动使用 apmsql.Register 注册常用驱动程序的包

  • module/apmsql/pq (github.com/lib/pq)
  • module/apmsql/pgxv4 (github.com/jackc/pgx/v4/stdlib)
  • module/apmsql/mysql (github.com/go-sql-driver/mysql)
  • module/apmsql/sqlite3 (github.com/mattn/go-sqlite3)
  • module/apmsql/sqlserver (github.com/microsoft/mssqldb)

import (
	"go.elastic.co/apm/module/apmsql/v2"
	_ "go.elastic.co/apm/module/apmsql/v2/pq"
	_ "go.elastic.co/apm/module/apmsql/v2/sqlite3"
)

func main() {
	db, err := apmsql.Open("postgres", "postgres://...")
	db, err := apmsql.Open("sqlite3", ":memory:")
}

如果使用了上下文方法,并且上下文包含事务,则会为查询和其他语句执行创建跨度。

module/apmgopg

编辑

apmgopg 包提供了一种检测 go-pg 数据库操作的方法。

要跟踪 go-pg 语句,请使用您计划使用的数据库实例调用 apmgopg.Instrument,并提供包含 apm 事务的上下文。

import (
	"github.com/go-pg/pg"

	"go.elastic.co/apm/module/apmgopg/v2"
)

func main() {
	db := pg.Connect(&pg.Options{})
	apmgopg.Instrument(db)

	db.WithContext(ctx).Model(...)
}

如果使用了上下文方法,并且上下文包含事务,则会为查询和其他语句执行创建跨度。

module/apmgopgv10

编辑

apmgopgv10 包提供了一种检测 go-pg 数据库操作 v10 版本的方法。

要跟踪 go-pg 语句,请使用您计划使用的数据库实例调用 apmgopgv10.Instrument,并提供包含 apm 事务的上下文。

import (
	"github.com/go-pg/pg/v10"

	"go.elastic.co/apm/module/apmgopgv10/v2"
)

func main() {
	db := pg.Connect(&pg.Options{})
	apmgopg.Instrument(db)

	db.WithContext(ctx).Model(...)
}

module/apmgorm

编辑

apmgorm 包提供了一种检测 GORM 数据库操作的方法。

要跟踪 GORM 操作,请导入相应的 apmgorm/dialects 包(而不是 gorm/dialects 包),并使用 apmgorm.Open(而不是 gorm.Open)。参数完全相同。

apmgorm.Open 获取 *gorm.DB 后,您可以调用 apmgorm.WithContext 将包含事务的上下文传播到操作中。

import (
	"go.elastic.co/apm/module/apmgorm/v2"
	_ "go.elastic.co/apm/module/apmgorm/v2/dialects/postgres"
)

func main() {
	db, err := apmgorm.Open("postgres", "")
	...
	db = apmgorm.WithContext(ctx, db)
	db.Find(...) // creates a "SELECT FROM <foo>" span
}

module/apmgormv2

编辑

apmgormv2 包提供了一种检测 GORM 数据库操作的方法。

要跟踪 GORM 操作,请导入相应的 apmgormv2/driver 包(而不是 gorm.io/driver 包),使用这些方言代替 gorm 驱动程序进行 gorm.Open 操作。

获得 *gorm.DB 后,您可以调用 db.WithContext 将包含事务的上下文传播到操作中。

import (
	"gorm.io/gorm"
	postgres "go.elastic.co/apm/module/apmgormv2/v2/driver/postgres"
)

func main() {
	db, err := gorm.Open(postgres.Open("dsn"), &gorm.Config{})
	...
	db = db.WithContext(ctx)
	db.Find(...) // creates a "SELECT FROM <foo>" span
}

module/apmgocql

编辑

apmgocql 包提供了一种检测 gocql 的方法,以便将查询作为当前事务中的跨度报告。

要报告 gocql 查询,请构造一个 apmgocql.Observer 并将其分配给 gocql.ClusterConfigQueryObserverBatchObserver 字段,或者通过其 Observer 方法将其安装到特定的 gocql.Querygocql.Batch 中。

只要查询与上下文相关联,并且上下文包含事务,就会为查询创建跨度。

import (
	"github.com/gocql/gocql"

	"go.elastic.co/apm/module/apmgocql/v2"
)

func main() {
	observer := apmgocql.NewObserver()
	config := gocql.NewCluster("cassandra_host")
	config.QueryObserver = observer
	config.BatchObserver = observer

	session, err := config.CreateSession()
	...
	err = session.Query("SELECT * FROM foo").WithContext(ctx).Exec()
	...
}

module/apmredigo

编辑

apmredigo 包提供了一种检测 Redigo 的方法,以便将 Redis 命令作为当前事务中的跨度报告。

要报告 Redis 命令,请使用顶级 DoDoWithTimeout 函数。除了初始 context.Context 参数外,这些函数与 redis.Conn 等效项具有相同的签名。如果传入的上下文包含已采样的事务,则会为 Redis 命令报告一个跨度。

还提供了一个顶级函数 Wrap 来包装 redis.Conn,以便其 DoDoWithTimeout 方法调用上述函数。最初,包装的连接将与后台上下文关联;其 WithContext 方法可用于获取具有其他上下文的浅拷贝。例如,在 HTTP 中间件中,您可以将连接绑定到请求上下文,这会将跨度与请求的 APM 事务关联。

import (
	"net/http"

	"github.com/gomodule/redigo/redis"

	"go.elastic.co/apm/module/apmredigo/v2"
)

var redisPool *redis.Pool // initialized at program startup

func handleRequest(w http.ResponseWriter, req *http.Request) {
	// Wrap and bind redis.Conn to request context. If the HTTP
	// server is instrumented with Elastic APM (e.g. with apmhttp),
	// Redis commands will be reported as spans within the request's
	// transaction.
	conn := apmredigo.Wrap(redisPool.Get()).WithContext(req.Context())
	defer conn.Close()
	...
}

module/apmgoredis

编辑

apmgoredis 包提供了一种检测 go-redis/redis 的方法,以便将 Redis 命令作为当前事务中的跨度报告。

要报告 Redis 命令,请使用顶级 Wrap 函数来包装 redis.Clientredis.ClusterClientredis.Ring。最初,包装的客户端将与后台上下文关联;其 WithContext 方法可用于获取具有其他上下文的浅拷贝。例如,在 HTTP 中间件中,您可以将客户端绑定到请求上下文,这会将跨度与请求的 APM 事务关联。

import (
	"net/http"

	"github.com/go-redis/redis"

	"go.elastic.co/apm/module/apmgoredis/v2"
)

var redisClient *redis.Client // initialized at program startup

func handleRequest(w http.ResponseWriter, req *http.Request) {
	// Wrap and bind redisClient to the request context. If the HTTP
	// server is instrumented with Elastic APM (e.g. with apmhttp),
	// Redis commands will be reported as spans within the request's
	// transaction.
	client := apmgoredis.Wrap(redisClient).WithContext(req.Context())
	...
}

module/apmgoredisv8

编辑

apmgoredisv8 包提供了一种检测 go-redis/redis v8 版本的方法,以便将 Redis 命令作为当前事务中的跨度报告。

要报告 Redis 命令,您可以从 redis.Clientredis.ClusterClientredis.Ring 的实例中调用 AddHook(apmgoredis.NewHook())

import (
	"github.com/go-redis/redis/v8"

	apmgoredis "go.elastic.co/apm/module/apmgoredisv8/v2"
)

func main() {
	redisClient := redis.NewClient(&redis.Options{})
	// Add apm hook to redisClient.
	// Redis commands will be reported as spans within the current transaction.
	redisClient.AddHook(apmgoredis.NewHook())

	redisClient.Get(ctx, "key")
}

module/apmrestful

编辑

apmrestful 包提供了一个 go-restful 过滤器,用于跟踪请求并捕获恐慌。

对于每个请求,事务都存储在请求上下文中,可以通过您的处理程序中的 http.Request.Context() 获取。

import (
	"github.com/emicklei/go-restful"

	"go.elastic.co/apm/module/apmrestful/v2"
)

func init() {
	// Trace all requests to web services registered with the default container.
	restful.Filter(apmrestful.Filter())
}

func main() {
	var ws restful.WebService
	ws.Path("/things").Consumes(restful.MIME_JSON, restful.MIME_XML).Produces(restful.MIME_JSON, restful.MIME_XML)
	ws.Route(ws.GET("/{id:[0-1]+}").To(func(req *restful.Request, resp *restful.Response) {
		// req.Request.Context() should be propagated to downstream operations such as database queries.
	}))
	...
}

module/apmchi

编辑

apmchi 包为 chi 路由器提供中间件,用于跟踪请求和捕获恐慌。

对于每个请求,事务都存储在请求上下文中,可以通过您的处理程序中的 http.Request.Context() 获取。

import (
	"github.com/go-chi/chi"

	"go.elastic.co/apm/module/apmchi/v2"
)

func main() {
	r := chi.NewRouter()
	r.Use(apmchi.Middleware())
	r.Get("/route/{pattern}", routeHandler)
	...
}

module/apmlogrus

编辑

apmlogrus 包提供了一个 logrus Hook 实现,用于将错误消息发送到 Elastic APM,以及一个用于向日志记录添加跟踪上下文字段的函数。

import (
	"github.com/sirupsen/logrus"

	"go.elastic.co/apm/module/apmlogrus/v2"
)

func init() {
	// apmlogrus.Hook will send "error", "panic", and "fatal" level log messages to Elastic APM.
	logrus.AddHook(&apmlogrus.Hook{})
}

func handleRequest(w http.ResponseWriter, req *http.Request) {
	// apmlogrus.TraceContext extracts the transaction and span (if any) from the given context,
	// and returns logrus.Fields containing the trace, transaction, and span IDs.
	traceContextFields := apmlogrus.TraceContext(req.Context())
	logrus.WithFields(traceContextFields).Debug("handling request")

	// Output:
	// {"level":"debug","msg":"handling request","time":"1970-01-01T00:00:00Z","trace.id":"67829ae467e896fb2b87ec2de50f6c0e","transaction.id":"67829ae467e896fb"}
}

module/apmzap

编辑

apmzap 包提供了一个 go.uber.org/zap/zapcore.Core 实现,用于将错误消息发送到 Elastic APM,以及一个用于向日志记录添加跟踪上下文字段的函数。

import (
	"go.uber.org/zap"

	"go.elastic.co/apm/module/apmzap/v2"
)

// apmzap.Core.WrapCore will wrap the core created by zap.NewExample
// such that logs are also sent to the apmzap.Core.
//
// apmzap.Core will send "error", "panic", and "fatal" level log
// messages to Elastic APM.
var logger = zap.NewExample(zap.WrapCore((&apmzap.Core{}).WrapCore))

func handleRequest(w http.ResponseWriter, req *http.Request) {
	// apmzap.TraceContext extracts the transaction and span (if any)
	// from the given context, and returns zap.Fields containing the
	// trace, transaction, and span IDs.
	traceContextFields := apmzap.TraceContext(req.Context())
	logger.With(traceContextFields...).Debug("handling request")

	// Output:
	// {"level":"debug","msg":"handling request","trace.id":"67829ae467e896fb2b87ec2de50f6c0e","transaction.id":"67829ae467e896fb"}
}

module/apmslog

编辑

apmslog 包提供了一个 slog 处理程序实现,用于将错误消息发送到 Elastic APM,以及在使用上下文感知日志记录方法时自动将跟踪上下文字段附加到日志记录中。

import (
	"context"
	"log/slog"

	"go.elastic.co/apm/module/apmslog/v2"
)

func ExampleHandler() {
	// Report slog "ERROR" level messages to Elastic APM using
	// apm.DefaultTracer() while utilizing some specific slog handler
	// to format logging messages
	apmHandler = apmslog.NewApmHandler(
		apmslog.WithHandler(
			slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{
				Level: slog.LevelInfo,
			}),
		),
	)
	logger = slog.New(apmHandler)

	// while using slog context aware methods, any existing trace,
	// transaction, or span ID are added from the given context
	tx := apm.DefaultTracer().StartTransaction("name", "type")
	defer tx.End()

	ctx := apm.ContextWithTransaction(context.Background(), tx)
	span, ctx := apm.StartSpan(ctx, "name", "type")
	defer span.End()

	// log msg will have a trace, transaction, and a span attached
	logger.InfoContext(ctx, "I should have a trace, transaction, and span id attached!")

	// the log msg will be reported to apm
	logger.ErrorContext(ctx, "I want this to be reported, but have no error to attach")

	// the log msg with its error will be reported to apm
	logger.ErrorContext(ctx, "I will report this error to apm", "error", errors.New("new error"))

	// BOTH errors with the log msg will be reported to apm. [ error, err ] slog attribute keys are by default reported
	logger.ErrorContext(ctx, "I will report this error to apm", "error", errors.New("new error"), "err", errors.New("new err"))
}

module/apmzerolog

编辑

apmzerolog 包提供了 ZerologLevelWriter 接口的实现,用于将错误记录发送到 Elastic APM,以及用于向日志记录添加跟踪上下文和详细错误堆栈跟踪的函数。

import (
	"net/http"

	"github.com/rs/zerolog"

	"go.elastic.co/apm/module/apmzerolog/v2"
)

// apmzerolog.Writer will send log records with the level error or greater to Elastic APM.
var logger = zerolog.New(zerolog.MultiLevelWriter(os.Stdout, &apmzerolog.Writer{}))

func init() {
	// apmzerolog.MarshalErrorStack will extract stack traces from
	// errors produced by github.com/pkg/errors. The main difference
	// with github.com/rs/zerolog/pkgerrors.MarshalStack is that
	// the apmzerolog implementation records fully-qualified function
	// names, enabling errors reported to Elastic APM to be attributed
	// to the correct package.
	zerolog.ErrorStackMarshaler = apmzerolog.MarshalErrorStack
}

func traceLoggingMiddleware(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		ctx := req.Context()
		logger := zerolog.Ctx(ctx).Hook(apmzerolog.TraceContextHook(ctx))
		req = req.WithContext(logger.WithContext(ctx))
		h.ServeHTTP(w, req)
	})
}

module/apmelasticsearch

编辑

apmelasticsearch 包提供了一种检测 Elasticsearch 客户端(例如 go-elasticsearcholivere/elastic)的 HTTP 传输的方法,以便将 Elasticsearch 请求作为当前事务中的跨度报告。

要为 Elasticsearch 请求创建跨度,请使用 WrapRoundTripper 函数包装客户端的 HTTP 传输,然后将请求与包含事务的上下文关联。

import (
	"net/http"

	"github.com/olivere/elastic"

	"go.elastic.co/apm/module/apmelasticsearch/v2"
)

var client, _ = elastic.NewClient(elastic.SetHttpClient(&http.Client{
	Transport: apmelasticsearch.WrapRoundTripper(http.DefaultTransport),
}))

func handleRequest(w http.ResponseWriter, req *http.Request) {
	result, err := client.Search("index").Query(elastic.NewMatchAllQuery()).Do(req.Context())
	...
}

module/apmmongo

编辑

apmmongo 包提供了一种检测 MongoDB Go Driver 的方法,以便将 MongoDB 命令作为当前事务中的跨度报告。

要为 MongoDB 命令创建跨度,请在构造客户端时将使用 apmmongo.CommandMonitor 创建的 CommandMonitor 作为选项传入,然后在执行命令时传入包含事务的上下文。

import (
	"context"
	"net/http"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"

	"go.elastic.co/apm/module/apmmongo/v2"
)

var client, _ = mongo.Connect(
	context.Background(),
	options.Client().SetMonitor(apmmongo.CommandMonitor()),
)

func handleRequest(w http.ResponseWriter, req *http.Request) {
	collection := client.Database("db").Collection("coll")
	cur, err := collection.Find(req.Context(), bson.D{})
	...
}

module/apmawssdkgo

编辑

apmawssdkgo 包提供了一种检测 AWS SDK Go 会话对象的方法,以便将 AWS 请求作为当前事务中的跨度报告。

要为 AWS 请求创建跨度,您应该在构造客户端时使用 session.NewSession 创建的 session.Session 进行包装。执行命令时,请传入包含事务的上下文。

支持以下服务:

  • S3
  • DynamoDB
  • SQS
  • SNS

将使用 apmawssdkgo.WrapSession 包装的 session.Session 传递给 AWS SDK 中的这些服务将报告当前事务中的跨度。

import (
	"context"
	"net/http"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3/s3manager"

	"go.elastic.co/apm/module/apmawssdkgo/v2"
)

func main() {
  session := session.Must(session.NewSession())
  session = apmawssdkgo.WrapSession(session)

  uploader := s3manager.NewUploader(session)
  s := &server{uploader}
  ...
}

func (s *server) handleRequest(w http.ResponseWriter, req *http.Request) {
  ctx := req.Context()
  out, err := s.uploader.UploadWithContext(ctx, &s3manager.UploadInput{
    Bucket: aws.String("your-bucket"),
    Key:    aws.String("your-key"),
    Body:   bytes.NewBuffer([]byte("your-body")),
  })
  ...
}

module/apmazure

编辑

apmazure 包提供了一种检测 Azure Pipeline Go 管道对象的方法,以便将 Azure 请求作为当前事务中的跨度报告。

要为 Azure 请求创建跨度,您应该使用相关服务的 NewPipeline 函数(如 azblob.NewPipeline)创建一个新管道,然后使用 apmazure.WrapPipeline 包装 pipeline.Pipeline。返回的 Pipeline 可以像平常一样使用。

支持以下服务:

  • Blob 存储
  • 队列存储
  • 文件存储
import (
  "github.com/Azure/azure-storage-blob-go/azblob"

  "go.elastic.co/apm/module/apmazure/v2"
)

func main() {
  p := azblob.NewPipeline(azblob.NewAnonymousCredential(), po)
  p = apmazure.WrapPipeline(p)
  u, err := url.Parse("https://my-account.blob.core.windows.net")
  serviceURL := azblob.NewServiceURL(*u, p)
  containerURL := serviceURL.NewContainerURL("mycontainer")
  blobURL := containerURL.NewBlobURL("readme.txt")
  // Use the blobURL to interact with Blob Storage
  ...
}

module/apmpgx

编辑

apmpgx 包提供了一种检测 Pgx (v4.17+) 的方法,以便将 SQL 查询作为当前事务中的跨度报告。此外,此库还扩展了对 pgx 的支持,例如 COPY FROM 查询和 BATCH,它们具有自己的跨度类型:db.postgresql.copydb.postgresql.batch

要报告 pgx 查询,请创建 pgx 连接,然后将配置提供给 apmpgx.Instrument()。如果配置中提供了日志记录器,则跟踪将写入日志。(即使没有日志记录器也可以安全使用)

只要查询与上下文相关联,并且上下文包含事务,就会为查询创建跨度。

带连接池的示例

import (
    "github.com/jackc/pgx/v4/pgxpool"

    "go.elastic.co/apm/module/apmpgx/v2"
)

func main() {
    c, err := pgxpool.ParseConfig("dsn_string")
    ...
    pool, err := pgxpool.ParseConfig("dsn")
    ...
    // set custom logger before instrumenting
    apmpgx.Instrument(pool.ConnConfig)
    ...
}

不带连接池的示例

import (
    "github.com/jackc/pgx/v4"

    "go.elastic.com/apm/module/apmpgx/v2"
)

func main() {
    c, err := pgx.Connect(context.Background, "dsn_string")
    ...
    // set custom logger before instrumenting
    apmpgx.Instrument(c.Config())
    ...
}