checkout service instrumented

This commit is contained in:
Paulo Monteiro 2020-02-28 11:19:48 -08:00
parent ccff406cdc
commit 574ee2a770
4 changed files with 200 additions and 41 deletions

View file

@ -51,10 +51,18 @@ spec:
value: "currencyservice:7000"
- name: CART_SERVICE_ADDR
value: "cartservice:7070"
- name: NEW_RELIC_APP_NAME
value: checkout-service
- name: NEW_RELIC_LICENSE_KEY
value: MY_LICENSE_KEY
- name: NEW_RELIC_DISTRIBUTED_TRACING_ENABLED
value: "true"
- name: NEW_RELIC_LABELS
value: clusterName:GKE
# - name: DISABLE_STATS
# value: "1"
# - name: DISABLE_TRACING
# value: "1"
- name: DISABLE_TRACING
value: "1"
# - name: DISABLE_PROFILER
# value: "1"
# - name: JAEGER_SERVICE_ADDR

View file

@ -25,11 +25,11 @@
[[projects]]
branch = "master"
digest = "1:a85b0dc359de4812d383ee6670f0dae595cfcb8b13ff6b872bdb013a18c37b07"
digest = "1:9d3a867627f4c2ed7530c68672ce7b5381fbd36419743dfa01ccff501a7995e5"
name = "git.apache.org/thrift.git"
packages = ["lib/go/thrift"]
pruneopts = "UT"
revision = "286eee16b147a302ddc7b10740c5e5401ebbec17"
revision = "aec555aac89ec9634e99ce0f4f0aec5799e6e3d8"
source = "github.com/apache/thrift"
[[projects]]
@ -40,11 +40,11 @@
"src/checkoutservice/money",
]
pruneopts = "UT"
revision = "27df445fc20f048c1c31e429f6c29075119fe454"
version = "v0.1.1"
revision = "d66cbdd27f73bb1ffa7479a3527f55dec070baa0"
version = "v0.1.4"
[[projects]]
digest = "1:1d3ad0f6a57c08e2168089a64c34313930571fcbe5359d71c608a97ce504f7ca"
digest = "1:56b3126ac15d478fa10928937c085b50be29cb29c32f069dcceeceac9423a327"
name = "github.com/golang/protobuf"
packages = [
"proto",
@ -58,16 +58,30 @@
"ptypes/wrappers",
]
pruneopts = "UT"
revision = "b5d812f8a3706043e23a9cd5babf2e5423744d30"
version = "v1.3.1"
revision = "5d5b4c10bd43f85e63bd9e4a3fa9b1ea2ef88af2"
version = "v1.3.4"
[[projects]]
digest = "1:0aeda02073125667ac6c9df50c7921cb22c08a4accdc54589c697a7e76be65c2"
name = "github.com/google/go-cmp"
packages = [
"cmp",
"cmp/internal/diff",
"cmp/internal/flags",
"cmp/internal/function",
"cmp/internal/value",
]
pruneopts = "UT"
revision = "5a6f75716e1203a923a78c9efb94089d857df0f6"
version = "v0.4.0"
[[projects]]
branch = "master"
digest = "1:dcb1edb161b1b1cac9aedf2a17b9b2c7829e242624968875a3c1b97dd9f4ef00"
digest = "1:1f1aa5c5a82ee4d757145920b88601a4c80aa0979a349a21c877a588cc0fe03c"
name = "github.com/google/pprof"
packages = ["profile"]
pruneopts = "UT"
revision = "54271f7e092ff31b10b7626fee166cbc6304e350"
revision = "4ac0da889f5869b4ce048f66afcd9cce809409e7"
[[projects]]
digest = "1:582b704bebaa06b48c29b0cec224a6058a09c86883aaddabde889cd1a5f73e1b"
@ -93,6 +107,25 @@
revision = "f55edac94c9bbba5d6182a4be46d86a2c9b5b50e"
version = "v1.0.2"
[[projects]]
digest = "1:dba348ae1fda55681897582516890a382c34d9da292790a4473fe0cdfbcfcff5"
name = "github.com/newrelic/go-agent"
packages = [
"v3/integrations/logcontext",
"v3/integrations/logcontext/nrlogrusplugin",
"v3/integrations/nrgrpc",
"v3/internal",
"v3/internal/cat",
"v3/internal/jsonx",
"v3/internal/logger",
"v3/internal/sysinfo",
"v3/internal/utilization",
"v3/newrelic",
]
pruneopts = "UT"
revision = "de0daeceb386cd36dffa4b0d288886f672402196"
version = "v3.3.0"
[[projects]]
digest = "1:04457f9f6f3ffc5fea48e71d62f2ca256637dee0a04d710288e27e05c8b41976"
name = "github.com/sirupsen/logrus"
@ -128,7 +161,7 @@
[[projects]]
branch = "master"
digest = "1:2f357867bf425774d35beca5be718402a4488b8b23b1563ce8c5bb91d09285a7"
digest = "1:bdb79e93c04bf9406eb285f6d3d85b884f71e73d68950d036be78c1b0c039689"
name = "golang.org/x/net"
packages = [
"context",
@ -141,11 +174,11 @@
"trace",
]
pruneopts = "UT"
revision = "da137c7871d730100384dbcf36e6f8fa493aef5b"
revision = "0de0cce0169b09b364e001f108dc0399ea8630b3"
[[projects]]
branch = "master"
digest = "1:31e33f76456ccf54819ab4a646cf01271d1a99d7712ab84bf1a9e7b61cd2031b"
digest = "1:79edde3241bb55de9f4143d5083bfcff722e550c3cb8db94084eab50d0e440b5"
name = "golang.org/x/oauth2"
packages = [
".",
@ -155,7 +188,7 @@
"jwt",
]
pruneopts = "UT"
revision = "0f29369cfe4552d0e4bcddc57cc75f4d7e672a33"
revision = "bf48bf16ab8d622ce64ec6ce98d2c98f916b6303"
[[projects]]
branch = "master"
@ -163,15 +196,15 @@
name = "golang.org/x/sync"
packages = ["semaphore"]
pruneopts = "UT"
revision = "112230192c580c3556b8cee6403af37a4fc5f28c"
revision = "cd5d95a43a6e21273425c7ae415d3df9ea832eeb"
[[projects]]
branch = "master"
digest = "1:730ba27cd66db3b98ec8f51a6f20d45ec277d490cca36b1f54e31d3fcaf4840e"
digest = "1:578046baf093df15df2904bbb0fe06f3f2385bad59987532201007754aa7e1d5"
name = "golang.org/x/sys"
packages = ["unix"]
pruneopts = "UT"
revision = "04f50cda93cbb67f2afa353c52f342100e80e625"
revision = "d5e6a3e2c0ae16fc7480523ebcb7fd4dd3215489"
[[projects]]
digest = "1:8d8faad6b12a3a4c819a3f9618cb6ee1fa1cfc33253abeeea8b55336721e3405"
@ -200,7 +233,7 @@
[[projects]]
branch = "master"
digest = "1:bc06b12d8436550fccc0212037e9281a7e4d53db25c2349eb3cc6c3457e0406b"
digest = "1:223d8f4aae8092b14cdce7d897c7c9bf0865307c392e10892f53bc1d98348eb3"
name = "google.golang.org/api"
packages = [
"googleapi/transport",
@ -209,15 +242,16 @@
"option",
"support/bundler",
"transport",
"transport/cert",
"transport/grpc",
"transport/http",
"transport/http/internal/propagation",
]
pruneopts = "UT"
revision = "aae1d1b89c27132abe4fa22731a2a61e7089079c"
revision = "2ab33c207318199ace1a47b541fe24c0b6e2b0b3"
[[projects]]
digest = "1:2c26b1c47556c0e5e73cdb05d8361c463737eee4baac35d38b40c728c3074a94"
digest = "1:c98e9b93e6d178378530b920fe6e1aa4b3dd4972872111e83827746aa1f33ded"
name = "google.golang.org/appengine"
packages = [
".",
@ -234,12 +268,12 @@
"urlfetch",
]
pruneopts = "UT"
revision = "b2f4a3cf3c67576a2ee09e1fe62656a5086ce880"
version = "v1.6.1"
revision = "971852bfffca25b069c31162ae8f247a3dba083b"
version = "v1.6.5"
[[projects]]
branch = "master"
digest = "1:cb0f37e3cdf50a27abbf33a48797b30786239d4fd69dbfbbc63cfa19d400d3ce"
digest = "1:bf950fbb11c183c2c5d3918402e89a76fa8d7e4d13873c3c91b04a942e7e0048"
name = "google.golang.org/genproto"
packages = [
"googleapis/api",
@ -253,16 +287,19 @@
"googleapis/monitoring/v3",
"googleapis/rpc/errdetails",
"googleapis/rpc/status",
"googleapis/type/calendarperiod",
"protobuf/field_mask",
]
pruneopts = "UT"
revision = "3bdd9d9f5532d75d09efb230bd767d265245cfe5"
revision = "46b91f19d98c8de158f0bac277af31327b3443d4"
[[projects]]
digest = "1:f379776e36e55e5b5cbf7187ea58280812785071de53046230006e47894650b6"
digest = "1:6595b4790062c71ba85f3a816172ce800beffe392bd351d511b29ea25a303b80"
name = "google.golang.org/grpc"
packages = [
".",
"attributes",
"backoff",
"balancer",
"balancer/base",
"balancer/grpclb",
@ -290,10 +327,13 @@
"internal/backoff",
"internal/balancerload",
"internal/binarylog",
"internal/buffer",
"internal/channelz",
"internal/envconfig",
"internal/grpcrand",
"internal/grpcsync",
"internal/resolver/dns",
"internal/resolver/passthrough",
"internal/syscall",
"internal/transport",
"keepalive",
@ -301,16 +341,14 @@
"naming",
"peer",
"resolver",
"resolver/dns",
"resolver/passthrough",
"serviceconfig",
"stats",
"status",
"tap",
]
pruneopts = "UT"
revision = "1d89a3c832915b2314551c1d2a506874d62e53f7"
version = "v1.22.0"
revision = "f495f5b15ae7ccda3b38c53a1bfcde4c1a58a2bc"
version = "v1.27.1"
[solve-meta]
analyzer-name = "dep"
@ -322,6 +360,9 @@
"github.com/GoogleCloudPlatform/microservices-demo/src/checkoutservice/money",
"github.com/golang/protobuf/proto",
"github.com/google/uuid",
"github.com/newrelic/go-agent/v3/integrations/logcontext/nrlogrusplugin",
"github.com/newrelic/go-agent/v3/integrations/nrgrpc",
"github.com/newrelic/go-agent/v3/newrelic",
"github.com/sirupsen/logrus",
"go.opencensus.io/exporter/jaeger",
"go.opencensus.io/plugin/ocgrpc",

View file

@ -53,6 +53,10 @@
branch = "master"
name = "golang.org/x/net"
[[constraint]]
name = "github.com/newrelic/go-agent"
version = "3.3.0"
[prune]
go-tests = true
unused-packages = true
unused-packages = true

View file

@ -15,6 +15,10 @@
package main
import (
"github.com/sirupsen/logrus"
"github.com/newrelic/go-agent/v3/integrations/logcontext/nrlogrusplugin"
"github.com/newrelic/go-agent/v3/newrelic"
"context"
"fmt"
"net"
@ -24,7 +28,6 @@ import (
"cloud.google.com/go/profiler"
"contrib.go.opencensus.io/exporter/stackdriver"
"github.com/google/uuid"
"github.com/sirupsen/logrus"
"go.opencensus.io/exporter/jaeger"
"go.opencensus.io/plugin/ocgrpc"
"go.opencensus.io/stats/view"
@ -32,6 +35,7 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/newrelic/go-agent/v3/integrations/nrgrpc"
pb "github.com/GoogleCloudPlatform/microservices-demo/src/checkoutservice/genproto"
money "github.com/GoogleCloudPlatform/microservices-demo/src/checkoutservice/money"
@ -43,19 +47,30 @@ const (
usdCurrency = "USD"
)
var app *newrelic.Application
var log *logrus.Logger
func init() {
app, _ = newrelic.NewApplication(
func(config *newrelic.Config) {
// add more specific configuration of the agent within a custom ConfigOption
config.CrossApplicationTracer.Enabled = false
config.DistributedTracer.Enabled = true
},
newrelic.ConfigAppName(os.Getenv("NEW_RELIC_APP_NAME")),
newrelic.ConfigLicense(os.Getenv("NEW_RELIC_LICENSE_KEY")))
log = logrus.New()
log.Level = logrus.DebugLevel
log.Formatter = &logrus.JSONFormatter{
FieldMap: logrus.FieldMap{
logrus.FieldKeyTime: "timestamp",
logrus.FieldKeyLevel: "severity",
logrus.FieldKeyMsg: "message",
},
TimestampFormat: time.RFC3339Nano,
}
// log.Formatter = &logrus.JSONFormatter{
// FieldMap: logrus.FieldMap{
// logrus.FieldKeyTime: "timestamp",
// logrus.FieldKeyLevel: "severity",
// logrus.FieldKeyMsg: "message",
// },
// TimestampFormat: time.RFC3339Nano,
// }
log.SetFormatter(nrlogrusplugin.ContextFormatter{})
log.Out = os.Stdout
}
@ -106,7 +121,11 @@ func main() {
var srv *grpc.Server
if os.Getenv("DISABLE_STATS") == "" {
log.Info("Stats enabled.")
srv = grpc.NewServer(grpc.StatsHandler(&ocgrpc.ServerHandler{}))
srv = grpc.NewServer(
// Add the New Relic gRPC server instrumentation
grpc.UnaryInterceptor(nrgrpc.UnaryServerInterceptor(app)),
grpc.StreamInterceptor(nrgrpc.StreamServerInterceptor(app)),
grpc.StatsHandler(&ocgrpc.ServerHandler{}))
} else {
log.Info("Stats disabled.")
srv = grpc.NewServer()
@ -208,6 +227,7 @@ func mustMapEnv(target *string, envKey string) {
}
func (cs *checkoutService) Check(ctx context.Context, req *healthpb.HealthCheckRequest) (*healthpb.HealthCheckResponse, error) {
defer newrelic.StartSegment(newrelic.FromContext(ctx), "Check").End()
return &healthpb.HealthCheckResponse{Status: healthpb.HealthCheckResponse_SERVING}, nil
}
@ -216,17 +236,46 @@ func (cs *checkoutService) Watch(req *healthpb.HealthCheckRequest, ws healthpb.H
}
func (cs *checkoutService) PlaceOrder(ctx context.Context, req *pb.PlaceOrderRequest) (*pb.PlaceOrderResponse, error) {
txn := newrelic.FromContext(ctx)
defer txn.StartSegment("PlaceOrder").End()
log.Infof("[PlaceOrder] user_id=%q user_currency=%q", req.UserId, req.UserCurrency)
txn.AddAttribute("email", req.Email)
orderID, err := uuid.NewUUID()
if err != nil {
txn.Application().RecordCustomEvent(
"BuyingFlow", map[string]interface{}{
"email": req.Email,
"status": false,
"step": "Create Order ID"})
txn.AddAttribute("status", "failed to generate order uuid")
return nil, status.Errorf(codes.Internal, "failed to generate order uuid")
}
txn.Application().RecordCustomEvent(
"BuyingFlow", map[string]interface{}{
"email": req.Email,
"orderId": orderID.String(),
"status": true,
"step": "Create Order ID"})
prep, err := cs.prepareOrderItemsAndShippingQuoteFromCart(ctx, req.UserId, req.UserCurrency, req.Address)
if err != nil {
txn.Application().RecordCustomEvent(
"BuyingFlow", map[string]interface{}{
"email": req.Email,
"orderId": orderID.String(),
"status": false,
"step": "Prepare Order Items"})
txn.AddAttribute("status", "failed to prepare order")
return nil, status.Errorf(codes.Internal, err.Error())
}
txn.Application().RecordCustomEvent(
"BuyingFlow", map[string]interface{}{
"email": req.Email,
"orderId": orderID.String(),
"status": true,
"step": "Prepare Order Items"})
total := pb.Money{CurrencyCode: req.UserCurrency,
Units: 0,
@ -238,14 +287,46 @@ func (cs *checkoutService) PlaceOrder(ctx context.Context, req *pb.PlaceOrderReq
txID, err := cs.chargeCard(ctx, &total, req.CreditCard)
if err != nil {
txn.Application().RecordCustomEvent(
"BuyingFlow", map[string]interface{}{
"email": req.Email,
"orderId": orderID.String(),
"status": false,
"step": "Charge Credit Card"})
txn.AddAttribute("status", "failed to charge card")
return nil, status.Errorf(codes.Internal, "failed to charge card: %+v", err)
}
txn.Application().RecordCustomEvent(
"BuyingFlow", map[string]interface{}{
"email": req.Email,
"orderId": orderID.String(),
"paymentId": txID,
"status": true,
"step": "Charge Credit Card"})
txn.AddAttribute("status", "payment went through")
txn.AddAttribute("paymentId", txID)
log.Infof("payment went through (transaction_id: %s)", txID)
shippingTrackingID, err := cs.shipOrder(ctx, req.Address, prep.cartItems)
if err != nil {
txn.Application().RecordCustomEvent(
"BuyingFlow", map[string]interface{}{
"email": req.Email,
"orderId": orderID.String(),
"paymentId": txID,
"status": false,
"step": "Process Shipment"})
txn.AddAttribute("status", "shipping error")
return nil, status.Errorf(codes.Unavailable, "shipping error: %+v", err)
}
txn.Application().RecordCustomEvent(
"BuyingFlow", map[string]interface{}{
"email": req.Email,
"orderId": orderID.String(),
"paymentId": txID,
"status": true,
"step": "Process Shipment"})
_ = cs.emptyUserCart(ctx, req.UserId)
@ -258,8 +339,24 @@ func (cs *checkoutService) PlaceOrder(ctx context.Context, req *pb.PlaceOrderReq
}
if err := cs.sendOrderConfirmation(ctx, req.Email, orderResult); err != nil {
txn.Application().RecordCustomEvent(
"BuyingFlow", map[string]interface{}{
"email": req.Email,
"orderId": orderID.String(),
"paymentId": txID,
"status": false,
"step": "Send Confirmation"})
txn.AddAttribute("feedback", "failed to send order confirmation")
log.Warnf("failed to send order confirmation to %q: %+v", req.Email, err)
} else {
txn.Application().RecordCustomEvent(
"BuyingFlow", map[string]interface{}{
"email": req.Email,
"orderId": orderID.String(),
"paymentId": txID,
"status": true,
"step": "Send Confirmation"})
txn.AddAttribute("feedback", "order confirmation email sent")
log.Infof("order confirmation email sent to %q", req.Email)
}
resp := &pb.PlaceOrderResponse{Order: orderResult}
@ -273,6 +370,7 @@ type orderPrep struct {
}
func (cs *checkoutService) prepareOrderItemsAndShippingQuoteFromCart(ctx context.Context, userID, userCurrency string, address *pb.Address) (orderPrep, error) {
defer newrelic.StartSegment(newrelic.FromContext(ctx), "PrepareOrderItems").End()
var out orderPrep
cartItems, err := cs.getUserCart(ctx, userID)
if err != nil {
@ -298,6 +396,7 @@ func (cs *checkoutService) prepareOrderItemsAndShippingQuoteFromCart(ctx context
}
func (cs *checkoutService) quoteShipping(ctx context.Context, address *pb.Address, items []*pb.CartItem) (*pb.Money, error) {
defer newrelic.StartSegment(newrelic.FromContext(ctx), "QuoteShipping").End()
conn, err := grpc.DialContext(ctx, cs.shippingSvcAddr,
grpc.WithInsecure(),
grpc.WithStatsHandler(&ocgrpc.ClientHandler{}))
@ -317,6 +416,7 @@ func (cs *checkoutService) quoteShipping(ctx context.Context, address *pb.Addres
}
func (cs *checkoutService) getUserCart(ctx context.Context, userID string) ([]*pb.CartItem, error) {
defer newrelic.StartSegment(newrelic.FromContext(ctx), "GetUserCart").End()
conn, err := grpc.DialContext(ctx, cs.cartSvcAddr, grpc.WithInsecure(), grpc.WithStatsHandler(&ocgrpc.ClientHandler{}))
if err != nil {
return nil, fmt.Errorf("could not connect cart service: %+v", err)
@ -331,6 +431,7 @@ func (cs *checkoutService) getUserCart(ctx context.Context, userID string) ([]*p
}
func (cs *checkoutService) emptyUserCart(ctx context.Context, userID string) error {
defer newrelic.StartSegment(newrelic.FromContext(ctx), "EmptyUserCart").End()
conn, err := grpc.DialContext(ctx, cs.cartSvcAddr, grpc.WithInsecure(), grpc.WithStatsHandler(&ocgrpc.ClientHandler{}))
if err != nil {
return fmt.Errorf("could not connect cart service: %+v", err)
@ -344,6 +445,7 @@ func (cs *checkoutService) emptyUserCart(ctx context.Context, userID string) err
}
func (cs *checkoutService) prepOrderItems(ctx context.Context, items []*pb.CartItem, userCurrency string) ([]*pb.OrderItem, error) {
defer newrelic.StartSegment(newrelic.FromContext(ctx), "PrepareOrderItems").End()
out := make([]*pb.OrderItem, len(items))
conn, err := grpc.DialContext(ctx, cs.productCatalogSvcAddr, grpc.WithInsecure(), grpc.WithStatsHandler(&ocgrpc.ClientHandler{}))
@ -370,6 +472,7 @@ func (cs *checkoutService) prepOrderItems(ctx context.Context, items []*pb.CartI
}
func (cs *checkoutService) convertCurrency(ctx context.Context, from *pb.Money, toCurrency string) (*pb.Money, error) {
defer newrelic.StartSegment(newrelic.FromContext(ctx), "ConvertCurrency").End()
conn, err := grpc.DialContext(ctx, cs.currencySvcAddr, grpc.WithInsecure(), grpc.WithStatsHandler(&ocgrpc.ClientHandler{}))
if err != nil {
return nil, fmt.Errorf("could not connect currency service: %+v", err)
@ -385,6 +488,7 @@ func (cs *checkoutService) convertCurrency(ctx context.Context, from *pb.Money,
}
func (cs *checkoutService) chargeCard(ctx context.Context, amount *pb.Money, paymentInfo *pb.CreditCardInfo) (string, error) {
defer newrelic.StartSegment(newrelic.FromContext(ctx), "ChargeCard").End()
conn, err := grpc.DialContext(ctx, cs.paymentSvcAddr, grpc.WithInsecure(), grpc.WithStatsHandler(&ocgrpc.ClientHandler{}))
if err != nil {
return "", fmt.Errorf("failed to connect payment service: %+v", err)
@ -401,6 +505,7 @@ func (cs *checkoutService) chargeCard(ctx context.Context, amount *pb.Money, pay
}
func (cs *checkoutService) sendOrderConfirmation(ctx context.Context, email string, order *pb.OrderResult) error {
defer newrelic.StartSegment(newrelic.FromContext(ctx), "SendOrderConfirmation").End()
conn, err := grpc.DialContext(ctx, cs.emailSvcAddr, grpc.WithInsecure(), grpc.WithStatsHandler(&ocgrpc.ClientHandler{}))
if err != nil {
return fmt.Errorf("failed to connect email service: %+v", err)
@ -413,6 +518,7 @@ func (cs *checkoutService) sendOrderConfirmation(ctx context.Context, email stri
}
func (cs *checkoutService) shipOrder(ctx context.Context, address *pb.Address, items []*pb.CartItem) (string, error) {
defer newrelic.StartSegment(newrelic.FromContext(ctx), "ShipOrder").End()
conn, err := grpc.DialContext(ctx, cs.shippingSvcAddr, grpc.WithInsecure(), grpc.WithStatsHandler(&ocgrpc.ClientHandler{}))
if err != nil {
return "", fmt.Errorf("failed to connect email service: %+v", err)