platform/lambda/metrics.go

Summary

Maintainability
A
0 mins
Test Coverage
package lambda

import (
    "time"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/cloudwatch"
    "github.com/golang/sync/errgroup"

    "github.com/apex/up/internal/metrics"
    "github.com/apex/up/platform/event"
)

// TODO: write a higher level pkg in tj/aws
// TODO: move the metrics pkg to tj/aws

type stat struct {
    Namespace string
    Name      string
    Metric    string
    Stat      string
    point     *cloudwatch.Datapoint
}

// Value returns the metric value.
func (s *stat) Value() int {
    if s.point == nil {
        return 0
    }

    switch s.Stat {
    case "Sum":
        return int(*s.point.Sum)
    case "Average":
        return int(*s.point.Average)
    case "Minimum":
        return int(*s.point.Minimum)
    case "Maximum":
        return int(*s.point.Maximum)
    default:
        return 0
    }
}

// stats to fetch.
var stats = []*stat{
    {"AWS/ApiGateway", "Requests", "Count", "Sum", nil},
    {"AWS/ApiGateway", "Duration min", "Latency", "Minimum", nil},
    {"AWS/ApiGateway", "Duration avg", "Latency", "Average", nil},
    {"AWS/ApiGateway", "Duration max", "Latency", "Maximum", nil},
    {"AWS/Lambda", "Duration sum", "Duration", "Sum", nil},
    {"AWS/ApiGateway", "Errors 4xx", "4XXError", "Sum", nil},
    {"AWS/ApiGateway", "Errors 5xx", "5XXError", "Sum", nil},
    {"AWS/Lambda", "Invocations", "Invocations", "Sum", nil},
    {"AWS/Lambda", "Errors", "Errors", "Sum", nil},
    {"AWS/Lambda", "Throttles", "Throttles", "Sum", nil},
}

// ShowMetrics implementation.
func (p *Platform) ShowMetrics(region, stage string, start time.Time) error {
    s := session.New(aws.NewConfig().WithRegion(region))
    c := cloudwatch.New(s)
    var g errgroup.Group
    name := p.config.Name

    d := time.Now().UTC().Sub(start)

    for _, s := range stats {
        s := s
        g.Go(func() error {
            m := metrics.New().
                Namespace(s.Namespace).
                TimeRange(time.Now().Add(-d), time.Now()).
                Period(int(d.Seconds() * 2)).
                Stat(s.Stat).
                Metric(s.Metric)

            switch s.Namespace {
            case "AWS/ApiGateway":
                m = m.Dimension("ApiName", name).Dimension("Stage", stage)
            case "AWS/Lambda":
                m = m.Dimension("FunctionName", name).Dimension("Resource", name+":"+stage)
            }

            res, err := c.GetMetricStatistics(m.Params())
            if err != nil {
                return err
            }

            if len(res.Datapoints) > 0 {
                s.point = res.Datapoints[0]
            }

            return nil
        })
    }

    if err := g.Wait(); err != nil {
        return err
    }

    for _, s := range stats {
        p.events.Emit("metrics.value", event.Fields{
            "name":   s.Name,
            "value":  s.Value(),
            "memory": p.config.Lambda.Memory,
        })
    }

    return nil
}