Skip to content

Commit

Permalink
Go lambda instrumentation enhancements (#1413)
Browse files Browse the repository at this point in the history
* added failure scenario when getting container fails

* fix test case failure

* add changelog

* fix ecs resource detector bug

* fix struct name as per golint suggestion

* minor changes

* added NewResourceDetector func and interface assertions

* fix golint failure

* minor changes to address review comments

* lambda go instrumentation enhancements

* minor changes to address review comments

* update go.mod

* Revert "update go.mod"

This reverts commit 2696b35.

* remove depending on SDK package

* renaming the public API for xrayconfig package

* fix README review suggestions

* remove logger and minor changes

* Update instrumentation/github.com/aws/aws-lambda-go/otellambda/xrayconfig/xrayconfig_test.go

Co-authored-by: Anthony Mirabella <a9@aneurysm9.com>
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
  • Loading branch information
3 people committed Nov 12, 2021
1 parent b93bc21 commit ae5d9e7
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 58 deletions.
Expand Up @@ -34,7 +34,7 @@ func HandleRequest(ctx context.Context, name MyEvent) (string, error) {
}

func main() {
lambda.Start(otellambda.WrapHandlerFunction(HandleRequest))
lambda.Start(otellambda.InstrumentHandler(HandleRequest))
}
```

Expand All @@ -44,19 +44,19 @@ Now configure the instrumentation with the provided options to export traces to
// Add import
import "go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-lambda-go/otellambda/xrayconfig"

// add options to WrapHandlerFunction call
// add options to InstrumentHandler call
func main() {
lambda.Start(otellambda.WrapHandlerFunction(HandleRequest, xrayconfig.AllRecommendedOptions()...))
lambda.Start(otellambda.InstrumentHandler(HandleRequest, xrayconfig.WithRecommendedOptions()...))
}
```
## Recommended AWS Lambda Instrumentation Options

| Instrumentation Option | Recommended Value | Exported As |
| --- | --- | --- |
| `WithTracerProvider` | An `sdktrace.TracerProvider` configured to export in batches to an OTel Collector running locally in Lambda | Not individually exported. Can only be used via `AllRecommendedOptions()`
| `WithFlusher` | An `otellambda.Flusher` which yields before calling ForceFlush on the configured `sdktrace.TracerProvider`. Yielding mitigates data delays caused by asynchronous nature of batching TracerProvider when in Lambda | Not individually exported. Can only be used via `AllRecommendedOptions()`
| `WithEventToCarrier` | Function which reads X-Ray TraceID from Lambda environment and inserts it into a `propagtation.TextMapCarrier` | Individually exported as `EventToCarrier()`, also included in `AllRecommendedOptions()`
| `WithPropagator` | An `xray.propagator` | Individually exported as `EventToCarrier()`, also included in `AllRecommendedOptions()`
| `WithTracerProvider` | An `sdktrace.TracerProvider` configured to export in batches to an OTel Collector running locally in Lambda | Not individually exported. Can only be used via `WithRecommendedOptions()`
| `WithFlusher` | An `otellambda.Flusher` which yields before calling ForceFlush on the configured `sdktrace.TracerProvider`. Yielding mitigates data delays caused by asynchronous nature of batching TracerProvider when in Lambda | Not individually exported. Can only be used via `WithRecommendedOptions()`
| `WithEventToCarrier` | Function which reads X-Ray TraceID from Lambda environment and inserts it into a `propagtation.TextMapCarrier` | Individually exported as `WithEventToCarrier()`, also included in `WithRecommendedOptions()`
| `WithPropagator` | An `xray.propagator` | Individually exported as `WithPropagator()`, also included in `WithRecommendedOptions()`


## Useful links
Expand Down
Expand Up @@ -16,9 +16,7 @@ package xrayconfig

import (
"context"
"log"
"os"
"runtime"

lambdadetector "go.opentelemetry.io/contrib/detectors/aws/lambda"
"go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-lambda-go/otellambda"
Expand All @@ -28,79 +26,48 @@ import (
sdktrace "go.opentelemetry.io/otel/sdk/trace"
)

var errorLogger = log.New(log.Writer(), "OTel Lambda XRay Configuration Error: ", 0)

func xrayEventToCarrier([]byte) propagation.TextMapCarrier {
xrayTraceID := os.Getenv("_X_AMZN_TRACE_ID")
return propagation.HeaderCarrier{"X-Amzn-Trace-Id": []string{xrayTraceID}}
}

type asyncSafeFlusher struct {
tp *sdktrace.TracerProvider
}

func (f asyncSafeFlusher) ForceFlush(ctx context.Context) error {
// yield processor to attempt to ensure all spans have
// been consumed and are ready to be flushed
// - see https://github.com/open-telemetry/opentelemetry-go/issues/2080
// to be removed upon resolution of above issue
runtime.Gosched()

return f.tp.ForceFlush(ctx)
}

// tracerProviderAndFlusher returns a list of otellambda.Option(s) to
// enable using a TracerProvider configured for AWS XRay via a collector
// and an otellambda.Flusher to flush this TracerProvider.
// tracerProviderAndFlusher is not exported because it should not be used
// without the provided EventToCarrier function and XRay Propagator
func tracerProviderAndFlusher() ([]otellambda.Option, error) {
ctx := context.Background()

// Do not need transport security in Lambda because collector
// runs locally in Lambda execution environment
// NewTracerProvider returns a TracerProvider configured with an exporter,
// ID generator, and lambda resource detector to send trace data to AWS X-Ray
// via a Collector instance listening on localhost
func NewTracerProvider(ctx context.Context) (*sdktrace.TracerProvider, error) {
exp, err := otlptracegrpc.New(ctx, otlptracegrpc.WithInsecure())
if err != nil {
return []otellambda.Option{}, err
return nil, err
}

detector := lambdadetector.NewResourceDetector()
res, err := detector.Detect(ctx)
resource, err := detector.Detect(ctx)
if err != nil {
return []otellambda.Option{}, err
return nil, err
}

tp := sdktrace.NewTracerProvider(
return sdktrace.NewTracerProvider(
sdktrace.WithBatcher(exp),
sdktrace.WithIDGenerator(xray.NewIDGenerator()),
sdktrace.WithResource(res),
)

return []otellambda.Option{otellambda.WithTracerProvider(tp), otellambda.WithFlusher(asyncSafeFlusher{tp: tp})}, nil
sdktrace.WithResource(resource),
), nil
}

// EventToCarrier returns an otellambda.Option to enable
// WithEventToCarrier returns an otellambda.Option to enable
// an otellambda.EventToCarrier function which reads the XRay trace
// information from the environment and returns this information in
// a propagation.HeaderCarrier
func EventToCarrier() otellambda.Option {
func WithEventToCarrier() otellambda.Option {
return otellambda.WithEventToCarrier(xrayEventToCarrier)
}

// Propagator returns an otellambda.Option to enable the xray.Propagator
func Propagator() otellambda.Option {

// WithPropagator returns an otellambda.Option to enable the xray.Propagator
func WithPropagator() otellambda.Option {
return otellambda.WithPropagator(xray.Propagator{})
}

// AllRecommendedOptions returns a list of all otellambda.Option(s)
// WithRecommendedOptions returns a list of all otellambda.Option(s)
// recommended for the otellambda package when using AWS XRay
func AllRecommendedOptions() []otellambda.Option {
options, err := tracerProviderAndFlusher()
if err != nil {
// should we fail to create the TracerProvider, do not alter otellambda's default configuration
errorLogger.Println("failed to create recommended configuration: ", err)
return []otellambda.Option{}
}
return append(options, []otellambda.Option{EventToCarrier(), Propagator()}...)
func WithRecommendedOptions(tp *sdktrace.TracerProvider) []otellambda.Option {
return []otellambda.Option{WithEventToCarrier(), WithPropagator(), otellambda.WithTracerProvider(tp), otellambda.WithFlusher(tp)}
}
Expand Up @@ -154,6 +154,10 @@ func assertSpanEqualsIgnoreTimeAndSpanID(t *testing.T, expected *v1trace.Resourc
func TestWrapEndToEnd(t *testing.T) {
setEnvVars()

ctx := context.Background()
tp, err := NewTracerProvider(ctx)
assert.NoError(t, err)

customerHandler := func() (string, error) {
return "hello world", nil
}
Expand All @@ -163,7 +167,7 @@ func TestWrapEndToEnd(t *testing.T) {
}()
<-time.After(5 * time.Millisecond)

wrapped := otellambda.InstrumentHandler(customerHandler, AllRecommendedOptions()...)
wrapped := otellambda.InstrumentHandler(customerHandler, WithRecommendedOptions(tp)...)
wrappedCallable := reflect.ValueOf(wrapped)
resp := wrappedCallable.Call([]reflect.Value{reflect.ValueOf(mockContext)})
assert.Len(t, resp, 2)
Expand Down

0 comments on commit ae5d9e7

Please sign in to comment.