DEV Community

Subramanya L
Subramanya L

Posted on

I Got Tired of Writing the Same PromQL Queries, So I Built PromDex

Every Spring Boot service already exposes a goldmine of metrics through Actuator and Micrometer.
The problem isn't collecting metrics. The problem is turning those metrics into something useful.
Every new service usually means repeating the same workflow:

  • Writing the same sum(rate(...)) PromQL queries again
  • Looking up whether a metric is a counter, gauge, histogram, or summary
  • Creating Grafana dashboards panel by panel
  • Copying dashboards between projects and tweaking them for the latest service None of this is particularly difficult. It's just repetitive. And repetitive work is exactly the kind of work that gets postponed — or copied incorrectly. So I built PromDex. ## What is PromDex? PromDex is a drop-in Spring Boot starter that automatically exposes REST endpoints for:
  • Building PromQL queries
  • Discovering Prometheus metrics
  • Generating Grafana dashboards Add the dependency, start your Spring Boot application, and the /promql/* endpoints are available immediately — no @Configuration, no manual bean wiring. ## What does it do?
  • Build PromQL queries from structured requests
  • Discover metrics directly from /actuator/prometheus
  • Detect counters, gauges, histograms, and summaries automatically
  • Generate Grafana import-ready dashboards
  • Support JVM, HTTP, Kafka, Database, Cache, Kubernetes, and custom application metrics ## Zero-configuration demo With nothing more than the dependency added, this endpoint:
curl http://localhost:8080/appmetrics > dashboard.json
Enter fullscreen mode Exit fullscreen mode

returns a complete Grafana dashboard JSON. Import it into Grafana and you get panels for JVM, HTTP, pod status, and discovered application metrics immediately — no dashboard building required.

How PromDex works

PromDex combines two sources of metrics:

  1. Static catalog — common metrics Spring Boot and Micrometer already expose (JVM memory, CPU usage, HTTP request metrics, pod metrics), each with correct PromQL already prepared.
  2. Runtime discovery — PromDex scrapes your application's own /actuator/prometheus endpoint and reads the Prometheus # TYPE declarations to determine whether each metric is a counter, gauge, histogram, or summary, then generates sensible default PromQL for each: | Metric type | Generated query | |---|---| | Counter | sum(rate(metric[5m])) | | Gauge | metric | | Histogram | histogram_quantile(0.9, sum(rate(metric_bucket[5m])) by (le)) | | Summary | sum(rate(metric_sum[5m])) / sum(rate(metric_count[5m])) | ## Installation
<dependency>
    <groupId>io.github.subramanya-dev</groupId>
    <artifactId>promdex</artifactId>
    <version>1.0.0</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

That's it. No configuration classes, no extra beans — just add the dependency and start your application.
Make sure Prometheus Actuator is exposed if you want live discovery:

management:
  endpoints:
    web:
      exposure:
        include: prometheus
Enter fullscreen mode Exit fullscreen mode

Build PromQL automatically

Instead of manually writing queries like:

sum(rate(http_requests_total{job="api", status="5.."}[5m])) by (status)
Enter fullscreen mode Exit fullscreen mode

send a request to /promql/build:

curl -X POST http://localhost:8080/promql/build \
  -H "Content-Type: application/json" \
  -d '{
    "metricName": "http_requests_total",
    "metricType": "COUNTER",
    "function": "rate",
    "range": "5m",
    "labels": { "job": "api", "status": "5.." },
    "aggregation": "sum",
    "groupByLabel": "status"
  }'
Enter fullscreen mode Exit fullscreen mode

PromDex returns:

{
  "query": "sum(rate(http_requests_total{job=\"api\", status=\"5..\"}[5m])) by (status)",
  "description": "Query built for metric 'http_requests_total' using rate",
  "valid": true,
  "warning": null
}
Enter fullscreen mode Exit fullscreen mode

No string concatenation, no manual PromQL building.

Discover metrics automatically

Want to know which metrics your application exposes?

curl http://localhost:8080/promql/discovered
Enter fullscreen mode Exit fullscreen mode

PromDex scrapes the metrics endpoint and returns every discovered metric with its type, description, suggested PromQL, and unit — including custom application metrics you've registered yourself.

Automatic method metrics

PromDex can instrument Spring beans with a single annotation:

@Service
class PaymentService {
    @MethodMetrics("payment_charge")
    public Receipt charge(Charge request) {
        return gateway.charge(request);
    }
}
Enter fullscreen mode Exit fullscreen mode

This automatically publishes invocation count, execution time, and exception metrics — tagged by method and outcome, and immediately discoverable and dashboard-ready.

Note: an exception that's caught and fully handled inside the method won't cross the AOP boundary automatically — record it explicitly with methodMetricsRecorder.recordHandledException(...) if you need it captured.

Production notes

PromDex generates PromQL heuristically. It's designed to remove repetitive work — not replace engineering judgement.
Before using generated dashboards or alerts in production:

  • Review the generated queries against real Prometheus data
  • Keep metric labels bounded — don't derive names or labels from request IDs, user IDs, order IDs, or raw error messages. That's a fast path to Prometheus cardinality explosions ## Project links
  • GitHub: github.com/subramanya-dev/promdex
  • Maven Central coordinates:
<dependency>
    <groupId>io.github.subramanya-dev</groupId>
    <artifactId>promdex</artifactId>
    <version>1.0.0</version>
</dependency>
Enter fullscreen mode Exit fullscreen mode

What's next?

PromDex is still in its early days, and there are plenty of ideas for future improvements. If you're building Spring Boot applications with Prometheus and Grafana, I'd love for you to give it a try.
Feedback, issues, feature requests, and pull requests are all welcome. If PromDex saves you from writing even one more sum(rate(...)) query by hand, it's already done its job.

Top comments (0)