Add trace_id and span_id fields to logs in go services
This commit adds `trace_id` and `span_id` json fields to log output in go services. It's the first step of introducing traces/logs correlation.
This commit is contained in:
parent
db03c38ffe
commit
7d0b45d6c8
4 changed files with 159 additions and 96 deletions
|
@ -16,6 +16,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
@ -43,12 +44,12 @@ const (
|
||||||
usdCurrency = "USD"
|
usdCurrency = "USD"
|
||||||
)
|
)
|
||||||
|
|
||||||
var log *logrus.Logger
|
var logger *logrus.Logger
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
log = logrus.New()
|
logger = logrus.New()
|
||||||
log.Level = logrus.DebugLevel
|
logger.Level = logrus.DebugLevel
|
||||||
log.Formatter = &logrus.JSONFormatter{
|
logger.Formatter = &logrus.JSONFormatter{
|
||||||
FieldMap: logrus.FieldMap{
|
FieldMap: logrus.FieldMap{
|
||||||
logrus.FieldKeyTime: "timestamp",
|
logrus.FieldKeyTime: "timestamp",
|
||||||
logrus.FieldKeyLevel: "severity",
|
logrus.FieldKeyLevel: "severity",
|
||||||
|
@ -56,7 +57,7 @@ func init() {
|
||||||
},
|
},
|
||||||
TimestampFormat: time.RFC3339Nano,
|
TimestampFormat: time.RFC3339Nano,
|
||||||
}
|
}
|
||||||
log.Out = os.Stdout
|
logger.Out = os.Stdout
|
||||||
}
|
}
|
||||||
|
|
||||||
type checkoutService struct {
|
type checkoutService struct {
|
||||||
|
@ -70,17 +71,17 @@ type checkoutService struct {
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if os.Getenv("DISABLE_TRACING") == "" {
|
if os.Getenv("DISABLE_TRACING") == "" {
|
||||||
log.Info("Tracing enabled.")
|
logger.Info("Tracing enabled.")
|
||||||
go initTracing()
|
go initTracing()
|
||||||
} else {
|
} else {
|
||||||
log.Info("Tracing disabled.")
|
logger.Info("Tracing disabled.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.Getenv("DISABLE_PROFILER") == "" {
|
if os.Getenv("DISABLE_PROFILER") == "" {
|
||||||
log.Info("Profiling enabled.")
|
logger.Info("Profiling enabled.")
|
||||||
go initProfiling("checkoutservice", "1.0.0")
|
go initProfiling("checkoutservice", "1.0.0")
|
||||||
} else {
|
} else {
|
||||||
log.Info("Profiling disabled.")
|
logger.Info("Profiling disabled.")
|
||||||
}
|
}
|
||||||
|
|
||||||
port := listenPort
|
port := listenPort
|
||||||
|
@ -96,32 +97,32 @@ func main() {
|
||||||
mustMapEnv(&svc.emailSvcAddr, "EMAIL_SERVICE_ADDR")
|
mustMapEnv(&svc.emailSvcAddr, "EMAIL_SERVICE_ADDR")
|
||||||
mustMapEnv(&svc.paymentSvcAddr, "PAYMENT_SERVICE_ADDR")
|
mustMapEnv(&svc.paymentSvcAddr, "PAYMENT_SERVICE_ADDR")
|
||||||
|
|
||||||
log.Infof("service config: %+v", svc)
|
logger.Infof("service config: %+v", svc)
|
||||||
|
|
||||||
lis, err := net.Listen("tcp", fmt.Sprintf(":%s", port))
|
lis, err := net.Listen("tcp", fmt.Sprintf(":%s", port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
logger.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var srv *grpc.Server
|
var srv *grpc.Server
|
||||||
if os.Getenv("DISABLE_STATS") == "" {
|
if os.Getenv("DISABLE_STATS") == "" {
|
||||||
log.Info("Stats enabled.")
|
logger.Info("Stats enabled.")
|
||||||
srv = grpc.NewServer(grpc.StatsHandler(&ocgrpc.ServerHandler{}))
|
srv = grpc.NewServer(grpc.StatsHandler(&ocgrpc.ServerHandler{}))
|
||||||
} else {
|
} else {
|
||||||
log.Info("Stats disabled.")
|
logger.Info("Stats disabled.")
|
||||||
srv = grpc.NewServer()
|
srv = grpc.NewServer()
|
||||||
}
|
}
|
||||||
pb.RegisterCheckoutServiceServer(srv, svc)
|
pb.RegisterCheckoutServiceServer(srv, svc)
|
||||||
healthpb.RegisterHealthServer(srv, svc)
|
healthpb.RegisterHealthServer(srv, svc)
|
||||||
log.Infof("starting to listen on tcp: %q", lis.Addr().String())
|
logger.Infof("starting to listen on tcp: %q", lis.Addr().String())
|
||||||
err = srv.Serve(lis)
|
err = srv.Serve(lis)
|
||||||
log.Fatal(err)
|
logger.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initJaegerTracing() {
|
func initJaegerTracing() {
|
||||||
svcAddr := os.Getenv("JAEGER_SERVICE_ADDR")
|
svcAddr := os.Getenv("JAEGER_SERVICE_ADDR")
|
||||||
if svcAddr == "" {
|
if svcAddr == "" {
|
||||||
log.Info("jaeger initialization disabled.")
|
logger.Info("jaeger initialization disabled.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,19 +135,19 @@ func initJaegerTracing() {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
logger.Fatal(err)
|
||||||
}
|
}
|
||||||
trace.RegisterExporter(exporter)
|
trace.RegisterExporter(exporter)
|
||||||
log.Info("jaeger initialization completed.")
|
logger.Info("jaeger initialization completed.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func initStats(exporter *stackdriver.Exporter) {
|
func initStats(exporter *stackdriver.Exporter) {
|
||||||
view.SetReportingPeriod(60 * time.Second)
|
view.SetReportingPeriod(60 * time.Second)
|
||||||
view.RegisterExporter(exporter)
|
view.RegisterExporter(exporter)
|
||||||
if err := view.Register(ocgrpc.DefaultServerViews...); err != nil {
|
if err := view.Register(ocgrpc.DefaultServerViews...); err != nil {
|
||||||
log.Warn("Error registering default server views")
|
logger.Warn("Error registering default server views")
|
||||||
} else {
|
} else {
|
||||||
log.Info("Registered default server views")
|
logger.Info("Registered default server views")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,20 +157,20 @@ func initStackdriverTracing() {
|
||||||
for i := 1; i <= 3; i++ {
|
for i := 1; i <= 3; i++ {
|
||||||
exporter, err := stackdriver.NewExporter(stackdriver.Options{})
|
exporter, err := stackdriver.NewExporter(stackdriver.Options{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Infof("failed to initialize stackdriver exporter: %+v", err)
|
logger.Infof("failed to initialize stackdriver exporter: %+v", err)
|
||||||
} else {
|
} else {
|
||||||
trace.RegisterExporter(exporter)
|
trace.RegisterExporter(exporter)
|
||||||
log.Info("registered Stackdriver tracing")
|
logger.Info("registered Stackdriver tracing")
|
||||||
|
|
||||||
// Register the views to collect server stats.
|
// Register the views to collect server stats.
|
||||||
initStats(exporter)
|
initStats(exporter)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
d := time.Second * 10 * time.Duration(i)
|
d := time.Second * 10 * time.Duration(i)
|
||||||
log.Infof("sleeping %v to retry initializing Stackdriver exporter", d)
|
logger.Infof("sleeping %v to retry initializing Stackdriver exporter", d)
|
||||||
time.Sleep(d)
|
time.Sleep(d)
|
||||||
}
|
}
|
||||||
log.Warn("could not initialize Stackdriver exporter after retrying, giving up")
|
logger.Warn("could not initialize Stackdriver exporter after retrying, giving up")
|
||||||
}
|
}
|
||||||
|
|
||||||
func initTracing() {
|
func initTracing() {
|
||||||
|
@ -187,16 +188,16 @@ func initProfiling(service, version string) {
|
||||||
// ProjectID must be set if not running on GCP.
|
// ProjectID must be set if not running on GCP.
|
||||||
// ProjectID: "my-project",
|
// ProjectID: "my-project",
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
log.Warnf("failed to start profiler: %+v", err)
|
logger.Warnf("failed to start profiler: %+v", err)
|
||||||
} else {
|
} else {
|
||||||
log.Info("started Stackdriver profiler")
|
logger.Info("started Stackdriver profiler")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
d := time.Second * 10 * time.Duration(i)
|
d := time.Second * 10 * time.Duration(i)
|
||||||
log.Infof("sleeping %v to retry initializing Stackdriver profiler", d)
|
logger.Infof("sleeping %v to retry initializing Stackdriver profiler", d)
|
||||||
time.Sleep(d)
|
time.Sleep(d)
|
||||||
}
|
}
|
||||||
log.Warn("could not initialize Stackdriver profiler after retrying, giving up")
|
logger.Warn("could not initialize Stackdriver profiler after retrying, giving up")
|
||||||
}
|
}
|
||||||
|
|
||||||
func mustMapEnv(target *string, envKey string) {
|
func mustMapEnv(target *string, envKey string) {
|
||||||
|
@ -216,6 +217,7 @@ func (cs *checkoutService) Watch(req *healthpb.HealthCheckRequest, ws healthpb.H
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *checkoutService) PlaceOrder(ctx context.Context, req *pb.PlaceOrderRequest) (*pb.PlaceOrderResponse, error) {
|
func (cs *checkoutService) PlaceOrder(ctx context.Context, req *pb.PlaceOrderRequest) (*pb.PlaceOrderResponse, error) {
|
||||||
|
log := logger.WithFields(getTraceLogFields(ctx))
|
||||||
log.Infof("[PlaceOrder] user_id=%q user_currency=%q", req.UserId, req.UserCurrency)
|
log.Infof("[PlaceOrder] user_id=%q user_currency=%q", req.UserId, req.UserCurrency)
|
||||||
|
|
||||||
orderID, err := uuid.NewUUID()
|
orderID, err := uuid.NewUUID()
|
||||||
|
@ -428,3 +430,16 @@ func (cs *checkoutService) shipOrder(ctx context.Context, address *pb.Address, i
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Dial and create client once, reuse.
|
// TODO: Dial and create client once, reuse.
|
||||||
|
|
||||||
|
func getTraceLogFields(ctx context.Context) logrus.Fields {
|
||||||
|
span := trace.FromContext(ctx)
|
||||||
|
if span == nil {
|
||||||
|
return logrus.Fields{}
|
||||||
|
}
|
||||||
|
traceID := span.SpanContext().TraceID
|
||||||
|
spanID := span.SpanContext().SpanID
|
||||||
|
return logrus.Fields{
|
||||||
|
"trace_id": hex.EncodeToString(traceID[:]),
|
||||||
|
"span_id": hex.EncodeToString(spanID[:]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -16,11 +16,13 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"go.opencensus.io/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ctxKeyLog struct{}
|
type ctxKeyLog struct{}
|
||||||
|
@ -68,6 +70,7 @@ func (lh *logHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
if v, ok := r.Context().Value(ctxKeySessionID{}).(string); ok {
|
if v, ok := r.Context().Value(ctxKeySessionID{}).(string); ok {
|
||||||
log = log.WithField("session", v)
|
log = log.WithField("session", v)
|
||||||
}
|
}
|
||||||
|
log = withTraceContext(log, r)
|
||||||
log.Debug("request started")
|
log.Debug("request started")
|
||||||
defer func() {
|
defer func() {
|
||||||
log.WithFields(logrus.Fields{
|
log.WithFields(logrus.Fields{
|
||||||
|
@ -81,6 +84,20 @@ func (lh *logHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
lh.next.ServeHTTP(rr, r)
|
lh.next.ServeHTTP(rr, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// withTraceContext add trace fields to log messages
|
||||||
|
func withTraceContext(log *logrus.Entry, r *http.Request) *logrus.Entry {
|
||||||
|
span := trace.FromContext(r.Context())
|
||||||
|
if span == nil {
|
||||||
|
return log
|
||||||
|
}
|
||||||
|
traceID := span.SpanContext().TraceID
|
||||||
|
spanID := span.SpanContext().SpanID
|
||||||
|
return log.WithFields(logrus.Fields{
|
||||||
|
"trace_id": hex.EncodeToString(traceID[:]),
|
||||||
|
"span_id": hex.EncodeToString(spanID[:]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func ensureSessionID(next http.Handler) http.HandlerFunc {
|
func ensureSessionID(next http.Handler) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
var sessionID string
|
var sessionID string
|
||||||
|
|
|
@ -17,6 +17,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/hex"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -48,7 +49,7 @@ import (
|
||||||
var (
|
var (
|
||||||
cat pb.ListProductsResponse
|
cat pb.ListProductsResponse
|
||||||
catalogMutex *sync.Mutex
|
catalogMutex *sync.Mutex
|
||||||
log *logrus.Logger
|
logger *logrus.Logger
|
||||||
extraLatency time.Duration
|
extraLatency time.Duration
|
||||||
|
|
||||||
port = "3550"
|
port = "3550"
|
||||||
|
@ -57,8 +58,8 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
log = logrus.New()
|
logger = logrus.New()
|
||||||
log.Formatter = &logrus.JSONFormatter{
|
logger.Formatter = &logrus.JSONFormatter{
|
||||||
FieldMap: logrus.FieldMap{
|
FieldMap: logrus.FieldMap{
|
||||||
logrus.FieldKeyTime: "timestamp",
|
logrus.FieldKeyTime: "timestamp",
|
||||||
logrus.FieldKeyLevel: "severity",
|
logrus.FieldKeyLevel: "severity",
|
||||||
|
@ -66,27 +67,27 @@ func init() {
|
||||||
},
|
},
|
||||||
TimestampFormat: time.RFC3339Nano,
|
TimestampFormat: time.RFC3339Nano,
|
||||||
}
|
}
|
||||||
log.Out = os.Stdout
|
logger.Out = os.Stdout
|
||||||
catalogMutex = &sync.Mutex{}
|
catalogMutex = &sync.Mutex{}
|
||||||
err := readCatalogFile(&cat)
|
err := readCatalogFile(context.Background(), &cat)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("could not parse product catalog")
|
logger.Warnf("could not parse product catalog")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if os.Getenv("DISABLE_TRACING") == "" {
|
if os.Getenv("DISABLE_TRACING") == "" {
|
||||||
log.Info("Tracing enabled.")
|
logger.Info("Tracing enabled.")
|
||||||
go initTracing()
|
go initTracing()
|
||||||
} else {
|
} else {
|
||||||
log.Info("Tracing disabled.")
|
logger.Info("Tracing disabled.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.Getenv("DISABLE_PROFILER") == "" {
|
if os.Getenv("DISABLE_PROFILER") == "" {
|
||||||
log.Info("Profiling enabled.")
|
logger.Info("Profiling enabled.")
|
||||||
go initProfiling("productcatalogservice", "1.0.0")
|
go initProfiling("productcatalogservice", "1.0.0")
|
||||||
} else {
|
} else {
|
||||||
log.Info("Profiling disabled.")
|
logger.Info("Profiling disabled.")
|
||||||
}
|
}
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
@ -95,10 +96,10 @@ func main() {
|
||||||
if s := os.Getenv("EXTRA_LATENCY"); s != "" {
|
if s := os.Getenv("EXTRA_LATENCY"); s != "" {
|
||||||
v, err := time.ParseDuration(s)
|
v, err := time.ParseDuration(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to parse EXTRA_LATENCY (%s) as time.Duration: %+v", v, err)
|
logger.Fatalf("failed to parse EXTRA_LATENCY (%s) as time.Duration: %+v", v, err)
|
||||||
}
|
}
|
||||||
extraLatency = v
|
extraLatency = v
|
||||||
log.Infof("extra latency enabled (duration: %v)", extraLatency)
|
logger.Infof("extra latency enabled (duration: %v)", extraLatency)
|
||||||
} else {
|
} else {
|
||||||
extraLatency = time.Duration(0)
|
extraLatency = time.Duration(0)
|
||||||
}
|
}
|
||||||
|
@ -108,13 +109,13 @@ func main() {
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
sig := <-sigs
|
sig := <-sigs
|
||||||
log.Printf("Received signal: %s", sig)
|
logger.Printf("Received signal: %s", sig)
|
||||||
if sig == syscall.SIGUSR1 {
|
if sig == syscall.SIGUSR1 {
|
||||||
reloadCatalog = true
|
reloadCatalog = true
|
||||||
log.Infof("Enable catalog reloading")
|
logger.Infof("Enable catalog reloading")
|
||||||
} else {
|
} else {
|
||||||
reloadCatalog = false
|
reloadCatalog = false
|
||||||
log.Infof("Disable catalog reloading")
|
logger.Infof("Disable catalog reloading")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -122,7 +123,7 @@ func main() {
|
||||||
if os.Getenv("PORT") != "" {
|
if os.Getenv("PORT") != "" {
|
||||||
port = os.Getenv("PORT")
|
port = os.Getenv("PORT")
|
||||||
}
|
}
|
||||||
log.Infof("starting grpc server at :%s", port)
|
logger.Infof("starting grpc server at :%s", port)
|
||||||
run(port)
|
run(port)
|
||||||
select {}
|
select {}
|
||||||
}
|
}
|
||||||
|
@ -130,14 +131,14 @@ func main() {
|
||||||
func run(port string) string {
|
func run(port string) string {
|
||||||
l, err := net.Listen("tcp", fmt.Sprintf(":%s", port))
|
l, err := net.Listen("tcp", fmt.Sprintf(":%s", port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
logger.Fatal(err)
|
||||||
}
|
}
|
||||||
var srv *grpc.Server
|
var srv *grpc.Server
|
||||||
if os.Getenv("DISABLE_STATS") == "" {
|
if os.Getenv("DISABLE_STATS") == "" {
|
||||||
log.Info("Stats enabled.")
|
logger.Info("Stats enabled.")
|
||||||
srv = grpc.NewServer(grpc.StatsHandler(&ocgrpc.ServerHandler{}))
|
srv = grpc.NewServer(grpc.StatsHandler(&ocgrpc.ServerHandler{}))
|
||||||
} else {
|
} else {
|
||||||
log.Info("Stats disabled.")
|
logger.Info("Stats disabled.")
|
||||||
srv = grpc.NewServer()
|
srv = grpc.NewServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +153,7 @@ func run(port string) string {
|
||||||
func initJaegerTracing() {
|
func initJaegerTracing() {
|
||||||
svcAddr := os.Getenv("JAEGER_SERVICE_ADDR")
|
svcAddr := os.Getenv("JAEGER_SERVICE_ADDR")
|
||||||
if svcAddr == "" {
|
if svcAddr == "" {
|
||||||
log.Info("jaeger initialization disabled.")
|
logger.Info("jaeger initialization disabled.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Register the Jaeger exporter to be able to retrieve
|
// Register the Jaeger exporter to be able to retrieve
|
||||||
|
@ -164,19 +165,19 @@ func initJaegerTracing() {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
logger.Fatal(err)
|
||||||
}
|
}
|
||||||
trace.RegisterExporter(exporter)
|
trace.RegisterExporter(exporter)
|
||||||
log.Info("jaeger initialization completed.")
|
logger.Info("jaeger initialization completed.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func initStats(exporter *stackdriver.Exporter) {
|
func initStats(exporter *stackdriver.Exporter) {
|
||||||
view.SetReportingPeriod(60 * time.Second)
|
view.SetReportingPeriod(60 * time.Second)
|
||||||
view.RegisterExporter(exporter)
|
view.RegisterExporter(exporter)
|
||||||
if err := view.Register(ocgrpc.DefaultServerViews...); err != nil {
|
if err := view.Register(ocgrpc.DefaultServerViews...); err != nil {
|
||||||
log.Info("Error registering default server views")
|
logger.Info("Error registering default server views")
|
||||||
} else {
|
} else {
|
||||||
log.Info("Registered default server views")
|
logger.Info("Registered default server views")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,21 +187,21 @@ func initStackdriverTracing() {
|
||||||
for i := 1; i <= 3; i++ {
|
for i := 1; i <= 3; i++ {
|
||||||
exporter, err := stackdriver.NewExporter(stackdriver.Options{})
|
exporter, err := stackdriver.NewExporter(stackdriver.Options{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("failed to initialize Stackdriver exporter: %+v", err)
|
logger.Warnf("failed to initialize Stackdriver exporter: %+v", err)
|
||||||
} else {
|
} else {
|
||||||
trace.RegisterExporter(exporter)
|
trace.RegisterExporter(exporter)
|
||||||
trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
|
trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
|
||||||
log.Info("registered Stackdriver tracing")
|
logger.Info("registered Stackdriver tracing")
|
||||||
|
|
||||||
// Register the views to collect server stats.
|
// Register the views to collect server stats.
|
||||||
initStats(exporter)
|
initStats(exporter)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
d := time.Second * 10 * time.Duration(i)
|
d := time.Second * 10 * time.Duration(i)
|
||||||
log.Infof("sleeping %v to retry initializing Stackdriver exporter", d)
|
logger.Infof("sleeping %v to retry initializing Stackdriver exporter", d)
|
||||||
time.Sleep(d)
|
time.Sleep(d)
|
||||||
}
|
}
|
||||||
log.Warn("could not initialize Stackdriver exporter after retrying, giving up")
|
logger.Warn("could not initialize Stackdriver exporter after retrying, giving up")
|
||||||
}
|
}
|
||||||
|
|
||||||
func initTracing() {
|
func initTracing() {
|
||||||
|
@ -218,21 +219,22 @@ func initProfiling(service, version string) {
|
||||||
// ProjectID must be set if not running on GCP.
|
// ProjectID must be set if not running on GCP.
|
||||||
// ProjectID: "my-project",
|
// ProjectID: "my-project",
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
log.Warnf("failed to start profiler: %+v", err)
|
logger.Warnf("failed to start profiler: %+v", err)
|
||||||
} else {
|
} else {
|
||||||
log.Info("started Stackdriver profiler")
|
logger.Info("started Stackdriver profiler")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
d := time.Second * 10 * time.Duration(i)
|
d := time.Second * 10 * time.Duration(i)
|
||||||
log.Infof("sleeping %v to retry initializing Stackdriver profiler", d)
|
logger.Infof("sleeping %v to retry initializing Stackdriver profiler", d)
|
||||||
time.Sleep(d)
|
time.Sleep(d)
|
||||||
}
|
}
|
||||||
log.Warn("could not initialize Stackdriver profiler after retrying, giving up")
|
logger.Warn("could not initialize Stackdriver profiler after retrying, giving up")
|
||||||
}
|
}
|
||||||
|
|
||||||
type productCatalog struct{}
|
type productCatalog struct{}
|
||||||
|
|
||||||
func readCatalogFile(catalog *pb.ListProductsResponse) error {
|
func readCatalogFile(ctx context.Context, catalog *pb.ListProductsResponse) error {
|
||||||
|
log := logger.WithFields(getTraceLogFields(ctx))
|
||||||
catalogMutex.Lock()
|
catalogMutex.Lock()
|
||||||
defer catalogMutex.Unlock()
|
defer catalogMutex.Unlock()
|
||||||
catalogJSON, err := ioutil.ReadFile("products.json")
|
catalogJSON, err := ioutil.ReadFile("products.json")
|
||||||
|
@ -248,9 +250,9 @@ func readCatalogFile(catalog *pb.ListProductsResponse) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseCatalog() []*pb.Product {
|
func parseCatalog(ctx context.Context) []*pb.Product {
|
||||||
if reloadCatalog || len(cat.Products) == 0 {
|
if reloadCatalog || len(cat.Products) == 0 {
|
||||||
err := readCatalogFile(&cat)
|
err := readCatalogFile(ctx, &cat)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []*pb.Product{}
|
return []*pb.Product{}
|
||||||
}
|
}
|
||||||
|
@ -266,17 +268,17 @@ func (p *productCatalog) Watch(req *healthpb.HealthCheckRequest, ws healthpb.Hea
|
||||||
return status.Errorf(codes.Unimplemented, "health check via Watch not implemented")
|
return status.Errorf(codes.Unimplemented, "health check via Watch not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *productCatalog) ListProducts(context.Context, *pb.Empty) (*pb.ListProductsResponse, error) {
|
func (p *productCatalog) ListProducts(ctx context.Context, _ *pb.Empty) (*pb.ListProductsResponse, error) {
|
||||||
time.Sleep(extraLatency)
|
time.Sleep(extraLatency)
|
||||||
return &pb.ListProductsResponse{Products: parseCatalog()}, nil
|
return &pb.ListProductsResponse{Products: parseCatalog(ctx)}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *productCatalog) GetProduct(ctx context.Context, req *pb.GetProductRequest) (*pb.Product, error) {
|
func (p *productCatalog) GetProduct(ctx context.Context, req *pb.GetProductRequest) (*pb.Product, error) {
|
||||||
time.Sleep(extraLatency)
|
time.Sleep(extraLatency)
|
||||||
var found *pb.Product
|
var found *pb.Product
|
||||||
for i := 0; i < len(parseCatalog()); i++ {
|
for i := 0; i < len(parseCatalog(ctx)); i++ {
|
||||||
if req.Id == parseCatalog()[i].Id {
|
if req.Id == parseCatalog(ctx)[i].Id {
|
||||||
found = parseCatalog()[i]
|
found = parseCatalog(ctx)[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if found == nil {
|
if found == nil {
|
||||||
|
@ -289,7 +291,7 @@ func (p *productCatalog) SearchProducts(ctx context.Context, req *pb.SearchProdu
|
||||||
time.Sleep(extraLatency)
|
time.Sleep(extraLatency)
|
||||||
// Intepret query as a substring match in name or description.
|
// Intepret query as a substring match in name or description.
|
||||||
var ps []*pb.Product
|
var ps []*pb.Product
|
||||||
for _, p := range parseCatalog() {
|
for _, p := range parseCatalog(ctx) {
|
||||||
if strings.Contains(strings.ToLower(p.Name), strings.ToLower(req.Query)) ||
|
if strings.Contains(strings.ToLower(p.Name), strings.ToLower(req.Query)) ||
|
||||||
strings.Contains(strings.ToLower(p.Description), strings.ToLower(req.Query)) {
|
strings.Contains(strings.ToLower(p.Description), strings.ToLower(req.Query)) {
|
||||||
ps = append(ps, p)
|
ps = append(ps, p)
|
||||||
|
@ -297,3 +299,16 @@ func (p *productCatalog) SearchProducts(ctx context.Context, req *pb.SearchProdu
|
||||||
}
|
}
|
||||||
return &pb.SearchProductsResponse{Results: ps}, nil
|
return &pb.SearchProductsResponse{Results: ps}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getTraceLogFields(ctx context.Context) logrus.Fields {
|
||||||
|
span := trace.FromContext(ctx)
|
||||||
|
if span == nil {
|
||||||
|
return logrus.Fields{}
|
||||||
|
}
|
||||||
|
traceID := span.SpanContext().TraceID
|
||||||
|
spanID := span.SpanContext().SpanID
|
||||||
|
return logrus.Fields{
|
||||||
|
"trace_id": hex.EncodeToString(traceID[:]),
|
||||||
|
"span_id": hex.EncodeToString(spanID[:]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
@ -41,12 +42,12 @@ const (
|
||||||
defaultPort = "50051"
|
defaultPort = "50051"
|
||||||
)
|
)
|
||||||
|
|
||||||
var log *logrus.Logger
|
var logger *logrus.Logger
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
log = logrus.New()
|
logger = logrus.New()
|
||||||
log.Level = logrus.DebugLevel
|
logger.Level = logrus.DebugLevel
|
||||||
log.Formatter = &logrus.JSONFormatter{
|
logger.Formatter = &logrus.JSONFormatter{
|
||||||
FieldMap: logrus.FieldMap{
|
FieldMap: logrus.FieldMap{
|
||||||
logrus.FieldKeyTime: "timestamp",
|
logrus.FieldKeyTime: "timestamp",
|
||||||
logrus.FieldKeyLevel: "severity",
|
logrus.FieldKeyLevel: "severity",
|
||||||
|
@ -54,22 +55,22 @@ func init() {
|
||||||
},
|
},
|
||||||
TimestampFormat: time.RFC3339Nano,
|
TimestampFormat: time.RFC3339Nano,
|
||||||
}
|
}
|
||||||
log.Out = os.Stdout
|
logger.Out = os.Stdout
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if os.Getenv("DISABLE_TRACING") == "" {
|
if os.Getenv("DISABLE_TRACING") == "" {
|
||||||
log.Info("Tracing enabled.")
|
logger.Info("Tracing enabled.")
|
||||||
go initTracing()
|
go initTracing()
|
||||||
} else {
|
} else {
|
||||||
log.Info("Tracing disabled.")
|
logger.Info("Tracing disabled.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.Getenv("DISABLE_PROFILER") == "" {
|
if os.Getenv("DISABLE_PROFILER") == "" {
|
||||||
log.Info("Profiling enabled.")
|
logger.Info("Profiling enabled.")
|
||||||
go initProfiling("shippingservice", "1.0.0")
|
go initProfiling("shippingservice", "1.0.0")
|
||||||
} else {
|
} else {
|
||||||
log.Info("Profiling disabled.")
|
logger.Info("Profiling disabled.")
|
||||||
}
|
}
|
||||||
|
|
||||||
port := defaultPort
|
port := defaultPort
|
||||||
|
@ -80,26 +81,26 @@ func main() {
|
||||||
|
|
||||||
lis, err := net.Listen("tcp", port)
|
lis, err := net.Listen("tcp", port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to listen: %v", err)
|
logger.Fatalf("failed to listen: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var srv *grpc.Server
|
var srv *grpc.Server
|
||||||
if os.Getenv("DISABLE_STATS") == "" {
|
if os.Getenv("DISABLE_STATS") == "" {
|
||||||
log.Info("Stats enabled.")
|
logger.Info("Stats enabled.")
|
||||||
srv = grpc.NewServer(grpc.StatsHandler(&ocgrpc.ServerHandler{}))
|
srv = grpc.NewServer(grpc.StatsHandler(&ocgrpc.ServerHandler{}))
|
||||||
} else {
|
} else {
|
||||||
log.Info("Stats disabled.")
|
logger.Info("Stats disabled.")
|
||||||
srv = grpc.NewServer()
|
srv = grpc.NewServer()
|
||||||
}
|
}
|
||||||
svc := &server{}
|
svc := &server{}
|
||||||
pb.RegisterShippingServiceServer(srv, svc)
|
pb.RegisterShippingServiceServer(srv, svc)
|
||||||
healthpb.RegisterHealthServer(srv, svc)
|
healthpb.RegisterHealthServer(srv, svc)
|
||||||
log.Infof("Shipping Service listening on port %s", port)
|
logger.Infof("Shipping Service listening on port %s", port)
|
||||||
|
|
||||||
// Register reflection service on gRPC server.
|
// Register reflection service on gRPC server.
|
||||||
reflection.Register(srv)
|
reflection.Register(srv)
|
||||||
if err := srv.Serve(lis); err != nil {
|
if err := srv.Serve(lis); err != nil {
|
||||||
log.Fatalf("failed to serve: %v", err)
|
logger.Fatalf("failed to serve: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,6 +118,7 @@ func (s *server) Watch(req *healthpb.HealthCheckRequest, ws healthpb.Health_Watc
|
||||||
|
|
||||||
// GetQuote produces a shipping quote (cost) in USD.
|
// GetQuote produces a shipping quote (cost) in USD.
|
||||||
func (s *server) GetQuote(ctx context.Context, in *pb.GetQuoteRequest) (*pb.GetQuoteResponse, error) {
|
func (s *server) GetQuote(ctx context.Context, in *pb.GetQuoteRequest) (*pb.GetQuoteResponse, error) {
|
||||||
|
log := logger.WithFields(getTraceLogFields(ctx))
|
||||||
log.Info("[GetQuote] received request")
|
log.Info("[GetQuote] received request")
|
||||||
defer log.Info("[GetQuote] completed request")
|
defer log.Info("[GetQuote] completed request")
|
||||||
|
|
||||||
|
@ -142,6 +144,7 @@ func (s *server) GetQuote(ctx context.Context, in *pb.GetQuoteRequest) (*pb.GetQ
|
||||||
// ShipOrder mocks that the requested items will be shipped.
|
// ShipOrder mocks that the requested items will be shipped.
|
||||||
// It supplies a tracking ID for notional lookup of shipment delivery status.
|
// It supplies a tracking ID for notional lookup of shipment delivery status.
|
||||||
func (s *server) ShipOrder(ctx context.Context, in *pb.ShipOrderRequest) (*pb.ShipOrderResponse, error) {
|
func (s *server) ShipOrder(ctx context.Context, in *pb.ShipOrderRequest) (*pb.ShipOrderResponse, error) {
|
||||||
|
log := logger.WithFields(getTraceLogFields(ctx))
|
||||||
log.Info("[ShipOrder] received request")
|
log.Info("[ShipOrder] received request")
|
||||||
defer log.Info("[ShipOrder] completed request")
|
defer log.Info("[ShipOrder] completed request")
|
||||||
// 1. Create a Tracking ID
|
// 1. Create a Tracking ID
|
||||||
|
@ -157,7 +160,7 @@ func (s *server) ShipOrder(ctx context.Context, in *pb.ShipOrderRequest) (*pb.Sh
|
||||||
func initJaegerTracing() {
|
func initJaegerTracing() {
|
||||||
svcAddr := os.Getenv("JAEGER_SERVICE_ADDR")
|
svcAddr := os.Getenv("JAEGER_SERVICE_ADDR")
|
||||||
if svcAddr == "" {
|
if svcAddr == "" {
|
||||||
log.Info("jaeger initialization disabled.")
|
logger.Info("jaeger initialization disabled.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,19 +173,19 @@ func initJaegerTracing() {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
logger.Fatal(err)
|
||||||
}
|
}
|
||||||
trace.RegisterExporter(exporter)
|
trace.RegisterExporter(exporter)
|
||||||
log.Info("jaeger initialization completed.")
|
logger.Info("jaeger initialization completed.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func initStats(exporter *stackdriver.Exporter) {
|
func initStats(exporter *stackdriver.Exporter) {
|
||||||
view.SetReportingPeriod(60 * time.Second)
|
view.SetReportingPeriod(60 * time.Second)
|
||||||
view.RegisterExporter(exporter)
|
view.RegisterExporter(exporter)
|
||||||
if err := view.Register(ocgrpc.DefaultServerViews...); err != nil {
|
if err := view.Register(ocgrpc.DefaultServerViews...); err != nil {
|
||||||
log.Warn("Error registering default server views")
|
logger.Warn("Error registering default server views")
|
||||||
} else {
|
} else {
|
||||||
log.Info("Registered default server views")
|
logger.Info("Registered default server views")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,21 +195,21 @@ func initStackdriverTracing() {
|
||||||
for i := 1; i <= 3; i++ {
|
for i := 1; i <= 3; i++ {
|
||||||
exporter, err := stackdriver.NewExporter(stackdriver.Options{})
|
exporter, err := stackdriver.NewExporter(stackdriver.Options{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("failed to initialize Stackdriver exporter: %+v", err)
|
logger.Warnf("failed to initialize Stackdriver exporter: %+v", err)
|
||||||
} else {
|
} else {
|
||||||
trace.RegisterExporter(exporter)
|
trace.RegisterExporter(exporter)
|
||||||
trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
|
trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()})
|
||||||
log.Info("registered Stackdriver tracing")
|
logger.Info("registered Stackdriver tracing")
|
||||||
|
|
||||||
// Register the views to collect server stats.
|
// Register the views to collect server stats.
|
||||||
initStats(exporter)
|
initStats(exporter)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
d := time.Second * 10 * time.Duration(i)
|
d := time.Second * 10 * time.Duration(i)
|
||||||
log.Infof("sleeping %v to retry initializing Stackdriver exporter", d)
|
logger.Infof("sleeping %v to retry initializing Stackdriver exporter", d)
|
||||||
time.Sleep(d)
|
time.Sleep(d)
|
||||||
}
|
}
|
||||||
log.Warn("could not initialize Stackdriver exporter after retrying, giving up")
|
logger.Warn("could not initialize Stackdriver exporter after retrying, giving up")
|
||||||
}
|
}
|
||||||
|
|
||||||
func initTracing() {
|
func initTracing() {
|
||||||
|
@ -224,14 +227,27 @@ func initProfiling(service, version string) {
|
||||||
// ProjectID must be set if not running on GCP.
|
// ProjectID must be set if not running on GCP.
|
||||||
// ProjectID: "my-project",
|
// ProjectID: "my-project",
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
log.Warnf("failed to start profiler: %+v", err)
|
logger.Warnf("failed to start profiler: %+v", err)
|
||||||
} else {
|
} else {
|
||||||
log.Info("started Stackdriver profiler")
|
logger.Info("started Stackdriver profiler")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
d := time.Second * 10 * time.Duration(i)
|
d := time.Second * 10 * time.Duration(i)
|
||||||
log.Infof("sleeping %v to retry initializing Stackdriver profiler", d)
|
logger.Infof("sleeping %v to retry initializing Stackdriver profiler", d)
|
||||||
time.Sleep(d)
|
time.Sleep(d)
|
||||||
}
|
}
|
||||||
log.Warn("could not initialize Stackdriver profiler after retrying, giving up")
|
logger.Warn("could not initialize Stackdriver profiler after retrying, giving up")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTraceLogFields(ctx context.Context) logrus.Fields {
|
||||||
|
span := trace.FromContext(ctx)
|
||||||
|
if span == nil {
|
||||||
|
return logrus.Fields{}
|
||||||
|
}
|
||||||
|
traceID := span.SpanContext().TraceID
|
||||||
|
spanID := span.SpanContext().SpanID
|
||||||
|
return logrus.Fields{
|
||||||
|
"trace_id": hex.EncodeToString(traceID[:]),
|
||||||
|
"span_id": hex.EncodeToString(spanID[:]),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue