Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fasthttp support!! #957

Merged
merged 38 commits into from
Jun 4, 2021
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
5496693
Add fasthttp support
Apr 28, 2021
20a5542
Upgrade fasthttp to v1.24.0
Apr 28, 2021
9343870
Fixes
May 3, 2021
096a75b
Fixes
May 3, 2021
3cd4e20
Add support to apmcontext.TransactionFromContext
May 3, 2021
cfacf14
Upgrade fasthttp
May 24, 2021
0100932
Refactor
May 24, 2021
ee6acf0
Update module/apmfasthttp/utils.go
May 24, 2021
6c52c3d
Merge branch 'master' of https://github.com/elastic/apm-agent-go
Jun 1, 2021
c927658
Upgrade dependencies
Jun 1, 2021
464542d
Fix
Jun 1, 2021
feb30a9
Delete main.go
Jun 1, 2021
f1fc9d5
Flip traceparent headers
Jun 2, 2021
e2e45c5
Run make scripts/Dockerfile-testing update-licenses fmt
Jun 2, 2021
1b21874
Fix typo
Jun 2, 2021
cab96b5
Update docs
Jun 2, 2021
eef5f32
Fix typo
Jun 2, 2021
a4d5652
Fix typo
Jun 2, 2021
dd6b9a1
Make overwitable the ContextWithBodyCapturer/BodyCapturerFromContext …
Jun 3, 2021
791218e
Add doc file
Jun 3, 2021
6ab3080
Update docs/instrumenting.asciidoc
Jun 4, 2021
68fda00
Update docs/instrumenting.asciidoc
Jun 4, 2021
047444e
Update docs/supported-tech.asciidoc
Jun 4, 2021
3727097
Update gocontext.go
Jun 4, 2021
9889f5e
Update internal/apmcontext/context.go
Jun 4, 2021
632423a
Fix docs
Jun 4, 2021
53b4cf6
Improve fasthttp example
Jun 4, 2021
6a98efe
Merge branch 'master' of https://github.com/elastic/apm-agent-go
Jun 4, 2021
eb46be7
Update docs/supported-tech.asciidoc
Jun 4, 2021
df2c95d
Update module/apmfasthttp/utils.go
Jun 4, 2021
d24f800
Update module/apmfasthttp/types.go
Jun 4, 2021
a14887f
Update module/apmfasthttp/server.go
Jun 4, 2021
a7e37dd
Update module/apmfasthttp/recovery.go
Jun 4, 2021
4c70e6e
Update module/apmfasthttp/context.go
Jun 4, 2021
ff6d6d0
Update module/apmfasthttp/consts.go
Jun 4, 2021
00ed97a
Update module/apmfasthttp/closer.go
Jun 4, 2021
bcca4da
Add +build go1.12 tag
Jun 4, 2021
a233546
Update module/apmfasthttp/doc.go
Jun 4, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 87 additions & 62 deletions docs/instrumenting.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ For each server instrumentation module, a transaction is reported for each handl
request. The transaction will be stored in the request context, which can be obtained through
that framework's API. The request context can be used for reporting <<custom-instrumentation-spans, custom spans>>.

* <<builtin-modules-apmhttp>>
* <<builtin-modules-apmfasthttp>>
* <<builtin-modules-apmecho>>
* <<builtin-modules-apmgin>>
* <<builtin-modules-apmbeego>>
* <<builtin-modules-apmgorilla>>
* <<builtin-modules-apmgrpc>>
* <<builtin-modules-apmhttp>>
* <<builtin-modules-apmhttprouter>>
* <<builtin-modules-apmnegroni>>
* <<builtin-modules-apmlambda>>
Expand All @@ -30,6 +31,91 @@ that framework's API. The request context can be used for reporting <<custom-ins
* <<builtin-modules-apmmongo>>
* <<builtin-modules-apmawssdkgo>>

[[builtin-modules-apmhttp]]
==== module/apmhttp
Package apmhttp provides a low-level `net/http` middleware handler. Other web middleware should
typically be based off this.

For each request, a transaction is stored in the request context, which can be obtained via
https://golang.org/pkg/net/http/#Request.Context[http.Request.Context] in your handler.

[source,go]
----
import (
"go.elastic.co/apm/module/apmhttp"
)

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

The apmhttp handler will recover panics and send them to Elastic APM.

Package apmhttp also provides functions for instrumenting an `http.Client` or `http.RoundTripper`
such that outgoing requests are traced as spans, if the request context includes a transaction.
When performing the request, the enclosing context should be propagated by using
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.

Client spans are not ended until the response body is fully consumed or closed.
If you fail to do either, the span will not be sent.
Always close the response body to ensure HTTP connections can be reused; see https://golang.org/pkg/net/http/#Client.Do[`func (*Client) Do`].

[source,go]
----
import (
"net/http"

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

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

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

[[builtin-modules-apmfasthttp]]
==== module/apmfasthttp
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.

For each request, a transaction is stored in the request context, which can be obtained via
https://pkg.go.dev/github.com/valyala/fasthttp#RequestCtx[fasthttp.RequestCtx] in your handler using `apm.TransactionFromContext`.

[source,go]
----
import (
"go.elastic.co/apm/module/apmfasthttp"
)

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

The apmfasthttp handler will recover panics and send them to Elastic APM.

[[builtin-modules-apmecho]]
==== module/apmecho
Packages apmecho and apmechov4 provide middleware for the https://github.com/labstack/echo[Echo]
Expand Down Expand Up @@ -185,67 +271,6 @@ fails; when any of `grpc.ClientStream.RecvMsg`, `grpc.ClientStream.SendMsg`, or
`grpc.ClientStream.Header` return with an error; or when `grpc.ClientStream.RecvMsg`
returns for a non-streaming server method.

[[builtin-modules-apmhttp]]
==== module/apmhttp
Package apmhttp provides a low-level `net/http` middleware handler. Other web middleware should
typically be based off this.

For each request, a transaction is stored in the request context, which can be obtained via
https://golang.org/pkg/net/http/#Request.Context[http.Request.Context] in your handler.

[source,go]
----
import (
"go.elastic.co/apm/module/apmhttp"
)

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

The apmhttp handler will recover panics and send them to Elastic APM.

Package apmhttp also provides functions for instrumenting an `http.Client` or `http.RoundTripper`
such that outgoing requests are traced as spans, if the request context includes a transaction.
When performing the request, the enclosing context should be propagated by using
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.

Client spans are not ended until the response body is fully consumed or closed.
If you fail to do either, the span will not be sent.
Always close the response body to ensure HTTP connections can be reused; see https://golang.org/pkg/net/http/#Client.Do[`func (*Client) Do`].

[source,go]
----
import (
"net/http"

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

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

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

[[builtin-modules-apmhttprouter]]
==== module/apmhttprouter
Package apmhttprouter provides a low-level middleware handler for https://github.com/julienschmidt/httprouter[httprouter].
Expand Down
9 changes: 9 additions & 0 deletions docs/supported-tech.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ We support several third-party web frameworks, as well as Go's standard `net/htt
package. Regardless of the framework, we create a transaction for each incoming
request, and name the transaction after the registered route.

[float]
==== fasthttp

We support https://github.com/valyala/fasthttp[valyala/fasthttp],
https://github.com/valyala/fasthttp/releases/tag/v1.26.0[v1.26.0] and greater.

See <<builtin-modules-apmfasthttp, module/apmfasthttp>> for more information
about fasthttp instrumentation.

[float]
==== httprouter

Expand Down
13 changes: 7 additions & 6 deletions gocontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ import (
"go.elastic.co/apm/internal/apmcontext"
)

type bodyCapturerKey struct{}

// ContextWithSpan returns a copy of parent in which the given span
// is stored, associated with the key ContextSpanKey.
func ContextWithSpan(parent context.Context, s *Span) context.Context {
Expand All @@ -40,7 +38,7 @@ func ContextWithTransaction(parent context.Context, t *Transaction) context.Cont
// ContextWithBodyCapturer returns a copy of parent in which the given
// body capturer is stored, associated with the key bodyCapturerKey.
func ContextWithBodyCapturer(parent context.Context, bc *BodyCapturer) context.Context {
return context.WithValue(parent, bodyCapturerKey{}, bc)
return apmcontext.ContextWithBodyCapturer(parent, bc)
}

// SpanFromContext returns the current Span in context, if any. The span must
Expand All @@ -59,8 +57,11 @@ func TransactionFromContext(ctx context.Context) *Transaction {
return value
}

func bodyCapturerFromContext(ctx context.Context) *BodyCapturer {
value, _ := ctx.Value(bodyCapturerKey{}).(*BodyCapturer)
// BodyCapturerFromContext returns the BodyCapturer in context, if any.
// The body capturer must have been added to the context previously using
// ContextWithBodyCapturer.
func BodyCapturerFromContext(ctx context.Context) *BodyCapturer {
savsgio marked this conversation as resolved.
Show resolved Hide resolved
value, _ := apmcontext.BodyCapturerFromContext(ctx).(*BodyCapturer)
return value
}

Expand Down Expand Up @@ -143,7 +144,7 @@ func CaptureError(ctx context.Context, err error) *Error {
}
e := tx.tracer.NewError(err)
e.Handled = true
bc := bodyCapturerFromContext(ctx)
bc := BodyCapturerFromContext(ctx)
if bc != nil {
e.Context.SetHTTPRequest(bc.request)
e.Context.SetHTTPRequestBody(bc)
Expand Down
28 changes: 28 additions & 0 deletions internal/apmcontext/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ var (
// it at package init time.
ContextWithTransaction = DefaultContextWithTransaction

// ContextWithBodyCapturer takes a context and body capturer and returns
// a new context from which the body capturer can be extracted using
// BodyCapturerFromContext.
//
// ContextWithBodyCapturer is used by apm.ContextWithBodyCapturer.
// It is a variable to allow other packages, such as apmot, to replace
// it at package init time.
ContextWithBodyCapturer = DefaultContextWithBodyCapturer

// SpanFromContext returns a span included in the context using
// ContextWithSpan.
//
Expand All @@ -54,10 +63,19 @@ var (
// It is a variable to allow other packages, such as apmot, to replace
// it at package init time.
TransactionFromContext = DefaultTransactionFromContext

// BodyCapturerFromContext returns a body capturer included in the context
// using ContextWithBodyCapturer.
//
// BodyCapturerFromContext is used by apm.BodyCapturerFromContext.
// It is a variable to allow other packages, such as apmot, to replace
// it at package init time.
BodyCapturerFromContext = DefaultBodyCapturerFromContext
)

type spanKey struct{}
type transactionKey struct{}
type bodyCapturerKey struct{}

// DefaultContextWithSpan is the default value for ContextWithSpan.
func DefaultContextWithSpan(ctx context.Context, span interface{}) context.Context {
Expand All @@ -69,6 +87,11 @@ func DefaultContextWithTransaction(ctx context.Context, tx interface{}) context.
return context.WithValue(ctx, transactionKey{}, tx)
}

// DefaultContextWithBodyCapturer is the default value for ContextWithBodyCapturer.
func DefaultContextWithBodyCapturer(ctx context.Context, bc interface{}) context.Context {
return context.WithValue(ctx, bodyCapturerKey{}, bc)
}

// DefaultSpanFromContext is the default value for SpanFromContext.
func DefaultSpanFromContext(ctx context.Context) interface{} {
return ctx.Value(spanKey{})
Expand All @@ -78,3 +101,8 @@ func DefaultSpanFromContext(ctx context.Context) interface{} {
func DefaultTransactionFromContext(ctx context.Context) interface{} {
return ctx.Value(transactionKey{})
}

// DefaultBodyCapturerFromContext is the default value for BodyCapturerFromContext.
func DefaultBodyCapturerFromContext(ctx context.Context) interface{} {
return ctx.Value(bodyCapturerKey{})
}
47 changes: 47 additions & 0 deletions module/apmfasthttp/closer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// 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.

// +build go1.12

package apmfasthttp // import "go.elastic.co/apm/module/apmfasthttp"

import (
"github.com/valyala/fasthttp"

"go.elastic.co/apm"
)

// newTxCloser returns a transaction closer.
func newTxCloser(ctx *fasthttp.RequestCtx, tx *apm.Transaction, bc *apm.BodyCapturer) *txCloser {
return &txCloser{
ctx: ctx,
tx: tx,
bc: bc,
}
}

// Close sets the response context to the APM transaction and
// ends the transaction.
func (c *txCloser) Close() error {
if err := setResponseContext(c.ctx, c.tx, c.bc); err != nil {
return err
}

c.tx.End()

return nil
}
22 changes: 22 additions & 0 deletions module/apmfasthttp/consts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// 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.

// +build go1.12

package apmfasthttp // import "go.elastic.co/apm/module/apmfasthttp"

const txKey = "apmfasthttp_transaction"
Loading