Hello everyone!

We have been investing plenty of personal time and energy for many years to share our knowledge with you all. However, we now need your help to keep this blog running. All you have to do is just click one of the adverts on the site, otherwise it will sadly be taken down due to hosting etc. costs. Thank you.

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.