Observability in Your Application: Metrics with OpenTelemetry

Understanding an application's behavior in real-time is essential when deploying it. Think of it as checking the weather before heading out—you want to know the state of your app at a glance.
Key indicators such as requests per minute (RPM), resource usage, response times, and custom business metrics are invaluable. Metrics give you this insight, enabling you to monitor and optimize your application's performance.
In this post, we’ll explore using OpenTelemetry (Otel) metrics and set up a pipeline to visualize them in Grafana.
OpenTelemetry Metrics: Key Concepts
Before diving into implementation, let’s understand the basic terms in OpenTelemetry Metrics:
MeterProvider: A singleton object that manages all meters in your application. Think of it as the provider for meters.
Meters: Groups of instruments used to collect related metrics. For example, you might use one meter per microservice or domain.
Instrument: A specific type of metric, such as counters, histograms, or gauges.
Metric: The actual data collected. For example, incrementing a counter when an endpoint is called represents a metric.
These are the foundational concepts you need to work with OpenTelemetry metrics.
Types of Metrics
OpenTelemetry supports three primary types of metrics:
Synchronous Metrics: These are recorded at the moment an event occurs. For example, tracking the number of HTTP requests.
Additive Metrics: Instruments like
UpDownCountertrack values that can go up and down, such as active users.Monotonic Metrics: A subset of additive metrics that only increase over time, like total orders placed.
Setting Up Metrics: A Practical Example
We’ll implement metrics for an Order API that:
Creates orders.
Ships them.
Retrieves them by ID.
We’ll track:
Requests per minute (RPM).
Response times.
Custom metrics, like the number of shipped and canceled orders in the past week.
Our setup includes OpenTelemetry, Prometheus, and Grafana:
OpenTelemetry collects metrics.
Prometheus scrapes and stores metrics.
Grafana visualizes them in dashboards.
Code Implementation
Initializing a MeterProvider
First, initialize the MeterProvider:
func InitMeter() (*metric2.MeterProvider, error) {
ctx := context.Background()
metricExporter, err := otlpmetrichttp.New(ctx, otlpmetrichttp.WithEndpoint("localhost:4318"), otlpmetrichttp.WithInsecure())
if err != nil {
return nil, err
}
meterProvider := metric2.NewMeterProvider(
metric2.WithReader(metric2.NewPeriodicReader(metricExporter)),
metric2.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String("Order Api"),
)),
)
otel.SetMeterProvider(meterProvider)
return meterProvider, nil
}
This creates a global object for your app to use. Add this to your app’s initialization:
mp, err := trace.InitMeter()
if err != nil {
log.Fatal("Failed to initialize meter provider")
}
defer func() { _ = mp.Shutdown(context.Background()) }()
Exporting Metrics with FiberPrometheus
We’ll use FiberPrometheus middleware to export standard HTTP metrics like:
http_requests_totalhttp_request_duration_seconds
prometheus := fiberprometheus.New("order-api")
prometheus.RegisterAt(app, "/metrics")
app.Use(prometheus.Middleware)
This will enable Prometheus to scrape metrics from the /metrics endpoint.
Adding Custom Metrics: Shipped Orders
For custom metrics, like shipped orders, define and register them:
func InitMetrics(db *gorm.DB) {
meterProvider := otel.GetMeterProvider()
meter := meterProvider.Meter("Order Api")
initShippedOrdersMetric(db, meter)
}
func initShippedOrdersMetric(db *gorm.DB, meter metric.Meter) error {
shippedOrdersMetric, err := meter.Int64ObservableGauge(
"shipped_orders_count_metric",
metric.WithDescription("Counts the number of shipped orders"),
)
if err != nil {
log.Fatalf("Failed to create metric: %v", err)
}
_, err = meter.RegisterCallback(
func(ctx context.Context, observer metric.Observer) error {
var count int64
result := db.WithContext(ctx).Model(&entity.Order{}).
Where("is_shipped = ? AND created_at > NOW() - INTERVAL '7 DAYS'", true).
Count(&count)
if result.Error != nil {
log.Printf("Database query error: %v", result.Error)
}
observer.ObserveInt64(shippedOrdersMetric, count)
return nil
},
shippedOrdersMetric,
)
return err
}
This captures the count of shipped orders in the past 7 days and exports it as a custom metric.
Visualizing Metrics in Grafana
Once Prometheus scrapes the metrics, you can visualize them in Grafana.
Setting Up the Dashboard
Open Grafana at
http://localhost:3000and log in.Create a new dashboard from the sidebar.
Example Visualizations
Requests per Minute (RPM):
sum(rate(http_requests_total[1m])) by (job) * 60Use the "Time Series" visualization type.
95th Percentile Response Time:
histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[1m])) by (le))Shipped Orders (Custom Metric): For the
shipped_orders_count_metric, use a single-value panel to display the current count.
Conclusion
With OpenTelemetry, Prometheus, and Grafana, you can track key performance metrics and custom business data in real time. Metrics like RPM, response times, and shipped orders help ensure your app runs smoothly and efficiently.
The complete codebase and setup instructions are available on GitHub. Explore and adapt it to your app for enhanced observability!
If you want to learn more about Golang and backend development, subscribe to my newsletter at firatkomurcu.com
May the force be with you!



