Golang: Jaeger and OpenTelemetry

Okky Muhamad Budiman
4 min readSep 9, 2022

--

Currently, in Software Development most of us already apply the concept of microservices. Microservices is a distributed system based on domain/areas, where each area is divided into several services and connected to each other.

What must be considered from microservices is performance and consistently data, so to make sure our function is running well and complete the data we need some tools monitoring that can show the process from A to Z in our systems, for example, when we need to check the performance of login API or if we need to find the error in our code.

We usually look up logs monitoring tools to find root causes, but sometimes seeing logs one by one is very heavy and exhausting. In my experience, I’m using Jaeger and OpenTelemetry tools for traces, and logs for my project on the server.

So what are Jaeger and OpenTelemetry and how to implement them in Go ?

Jaeger

Based on the Jaeger website :

Jaeger, inspired by Dapper and OpenZipkin, is a distributed tracing system released as open source by Uber Technologies. It is used for monitoring and troubleshooting microservices-based distributed systems, including:

Distributed context propagation

Distributed transaction monitoring

Root cause analysis

Service dependency analysis

Performance / latency optimization

Uber published a blog post, Evolving Distributed Tracing at Uber, where they explain the history and reasons for the architectural choices made in Jaeger. Yuri Shkuro, creator of Jaeger, also published a book Mastering Distributed Tracing that covers in-depth many aspects of Jaeger design and operation, as well as distributed tracing in general.

OpenTelemetry

OpenTelemetry is instrumentation for emitting traces, metrics, and logs. Otel will be sent to observability backends, in this case, we using Jaeger.

Implementation

let’s say we are making a system login application where there are several steps to do it.

Hit API Login(router) -> Login Handler -> Login Services -> Login Repository

Setup Jaeger Config
Jaeger using docker

docker run -d — name jaeger \
-e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
-e COLLECTOR_OTLP_ENABLED=true \
-p 6831:6831/udp \
-p 6832:6832/udp \
-p 5778:5778 \
-p 16686:16686 \
-p 4317:4317 \
-p 4318:4318 \
-p 14250:14250 \
-p 14268:14268 \
-p 14269:14269 \
-p 9411:9411 \
jaegertracing/all-in-one:1.37

Install OpenTelemetry in Go

go get go.opentelemetry.io/otel \
go.opentelemetry.io/otel/trace \
go.opentelemetry.io/otel/sdk \
go.opentelemetry.io/otel/sdk/resource \
go.opentelemetry.io/otel/exporters/jaeger\
go.opentelemetry.io/otel/semconv\

Create pkg OpenTelemetry and Create Implementation
Adding wrapper package for OpenTelemetry.

import (
“go.opentelemetry.io/otel/attribute”
“go.opentelemetry.io/otel/exporters/jaeger”
“go.opentelemetry.io/otel/sdk/resource”
sdktrace “go.opentelemetry.io/otel/sdk/trace”
semconv “go.opentelemetry.io/otel/semconv/v1.7.0”
)
func NewExporter(url string) (*sdktrace.TracerProvider, error) {
// Your preferred exporter: console, jaeger, zipkin, OTLP, etc.
exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)))
if err != nil {
return nil, err
}
tp := sdktrace.NewTracerProvider(
// Always be sure to batch in production.
sdktrace.WithBatcher(exp),
// Record information about this application in a Resource.
sdktrace.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String(“flip-ewallet”),
attribute.String(“environment”, “development”),
attribute.Int64(“ID”, 1),
)),
)
return tp, nil
}

Implement package OpenTelemetry
In main.go we will init OpenTelemetry and add tracer. example component-main

// opentelemetry
tp, err := opentelemetry.NewExporter(cfg.Jaeger.Endpoint)
if err != nil {
log.Fatal(err)
}
otel.SetTracerProvider(tp)

tp.Tracer("component-http")

Implement tracer in 1 cycle of endpoint, for this case we assume we already implement API login (router, handler, repository). So in every step, we will put a tracer so that we can see the traces in Jaeger UI.

Login Handler :

func (u userHandler) Login(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
ctx, span := otel.Tracer("users").Start(ctx, "UserHandler_Login")
defer span.End()
.....
}

Login Services :

func (u userService) Login(ctx context.Context, username string) (string, error) {
ctx, span := otel.Tracer(“users”).Start(ctx, “UserService_Login”)
defer span.End()
.....
}

User Repository :

func (u *userRepo) Find(ctx context.Context, username string) (*dao.User, error) {
ctx, span := otel.GetTracerProvider().Tracer("users").Start(ctx, "UserRepository_Find")
defer span.End()
.....
}

JWT Service :

func JWTSigning(ctx context.Context, key string) (string, error) {
ctx, span := otel.Tracer("users").Start(ctx, "UserService_jwtSigning")
defer span.End()
.....
}

Test Endpoint and Access Jaeger UI
Go to http://localhost:16686 to see the Operation and Logs.

We can see in operation of one cycle of login endpoint from A to Z and process, so we can see traces, logs, and performance in every step.

With Jaeger and OpenTelemetry we can monitor our service operations easily.

References :

--

--

Okky Muhamad Budiman
Okky Muhamad Budiman

Written by Okky Muhamad Budiman

Tech Enthusiast, Punk Rock Software Engineer, Hustler Harder

Responses (1)