module/apmfasthttp/server.go (85 lines of code) (raw):
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package apmfasthttp // import "go.elastic.co/apm/module/apmfasthttp/v2"
import (
"github.com/valyala/fasthttp"
"go.elastic.co/apm/v2"
)
// Wrap returns a fasthttp.RequestHandler wrapping handler, reporting each request as
// a transaction to Elastic APM.
//
// By default, the returned RequestHandler will use apm.DefaultTracer().
// Use WithTracer to specify an alternative tracer.
//
// By default, the returned RequestHandler will recover panics, reporting
// them to the configured tracer. To override this behaviour, use
// WithRecovery.
func Wrap(handler fasthttp.RequestHandler, options ...ServerOption) fasthttp.RequestHandler {
h := new(apmHandler)
h.requestHandler = handler
for i := range options {
options[i](h)
}
if h.tracer == nil {
h.tracer = apm.DefaultTracer()
}
if h.requestName == nil {
h.requestName = ServerRequestName
}
if h.requestIgnorer == nil {
h.requestIgnorer = NewDynamicServerRequestIgnorer(h.tracer)
}
if h.recovery == nil {
h.recovery = NewTraceRecovery(h.tracer)
}
return h.handler
}
func (h *apmHandler) handler(ctx *fasthttp.RequestCtx) {
if !h.tracer.Recording() || h.requestIgnorer(ctx) {
h.requestHandler(ctx)
return
}
tx, bc, err := StartTransactionWithBody(ctx, h.tracer, h.requestName(ctx))
if err != nil {
ctx.Error(err.Error(), fasthttp.StatusInternalServerError)
return
}
defer func() {
if err := recover(); err != nil {
if h.panicPropagation {
defer panic(err)
}
// 500 status code will be set only for APM transaction
// to allow other middleware to choose a different response code
if ctx.Response.Header.StatusCode() == fasthttp.StatusOK {
ctx.Response.Header.SetStatusCode(fasthttp.StatusInternalServerError)
}
h.recovery(ctx, tx, bc, err)
}
}()
h.requestHandler(ctx)
}
// WithTracer returns a ServerOption which sets t as the tracer
// to use for tracing server requests.
func WithTracer(t *apm.Tracer) ServerOption {
if t == nil {
panic("t == nil")
}
return func(h *apmHandler) {
h.tracer = t
}
}
// WithServerRequestName returns a ServerOption which sets fn as the function
// to use to obtain the transaction name for the given server request.
func WithServerRequestName(fn RequestNameFunc) ServerOption {
if fn == nil {
panic("fn == nil")
}
return func(h *apmHandler) {
h.requestName = fn
}
}
// WithServerRequestIgnorer returns a ServerOption which sets fn as the
// function to use to determine whether or not a server request should
// be ignored. If request ignorer is nil, all requests will be reported.
func WithServerRequestIgnorer(fn RequestIgnorerFunc) ServerOption {
if fn == nil {
panic("fn == nil")
}
return func(h *apmHandler) {
h.requestIgnorer = fn
}
}
// WithRecovery returns a ServerOption which sets r as the recovery
// function to use for tracing server requests.
func WithRecovery(r RecoveryFunc) ServerOption {
if r == nil {
panic("r == nil")
}
return func(h *apmHandler) {
h.recovery = r
}
}
// WithPanicPropagation returns a ServerOption which enable panic propagation.
// Any panic will be recovered and recorded as an error in a transaction, then
// panic will be caused again.
func WithPanicPropagation() ServerOption {
return func(h *apmHandler) {
h.panicPropagation = true
}
}