This example demonstrates how you can attach OpenTelemetry tracing to outgoing HTTP client request and server response. As this is a follow up to the previous example linked below I am cutting it short.


Config


This piece of code is just an addition to http.go file you can find in the previous post - Implementing OpenTelemetry and Jaeger tracing in Golang HTTP API.


// HTTPClientTransporter is a convenience function which helps attaching tracing
// functionality to conventional HTTP clients.
func HTTPClientTransporter(rt http.RoundTripper) http.RoundTripper {
return otelhttp.NewTransport(rt)
}

Client


This is just an example so you are meant to tidy it up. It is calling the server and expecting a response.


package service

import (
"context"
"fmt"
"net/http"
"time"

"github.com/you/client/internal/pkg/trace"
)

type service struct {
// ...
}

func (s service) httpRequest(ctx context.Context) error {
// Create a child span.
ctx, span := trace.NewSpan(ctx, "service.httpRequest", nil)
defer span.End()

ctx, cancel := context.WithTimeout(ctx, time.Second*10)
defer cancel()

req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://localhost:8090/server/users", nil)
if err != nil {
trace.AddSpanError(span, err)
trace.FailSpan(span, "request error")

return err
}

res, err := trace.HTTPClientTransporter(http.DefaultTransport).RoundTrip(req)
defer res.Body.Close()
if err != nil {
trace.AddSpanError(span, err)
trace.FailSpan(span, "rounttrip error")

return err
}

if res.StatusCode != http.StatusOK {
err := fmt.Errorf("unexpected response code")

trace.AddSpanError(span, err)
trace.FailSpan(span, "response error")

return err
}

return nil
}

Server


Same as above, you can tidy this up as well.


// ...

func main() {
ctx := context.Background()

// Bootstrap tracer.
prv, err := trace.NewProvider(trace.ProviderConfig{
JaegerEndpoint: "http://localhost:14268/api/traces",
ServiceName: "server",
ServiceVersion: "2.0.0",
Environment: "dev",
Disabled: false,
})
if err != nil {
log.Fatalln(err)
}
defer prv.Close(ctx)

handler := http.NewServeMux()
handler.HandleFunc("/server/users", trace.HTTPHandlerFunc(app.User{}.Create, "users_create"))

log.Fatal(http.ListenAndServe(":8090", handler))
}

// ...

func (u User) Create(w http.ResponseWriter, r *http.Request) {
// Create the parent span.
_, span := trace.NewSpan(r.Context(), "User.Create", nil)
defer span.End()

// ...
}

Result


The first three are a successful request and the last two is failed tracing information.