Mahakam comes with a built-in tracing support. It uses OpenTelemetry to collect traces data.
Example
Here is an example of a tracing middleware.
// main.go
package main
import (
"context"
"log"
"net/http"
"time"
"github.com/seiortech/mahakam"
"github.com/seiortech/mahakam/extensions"
"github.com/seiortech/mahakam/middleware"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
)
var (
serviceName = "example-tracing"
collectorURL = "jaeger:4317"
)
func main() {
tracer := extensions.NewTracer(serviceName, collectorURL)
cleanup := tracer.Init()
defer cleanup(context.Background())
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
_, span := tracer.Tracer().Start(r.Context(), "handle_root")
defer span.End()
span.SetAttributes(attribute.String("handler", "root"))
w.Write([]byte("Hello, World!"))
})
mux.HandleFunc("/foo", func(w http.ResponseWriter, r *http.Request) {
ctx, span := tracer.Tracer().Start(r.Context(), "handle_foo")
defer span.End()
span.SetAttributes(attribute.String("handler", "foo"))
fetchData(ctx, tracer.Tracer())
queryDatabase(ctx, tracer.Tracer())
w.Write([]byte("foo"))
})
mux.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
ctx, span := tracer.Tracer().Start(r.Context(), "handle_bar")
defer span.End()
span.SetAttributes(attribute.String("handler", "bar"))
fetchData(ctx, tracer.Tracer())
w.Write([]byte("bar"))
})
s := mahakam.NewServer("0.0.0.0:8080", mux)
s.Use(middleware.Logger)
s.Use(tracer.Middleware)
if err := s.ListenAndServe(); err != nil {
log.Fatalln("Failed to start server:", err)
}
}
func fetchData(ctx context.Context, tracer trace.Tracer) {
_, span := tracer.Start(ctx, "fetch_data")
defer span.End()
span.SetAttributes(
attribute.String("operation", "fetch_data"),
attribute.Int("duration_ms", 2000),
)
time.Sleep(2 * time.Second)
}
func queryDatabase(ctx context.Context, tracer trace.Tracer) {
_, span := tracer.Start(ctx, "query_database")
defer span.End()
span.SetAttributes(
attribute.String("operation", "query_database"),
attribute.String("db.type", "postgres"),
attribute.Int("duration_ms", 1000),
)
time.Sleep(1 * time.Second)
}
now, we need to setup our jaeger and prometheus exporters.
services:
server:
build: .
restart: always
container_name: server
platform: linux/aarch64
ports:
- "8080:8080"
environment:
- OTLP_ENDPOINT=http://jaeger:4318/v1/traces
depends_on:
- jaeger
jaeger:
image: jaegertracing/all-in-one:1.70.0
ports:
- "16686:16686" # UI
- "4317:4317"
environment:
- COLLECTOR_OTLP_ENABLED=true
- LOG_LEVEL=debug
container_name: jaeger
After that, we need to create a Dockerfile for building our server.
FROM golang:1.24.3-alpine AS build
WORKDIR /app
COPY go.mod ./
RUN go mod download && go mod verify
COPY . .
RUN go build -o /server example/tracing/cmd/main.go
FROM alpine
WORKDIR /app
RUN mkdir -p /app/logs
COPY --from=build /server /app/server
ENTRYPOINT ["/app/server"]
Now, let’s run our docker-compose.
docker-compose up -d
After that, we can access our jaeger dashboard at http://localhost:16686/
.
💡 Tip
You can use any other OpenTelemetry compatible monitor like
Grafana Tempo
or SigNoz
.
Implementation
You can check out the Mahakam repository for the tracing example.