Skip to content

Commit

Permalink
[hotrod] Upgrade HotROD to use OpenTelemetry instrumentation (#4548)
Browse files Browse the repository at this point in the history
## Which problem is this PR solving?
* Part of #3380
* This PR tries to instrument the hotROD app with otel downgrading
opentracing. Related
#4533 (comment)

## Short description of the changes
- Upgrades hotROD application to support `otel tracer`

---------

Signed-off-by: Afzal Ansari <afzal442@gmail.com>
Signed-off-by: afzal442 <afzal442@gmail.com>
Signed-off-by: Afzal <94980910+afzalbin64@users.noreply.github.com>
Co-authored-by: Afzal <94980910+afzalbin64@users.noreply.github.com>
  • Loading branch information
afzal442 and afzalbin64 committed Jul 15, 2023
1 parent 673b69f commit 96f7fdc
Show file tree
Hide file tree
Showing 12 changed files with 68 additions and 96 deletions.
2 changes: 1 addition & 1 deletion examples/hotrod/cmd/frontend.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ var frontendCmd = &cobra.Command{
logger := log.NewFactory(zapLogger)
server := frontend.NewServer(
options,
tracing.Init("frontend", otelExporter, metricsFactory, logger),
tracing.InitOTEL("frontend", otelExporter, metricsFactory, logger),
logger,
)
return logError(zapLogger, server.Run())
Expand Down
2 changes: 1 addition & 1 deletion examples/hotrod/cmd/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ var routeCmd = &cobra.Command{
logger := log.NewFactory(zapLogger)
server := route.NewServer(
net.JoinHostPort("0.0.0.0", strconv.Itoa(routePort)),
tracing.Init("route", otelExporter, metricsFactory, logger),
tracing.InitOTEL("route", otelExporter, metricsFactory, logger),
logger,
)
return logError(zapLogger, server.Run())
Expand Down
26 changes: 18 additions & 8 deletions examples/hotrod/pkg/tracing/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,35 @@ import (
"io"
"net/http"

"github.com/opentracing-contrib/go-stdlib/nethttp"
"github.com/opentracing/opentracing-go"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel/trace"
)

// HTTPClient wraps an http.Client with tracing instrumentation.
type HTTPClient struct {
Tracer opentracing.Tracer
Client *http.Client
TracerProvider trace.TracerProvider
Client *http.Client
}

func NewHTTPClient(tp trace.TracerProvider) *HTTPClient {
return &HTTPClient{
TracerProvider: tp,
Client: &http.Client{
Transport: otelhttp.NewTransport(
http.DefaultTransport,
otelhttp.WithTracerProvider(tp),
),
},
}
}

// GetJSON executes HTTP GET against specified url and tried to parse
// the response into out object.
func (c *HTTPClient) GetJSON(ctx context.Context, endpoint string, url string, out interface{}) error {
req, err := http.NewRequest(http.MethodGet, url, nil)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return err
}
req = req.WithContext(ctx)
req, ht := nethttp.TraceRequest(c.Tracer, req, nethttp.OperationName("HTTP GET: "+endpoint))
defer ht.Finish()

res, err := c.Client.Do(req)
if err != nil {
Expand All @@ -57,6 +66,7 @@ func (c *HTTPClient) GetJSON(ctx context.Context, endpoint string, url string, o
}
return errors.New(string(body))
}

decoder := json.NewDecoder(res.Body)
return decoder.Decode(out)
}
50 changes: 10 additions & 40 deletions examples/hotrod/pkg/tracing/mux.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,15 @@ package tracing
import (
"net/http"

"github.com/opentracing-contrib/go-stdlib/nethttp"
"github.com/opentracing/opentracing-go"
"go.opentelemetry.io/otel/baggage"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"

"github.com/jaegertracing/jaeger/examples/hotrod/pkg/log"
)

// NewServeMux creates a new TracedServeMux.
func NewServeMux(copyBaggage bool, tracer opentracing.Tracer, logger log.Factory) *TracedServeMux {
func NewServeMux(copyBaggage bool, tracer trace.TracerProvider, logger log.Factory) *TracedServeMux {
return &TracedServeMux{
mux: http.NewServeMux(),
copyBaggage: copyBaggage,
Expand All @@ -41,51 +39,23 @@ func NewServeMux(copyBaggage bool, tracer opentracing.Tracer, logger log.Factory
type TracedServeMux struct {
mux *http.ServeMux
copyBaggage bool
tracer opentracing.Tracer
tracer trace.TracerProvider
logger log.Factory
}

// Handle implements http.ServeMux#Handle, which is used to register new handler.
func (tm *TracedServeMux) Handle(pattern string, handler http.Handler) {
tm.logger.Bg().Debug("registering traced handler", zap.String("endpoint", pattern))

middleware := nethttp.Middleware(
tm.tracer,
handler,
nethttp.OperationNameFunc(func(r *http.Request) string {
return "HTTP " + r.Method + " " + pattern
}),
// Jaeger SDK was able to accept `jaeger-baggage` header even for requests without am active trace.
// OTEL Bridge does not support that, so we use Baggage propagator to manually extract the baggage
// into Context (in otelBaggageExtractor handler below), and once the Bridge creates a Span,
// we use this SpanObserver to copy OTEL baggage from Context into the Span.
nethttp.MWSpanObserver(func(span opentracing.Span, r *http.Request) {
if !tm.copyBaggage {
return
}
bag := baggage.FromContext(r.Context())
for _, m := range bag.Members() {
if b := span.BaggageItem(m.Key()); b == "" {
span.SetBaggageItem(m.Key(), m.Value())
}
}
}),
)
tm.mux.Handle(pattern, otelBaggageExtractor(middleware))
middleware := otelhttp.NewHandler(
otelhttp.WithRouteTag(pattern, handler),
pattern,
otelhttp.WithTracerProvider(tm.tracer))

tm.mux.Handle(pattern, middleware)
}

// ServeHTTP implements http.ServeMux#ServeHTTP.
func (tm *TracedServeMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
tm.mux.ServeHTTP(w, r)
}

// Used with nethttp.MWSpanObserver above.
func otelBaggageExtractor(next http.Handler) http.Handler {
propagator := propagation.Baggage{}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
carrier := propagation.HeaderCarrier(r.Header)
ctx := propagator.Extract(r.Context(), carrier)
r = r.WithContext(ctx)
next.ServeHTTP(w, r)
})
}
15 changes: 4 additions & 11 deletions examples/hotrod/services/customer/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@ package customer
import (
"context"
"fmt"
"net/http"

"github.com/opentracing-contrib/go-stdlib/nethttp"
"github.com/opentracing/opentracing-go"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"

"github.com/jaegertracing/jaeger/examples/hotrod/pkg/log"
Expand All @@ -30,21 +28,16 @@ import (

// Client is a remote client that implements customer.Interface
type Client struct {
tracer opentracing.Tracer
logger log.Factory
client *tracing.HTTPClient
hostPort string
}

// NewClient creates a new customer.Client
func NewClient(tracer opentracing.Tracer, logger log.Factory, hostPort string) *Client {
func NewClient(tracer trace.TracerProvider, logger log.Factory, hostPort string) *Client {
return &Client{
tracer: tracer,
logger: logger,
client: &tracing.HTTPClient{
Client: &http.Client{Transport: &nethttp.Transport{}},
Tracer: tracer,
},
logger: logger,
client: tracing.NewHTTPClient(tracer),
hostPort: hostPort,
}
}
Expand Down
8 changes: 4 additions & 4 deletions examples/hotrod/services/customer/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,22 +49,22 @@ func newDatabase(tracer trace.Tracer, logger log.Factory) *database {
customers: map[string]*Customer{
"123": {
ID: "123",
Name: "Rachel's Floral Designs",
Name: "Rachel's_Floral_Designs",
Location: "115,277",
},
"567": {
ID: "567",
Name: "Amazing Coffee Roasters",
Name: "Amazing_Coffee_Roasters",
Location: "211,653",
},
"392": {
ID: "392",
Name: "Trom Chocolatier",
Name: "Trom_Chocolatier",
Location: "577,322",
},
"731": {
ID: "731",
Name: "Japanese Desserts",
Name: "Japanese_Desserts",
Location: "728,326",
},
},
Expand Down
6 changes: 3 additions & 3 deletions examples/hotrod/services/customer/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
"encoding/json"
"net/http"

"github.com/opentracing/opentracing-go"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"

"github.com/jaegertracing/jaeger/examples/hotrod/pkg/httperr"
Expand All @@ -31,7 +31,7 @@ import (
// Server implements Customer service
type Server struct {
hostPort string
tracer opentracing.Tracer
tracer trace.TracerProvider
logger log.Factory
database *database
}
Expand All @@ -40,7 +40,7 @@ type Server struct {
func NewServer(hostPort string, otelExporter string, metricsFactory metrics.Factory, logger log.Factory) *Server {
return &Server{
hostPort: hostPort,
tracer: tracing.Init("customer", otelExporter, metricsFactory, logger),
tracer: tracing.InitOTEL("customer", otelExporter, metricsFactory, logger),
logger: logger,
database: newDatabase(
tracing.InitOTEL("mysql", otelExporter, metricsFactory, logger).Tracer("mysql"),
Expand Down
12 changes: 5 additions & 7 deletions examples/hotrod/services/driver/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import (
"context"
"time"

otgrpc "github.com/opentracing-contrib/go-grpc"
"github.com/opentracing/opentracing-go"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
Expand All @@ -30,25 +30,23 @@ import (

// Client is a remote client that implements driver.Interface
type Client struct {
tracer opentracing.Tracer
logger log.Factory
client DriverServiceClient
}

// NewClient creates a new driver.Client
func NewClient(tracer opentracing.Tracer, logger log.Factory, hostPort string) *Client {
func NewClient(tracerProvider trace.TracerProvider, logger log.Factory, hostPort string) *Client {
conn, err := grpc.Dial(hostPort, grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithUnaryInterceptor(
otgrpc.OpenTracingClientInterceptor(tracer)),
otelgrpc.UnaryClientInterceptor(otelgrpc.WithTracerProvider(tracerProvider))),
grpc.WithStreamInterceptor(
otgrpc.OpenTracingStreamClientInterceptor(tracer)))
otelgrpc.StreamClientInterceptor(otelgrpc.WithTracerProvider(tracerProvider))))
if err != nil {
logger.Bg().Fatal("Cannot create gRPC connection", zap.Error(err))
}

client := NewDriverServiceClient(conn)
return &Client{
tracer: tracer,
logger: logger,
client: client,
}
Expand Down
16 changes: 12 additions & 4 deletions examples/hotrod/services/frontend/best_eta.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import (
"sync"
"time"

"github.com/opentracing/opentracing-go"
"go.opentelemetry.io/otel/baggage"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"

"github.com/jaegertracing/jaeger/examples/hotrod/pkg/log"
Expand All @@ -47,7 +48,7 @@ type Response struct {
ETA time.Duration
}

func newBestETA(tracer opentracing.Tracer, logger log.Factory, options ConfigOptions) *bestETA {
func newBestETA(tracer trace.TracerProvider, logger log.Factory, options ConfigOptions) *bestETA {
return &bestETA{
customer: customer.NewClient(
tracer,
Expand Down Expand Up @@ -76,9 +77,16 @@ func (eta *bestETA) Get(ctx context.Context, customerID string) (*Response, erro
}
eta.logger.For(ctx).Info("Found customer", zap.Any("customer", customer))

if span := opentracing.SpanFromContext(ctx); span != nil {
span.SetBaggageItem("customer", customer.Name)
m, err := baggage.NewMember("customer", customer.Name)
if err != nil {
eta.logger.For(ctx).Error("cannot create baggage member", zap.Error(err))
}
bag := baggage.FromContext(ctx)
bag, err = bag.SetMember(m)
if err != nil {
eta.logger.For(ctx).Error("cannot set baggage member", zap.Error(err))
}
ctx = baggage.ContextWithBaggage(ctx, bag)

drivers, err := eta.driver.FindNearest(ctx, customer.Location)
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions examples/hotrod/services/frontend/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"net/http"
"path"

"github.com/opentracing/opentracing-go"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"

"github.com/jaegertracing/jaeger/examples/hotrod/pkg/httperr"
Expand All @@ -36,7 +36,7 @@ var assetFS embed.FS
// Server implements jaeger-demo-frontend service
type Server struct {
hostPort string
tracer opentracing.Tracer
tracer trace.TracerProvider
logger log.Factory
bestETA *bestETA
assetFS http.FileSystem
Expand All @@ -56,7 +56,7 @@ type ConfigOptions struct {
}

// NewServer creates a new frontend.Server
func NewServer(options ConfigOptions, tracer opentracing.Tracer, logger log.Factory) *Server {
func NewServer(options ConfigOptions, tracer trace.TracerProvider, logger log.Factory) *Server {
return &Server{
hostPort: options.FrontendHostPort,
tracer: tracer,
Expand Down
15 changes: 4 additions & 11 deletions examples/hotrod/services/route/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,9 @@ package route

import (
"context"
"net/http"
"net/url"

"github.com/opentracing-contrib/go-stdlib/nethttp"
"github.com/opentracing/opentracing-go"
"go.opentelemetry.io/otel/trace"
"go.uber.org/zap"

"github.com/jaegertracing/jaeger/examples/hotrod/pkg/log"
Expand All @@ -30,21 +28,16 @@ import (

// Client is a remote client that implements route.Interface
type Client struct {
tracer opentracing.Tracer
logger log.Factory
client *tracing.HTTPClient
hostPort string
}

// NewClient creates a new route.Client
func NewClient(tracer opentracing.Tracer, logger log.Factory, hostPort string) *Client {
func NewClient(tracer trace.TracerProvider, logger log.Factory, hostPort string) *Client {
return &Client{
tracer: tracer,
logger: logger,
client: &tracing.HTTPClient{
Client: &http.Client{Transport: &nethttp.Transport{}},
Tracer: tracer,
},
logger: logger,
client: tracing.NewHTTPClient(tracer),
hostPort: hostPort,
}
}
Expand Down

0 comments on commit 96f7fdc

Please sign in to comment.