This is the official Go package for Elastic APM.
The Go agent enables you to trace the execution of operations in your application, sending performance metrics and errors to the Elastic APM server.
This repository is in a pre-alpha state and under heavy development. Do not deploy into production!
go get -u github.com/elastic/apm-agent-go
Tested with Go 1.8+.
Apache 2.0.
If you find a bug, please report an issue. For any other assistance, please open or add to a topic on the APM discuss forum.
If you need help or hit an issue, please start by opening a topic on our discuss forums. Please note that we reserve GitHub tickets for confirmed bugs and enhancement requests.
The Go agent can be configured in code, or via environment variables.
The two most critical configuration attributes are the server URL and the secret token. All other attributes have default values, and are not required to enable tracing.
Environment variable | Default | Description |
---|---|---|
ELASTIC_APM_SERVER_URL | Base URL of the Elastic APM server. If unspecified, no tracing will take place. | |
ELASTIC_APM_SECRET_TOKEN | The secret token for Elastic APM server. | |
ELASTIC_APM_VERIFY_SERVER_CERT | true | Verify certificates when using https. |
ELASTIC_APM_FLUSH_INTERVAL | 10s | Time to wait before sending transactions to the Elastic APM server. Transactions will be batched up and sent periodically. |
ELASTIC_APM_MAX_QUEUE_SIZE | 500 | Maximum number of transactions to queue before sending to the Elastic APM server. Once this number is reached, any new transactions will replace old ones until the queue is flushed. |
ELASTIC_APM_TRANSACTION_MAX_SPANS | 500 | Maximum number of spans to capture per transaction. After this is reached, new spans will not be created, and a dropped count will be incremented. |
ELASTIC_APM_TRANSACTION_SAMPLE_RATE | 1.0 | Number in the range 0.0-1.0 inclusive, controlling how many transactions should be sampled (i.e. include full detail.) |
ELASTIC_APM_ENVIRONMENT | Environment name, e.g. "production". | |
ELASTIC_APM_FRAMEWORK_NAME | Framework name, e.g. "gin". | |
ELASTIC_APM_FRAMEWORK_VERSION | Framework version, e.g. "1.0". | |
ELASTIC_APM_SERVICE_NAME | Service name, e.g. "my-service". If this is unspecified, the agent will report the program binary name as the service name. | |
ELASTIC_APM_SERVICE_VERSION | Service version, e.g. "1.0". | |
ELASTIC_APM_HOSTNAME | Override for the hostname. |
The Go agent includes instrumentation for various standard packages, including
but not limited to net/http
and database/sql
. You can find an overview of
the included instrumentation modules below, as well as an overview of how to add
custom instrumentation to your application.
Package contrib/apmhttp
can be used to wrap net/http
handlers:
import (
"github.com/elastic/apm-agent-go/contrib/apmhttp"
)
func main() {
var myHandler http.Handler = ...
tracedHandler := apmhttp.Handler{
Handler: myHandler,
}
}
If you want your handler to recover panics and send them to Elastic APM, then you can set the Recovery field of apmhttp.Handler:
apmhttp.Handler{
Handler: myHandler,
Recovery: apmhttp.NewTraceHandler(nil),
}
Package contrib/apmgin
provides middleware for Gin:
import (
"github.com/elastic/apm-agent-go/contrib/apmgin"
)
func main() {
engine := gin.New()
engine.Use(apmgin.Middleware(engine, nil))
...
}
The apmgin middleware will recover panics and send them to Elastic APM, so you do not need to install the gin.Recovery middleware.
Package contrib/apmgorilla
provides middleware for gorilla/mux:
import (
"github.com/gorilla/mux"
"github.com/elastic/apm-agent-go/contrib/apmgorilla"
)
func main() {
router := mux.NewRouter()
router.Use(apmgorilla.Middleware(nil)) // nil for default tracer
...
}
The apmgorilla middleware will recover panics and send them to Elastic APM, so you do not need to install any other recovery middleware.
Package contrib/apmhttprouter
provides a handler wrapper for httprouter:
import (
"github.com/julienschmidt/httprouter"
"github.com/elastic/apm-agent-go"
"github.com/elastic/apm-agent-go/contrib/apmhttp"
"github.com/elastic/apm-agent-go/contrib/apmhttprouter"
)
func main() {
router := httprouter.New()
const route = "/my/route"
tracer := elasticapm.DefaultTracer
recovery := apmhttp.NewTraceRecovery(tracer) // optional panic recovery
router.GET(route, apmhttprouter.WrapHandle(h, tracer, route, recovery))
...
}
httprouter does not provide a means of obtaining the matched route, hence the route must be passed into the wrapper.
Package contrib/apmecho
provides middleware for Echo:
import (
"github.com/labstack/echo"
"github.com/elastic/apm-agent-go/contrib/apmecho"
)
func main() {
e := echo.New()
e.Use(apmecho.Middleware(nil)) // nil for default tracer
...
}
The apmecho middleware will recover panics and send them to Elastic APM, so you do not need to install the echo/middleware.Recover middleware.
Package contrib/apmlambda
intercepts and reports transactions for AWS Lambda Go
functions. Importing the package is enough to report the function invocations.
import (
_ "github.com/elastic/apm-agent-go/contrib/apmlambda"
)
We currently do not expose the transactions via context
; when we do, it will
be necessary to make a small change to your code to call apmlambda.Start instead
of lambda.Start.
Package contrib/apmsql
provides methods for wrapping database/sql/driver.Drivers
,
tracing queries as spans. To trace SQL queries, you should register drivers using
apmsql.Register
and obtain connections with apmsql.Open
. The parameters are
exactly the same as if you were to call sql.Register
and sql.Open
respectively.
As a convenience, we also provide packages which will automatically register popular
drivers with apmsql.Register
: contrib/apmsql/pq
and contrib/apmsql/sqlite3
. e.g.
import (
"github.com/elastic/apm-agent-go/contrib/apmsql"
_ "github.com/elastic/apm-agent-go/contrib/apmsql/pq"
_ "github.com/elastic/apm-agent-go/contrib/apmsql/sqlite3"
)
func main() {
db, err := apmsql.Open("pq", "postgres://...")
db, err := apmsql.Open("sqlite3", ":memory:")
}
Spans will be created for queries and other statement executions if the context methods are used, and the context includes a transaction.
Package contrib/apmgrpc
provides unary interceptors for gprc-go,
for tracing incoming server requests as transactions, and outgoing client
requests as spans:
import (
"github.com/elastic/apm-agent-go/contrib/apmgrpc"
)
func main() {
server := grpc.NewServer(grpc.UnaryInterceptor(apmgrpc.NewUnaryServerInterceptor()))
...
conn, err := grpc.Dial(addr, grpc.WithUnaryInterceptor(apmgrpc.NewUnaryClientInterceptor()))
...
}
The server interceptor can optionally be made to recover panics, in the same way as grpc_recovery. The apmgrpc server interceptor will always send panics it observes as errors to the Elastic APM server. If you want to recover panics but also want to continue using grpc_recovery, then you should ensure that it comes before the apmgrpc interceptor in the interceptor chain, or panics will not be captured by apmgrpc.
server := grpc.NewServer(grpc.UnaryInterceptor(
apmgrpc.NewUnaryServerInterceptor(apmgrpc.WithRecovery()),
))
...
There is currently no support for intercepting at the stream level. Please file an issue and/or send a pull request if this is something you need.
For custom instrumentation, elasticapm.Tracer
provides the entry point to instrumenting your application. You can use the
elasticapm.DefaultTracer
, which is configured based on ELASTIC_APM_*
environment
variables.
Given a Tracer, you will typically do one of two things:
- report a transaction, or
- report a span.
A transaction represents a top-level operation in your application, such as an HTTP or RPC request. A span represents an operation within a transaction, such as a database query, or a request to another service.
To start a new transaction, you call Tracer.StartTransaction
with the
transaction name and type. e.g.
tx := elasticapm.DefaultTracer.StartTransaction("GET /api/v1", "request")
When the transaction has finished, you call Transaction.Done
with the
duration, or supplying a negative value to have Done compute the duration
as time.Now().Since(start)
. e.g.
defer tx.Done(-1)
Once you have started a transaction, you can include it in a context
object
for propagating throughout the application. e.g.
ctx := elasticapm.ContextWithTransaction(ctx, tx)
The transaction can then be retrieved with elasticapm.TransactionFromContext
:
tx := elasticapm.TransactionFromContext(ctx)
To trace the execution of an operation within your transaction, you start
a span on that transaction using Transaction.StartSpan
. Alternatively, if
you have a transaction within a context
object, you can use elasticapm.StartSpan
instead. e.g.
span, ctx := elasticapm.StartSpan(ctx, "span_name", "span_type")
if span != nil {
defer span.Done(-1)
}
Note that both Transaction.StartSpan
and elasticapm.StartSpan
will return
a nil Span
if the transaction is not being sampled. By default, all
transactions are sampled; that is, all transactions are sent with complete
detail to the Elastic APM server. If you configure the agent to sample
transactions at less than 100%, then spans and context will be dropped, and
in this case, StartSpan will sometimes return nil. Since sampling on the
DefaultTracer can be configured via an environment variable (ELASTIC_APM_TRANSACTION_SAMPLE_RATE
),
it is a good idea to always allow for the result to be nil.
If a span is created using elasticapm.StartSpan
, it will be included
in the resulting context
. If you start a span using Transaction.StartSpan
,
then you can add it to a context
object using elasticapm.ContextWithSpan
:
ctx := elasticapm.ContextWithSpan(ctx, span)
Spans can be extracted from context
using elasticapm.SpanFromContext
:
span := elasticapm.SpanFromContext(ctx)
If you want to recover panics, and report them along with your transaction,
you can use the Tracer.Recover
or Tracer.Recovered
methods. The former
should be used as a deferred call, while the latter can be used if you have
your own recovery logic. e.g.
tx := elasticapm.DefaultTracer.StartTransaction(...)
defer tx.Done(-1)
defer elasticapm.DefaultTracer.Recover(tx)
(or)
tx := elasticapm.DefaultTracer.StartTransaction(...)
defer tx.Done(-1)
defer func() {
if v := recover(); v != nil {
e := elasticapm.DefaultTracer.Recovered(v, tx)
e.Send()
}
...
}()
If you use Tracer.Recover
, the stack trace for the panic will be included
in the error data sent to Elastic APM. If the panic value is an error
created with github.com/pkg/errors, then its stack trace will be used;
otherwise the agent code will take a stack trace from the point of recovery.
If you use Tracer.Recovered
, the stack trace is not set at all, and you
can set it using Error.SetExceptionStacktrace
as necessary.
As well asfrom recovering from panics, you can also report errors using
Tracer.NewError
. Given the resulting elasticapm.Error object, you can then
either set an "exception", or a log message. e.g.
...
if err != nil {
e := elasticapm.DefaultTracer.NewError()
e.SetException(err)
e.Exception.Handled = true // error was handled by the application
e.Transaction = tx // optional; errors need not correspond to transactions
e.Send()
}
If you are capturing errors in the context of a transaction which has been
added to a context
object, then you can use the elasticapm.CaptureError
function:
if err != nil {
e := elasticapm.CaptureError(ctx, err)
e.Send()
}