From efe01c75cefc8a398dfa64ddbb41dfcd632f5016 Mon Sep 17 00:00:00 2001 From: Stein Fletcher Date: Fri, 24 Jan 2020 13:58:10 +0000 Subject: [PATCH] Add cloudwatch handler --- cloudwatch_handler.go | 71 ++++++++++++++++++++++++++++++++++++++ cloudwatch_handler_test.go | 49 ++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 cloudwatch_handler.go create mode 100644 cloudwatch_handler_test.go diff --git a/cloudwatch_handler.go b/cloudwatch_handler.go new file mode 100644 index 0000000..29674f1 --- /dev/null +++ b/cloudwatch_handler.go @@ -0,0 +1,71 @@ +package g8 + +import ( + "context" + + "github.com/aws/aws-lambda-go/events" + "github.com/aws/aws-lambda-go/lambda" + "github.com/google/uuid" + newrelic "github.com/newrelic/go-agent" + "github.com/newrelic/go-agent/_integrations/nrlambda" + "github.com/rs/zerolog" +) + +type CloudWatchContext struct { + Context context.Context + Event events.CloudWatchEvent + Logger zerolog.Logger + NewRelicTx newrelic.Transaction + CorrelationID string +} + +type CloudWatchHandlerFunc func(c *CloudWatchContext) error + +func CloudWatchHandler(h CloudWatchHandlerFunc, conf HandlerConfig) func(context.Context, events.CloudWatchEvent) error { + return func(ctx context.Context, event events.CloudWatchEvent) error { + correlationID := uuid.New().String() + + // the resource that triggered the event, e.g. "arn:aws:events:us-east-1:123456789012:rule/MyScheduledRule" + var cloudWatchResource string + if len(event.Resources) > 0 { + cloudWatchResource = event.Resources[0] + } + + logger := configureLogger(conf). + Str("cloud_watch_resource", cloudWatchResource). + Str("correlation_id", correlationID). + Logger() + + c := &CloudWatchContext{ + Context: ctx, + Event: event, + Logger: logger, + NewRelicTx: newrelic.FromContext(ctx), + CorrelationID: correlationID, + } + + c.AddNewRelicAttribute("functionName", conf.FunctionName) + c.AddNewRelicAttribute("buildVersion", conf.BuildVersion) + c.AddNewRelicAttribute("correlationID", correlationID) + c.AddNewRelicAttribute("cloudWatchResource", cloudWatchResource) + + if err := h(c); err != nil { + logUnhandledError(c.Logger, err) + return err + } + return nil + } +} + +func CloudWatchHandlerWithNewRelic(h CloudWatchHandlerFunc, conf HandlerConfig) lambda.Handler { + return nrlambda.Wrap(CloudWatchHandler(h, conf), conf.NewRelicApp) +} + +func (c *CloudWatchContext) AddNewRelicAttribute(key string, val interface{}) { + if c.NewRelicTx == nil { + return + } + if err := c.NewRelicTx.AddAttribute(key, val); err != nil { + c.Logger.Error().Msgf("failed to add attr '%s' to new relic tx: %+v", key, err) + } +} diff --git a/cloudwatch_handler_test.go b/cloudwatch_handler_test.go new file mode 100644 index 0000000..1a13cc3 --- /dev/null +++ b/cloudwatch_handler_test.go @@ -0,0 +1,49 @@ +package g8_test + +import ( + "context" + "io/ioutil" + "testing" + + "github.com/aws/aws-lambda-go/events" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + + "github.com/JSainsburyPLC/g8" +) + +func TestCloudWatchHandler_SingleMessage(t *testing.T) { + timesCalled := 0 + resourceArn := "arn:aws:events:us-east-1:123456789012:rule/MyScheduledRule" + h := g8.CloudWatchHandler(func(c *g8.CloudWatchContext) error { + timesCalled++ + + assert.Equal(t, resourceArn, c.Event.Resources[0]) + assert.NotEmpty(t, c.CorrelationID) + + return nil + }, g8.HandlerConfig{Logger: zerolog.New(ioutil.Discard)}) + + err := h(context.Background(), events.CloudWatchEvent{ + Resources: []string{resourceArn}, + }) + + assert.Nil(t, err) + assert.Equal(t, 1, timesCalled) +} + +func TestCloudWatchHandler_HandlerError(t *testing.T) { + timesCalled := 0 + handlerFunc := func(c *g8.CloudWatchContext) error { + timesCalled++ + return assert.AnError + } + + h := g8.CloudWatchHandler(handlerFunc, g8.HandlerConfig{ + Logger: zerolog.New(ioutil.Discard), + }) + err := h(context.Background(), events.CloudWatchEvent{}) + + assert.Equal(t, assert.AnError, err) + assert.Equal(t, 1, timesCalled) +}