Free lesson · GenAI Platform Engineering

Deploy analytics pipeline with Grafana dashboards

Deploy the analytics service, configure Prometheus metrics export, and build Grafana dashboards showing adoption funnels, feature heatmaps, and satisfaction trends.

Course: AI Developer Platform Engineering · Chapter 19 · Platform Analytics & Adoption

Free to read — no subscription required.

Introduction

Engineers often build thorough metrics designs only to find stakeholders can't see the data because the delivery pipeline was never production-hardened. Every adoption funnel stage, developer satisfaction score, and executive KPI you've defined in this chapter stays invisible until a reliable collection loop, Prometheus-compatible exporter, and provisioned Grafana dashboards are running continuously. By the end of this lesson, you'll be able to instrument a Python analytics service with Prometheus client libraries, configure a metrics collection orchestrator, and provision Grafana dashboards as code—creating a pipeline that surfaces platform adoption data to stakeholders without manual intervention or silent data gaps.

Key Terminology

  • Metrics Exporter: A service component that computes and exposes metric values in a format compatible with a monitoring system's scrape protocol, such as Prometheus's text exposition format.
  • Dashboard Provisioning: The practice of defining dashboards as code—either through configuration files or API calls—so that dashboard state is reproducible across environments without manual UI interaction.
  • Metric Staleness: A condition where a Prometheus gauge or counter has not been updated within its expected collection interval, causing dashboards to display outdated values that may mislead decision-makers.
  • Collection Cycle: A single iteration of the metric computation loop where the orchestrator queries source databases, computes aggregations, and updates Prometheus gauges before the next scrape.
  • PromQL: Prometheus Query Language, a functional query language used in Grafana panel definitions to select, aggregate, and transform time-series data stored in Prometheus.

Concepts

Deployment Considerations for Production

When deploying this pipeline to a production Kubernetes cluster, configure the Prometheus scrape target using a ServiceMonitor custom resource rather than static scrape configs. This ensures that scaling the analytics service horizontally does not require manual Prometheus reconfiguration. Set the scrape interval to match your collection interval—if the orchestrator refreshes gauges every 60 seconds, a 15-second Prometheus scrape wastes resources reading identical values.

For Grafana dashboard versioning, store the provisioned dashboard JSON in your Git repository and run the provisioner as a CI/CD step on merge to main. This gives you audit trails for every dashboard change and the ability to roll back a broken panel without navigating the Grafana UI under pressure during an incident.

Monitor the pipeline itself using the platform_metric_collection_total counter and platform_metric_collection_duration_seconds histogram. Create a Grafana alert rule that fires when the error rate exceeds 20% over a 10-minute window, and a separate alert when collection duration p99 exceeds 30 seconds. These alerts protect against silent data pipeline failures that would otherwise erode trust in your executive dashboards.

Code Walkthrough

Now that you understand how the Metrics Exporter, Collection Cycle, Dashboard Provisioning, and PromQL concepts fit together, the implementation maps directly onto those abstractions. The analytics pipeline connects four components: an ingestion API, a computation worker, a Prometheus exporter, and Grafana with provisioned dashboards. The diagram below shows how data flows through each layer.

Loading diagram...

The ingestion API writes raw events to PostgreSQL without blocking on computation. The worker runs on a configurable schedule—every 60 seconds for operational metrics, every 15 minutes for aggregate KPIs—writing computed values to Redis so that the Prometheus exporter always finds fresh gauge values on each scrape. Grafana queries Prometheus using PromQL expressions defined in provisioned dashboard JSON, giving you reproducible dashboards across environments without manual UI configuration.

The PlatformMetricsExporter class below initializes all gauges, counters, and histograms your Grafana dashboards will query, then performs a single timed collection cycle that reads current funnel counts from the database and sets each labeled gauge accordingly.

Code snippetpython
1from prometheus_client import Gauge, Counter, Histogram, start_http_server 2from dataclasses import dataclass 3from typing import Optional 4import time 5import logging 6 7logger = logging.getLogger(__name__) 8 9@dataclass 10class MetricCollectionResult: 11 success: bool 12 duration_seconds: float 13 error: Optional[str] = None 14 15class PlatformMetricsExporter: 16 def __init__(self, db_conn, redis_client, port: int = 9090): 17 self.db = db_conn 18 self.cache = redis_client 19 self.adoption_funnel = Gauge( 20 "platform_adoption_funnel_developers", 21 "Developer count at each adoption funnel stage", 22 ["stage"], 23 ) 24 self.satisfaction_nps = Gauge( 25 "platform_nps_score", "Current Net Promoter Score" 26 ) 27 self.collection_runs = Counter( 28 "platform_metric_collection_total", 29 "Total metric collection cycles", 30 ["status"], 31 ) 32 self.collection_duration = Histogram( 33 "platform_metric_collection_duration_seconds", 34 "Time spent collecting metrics per cycle", 35 ) 36 start_http_server(port) 37 38 def collect_adoption_metrics(self) -> MetricCollectionResult: 39 start = time.monotonic() 40 try: 41 with self.db.cursor() as cur: 42 cur.execute( 43 "SELECT stage, count FROM adoption_funnel_current_view" 44 ) 45 for stage, count in cur.fetchall(): 46 self.adoption_funnel.labels(stage=stage).set(count) 47 duration = time.monotonic() - start 48 self.collection_runs.labels(status="success").inc() 49 self.collection_duration.observe(duration) 50 return MetricCollectionResult(success=True, duration_seconds=duration) 51 except Exception as exc: 52 duration = time.monotonic() - start 53 self.collection_runs.labels(status="error").inc() 54 logger.error("Metric collection failed: %s", exc) 55 return MetricCollectionResult( 56 success=False, duration_seconds=duration, error=str(exc) 57 )

The collection_runs counter with status labels feeds the PromQL expression rate(platform_metric_collection_total{status="error"}[10m]) that you configure as a Grafana alert rule, triggering when the error rate exceeds 20% over a 10-minute window. The collection_duration histogram powers a separate alert on p99 latency, protecting against silent pipeline slowdowns that would cause Metric Staleness across your executive dashboards.

Confirm that the pipeline is healthy by opening Grafana, navigating to the provisioned adoption funnel dashboard, and verifying that each funnel stage panel displays a non-zero value with a timestamp updated within the last 60 seconds.

Do's and Don'ts

Having walked through the exporter implementation and its discipline-specific application, the following imperatives distil the highest-leverage operational habits for keeping the pipeline trustworthy in production.

Do's

  1. Do label every Prometheus metric with the dimensions you'll filter on in Grafanaplatform_adoption_funnel_developers uses a stage label so a single gauge covers all funnel positions; without labels you'd need separate metric names per stage and PromQL aggregations across them become impossible.
  2. Do separate the collection cycle cadence from the scrape interval — the PlatformMetricsExporter writes computed values to Redis every 60 seconds for operational metrics and every 15 minutes for aggregate KPIs, decoupling how often Prometheus scrapes /metrics from how often expensive DB queries run, so the exporter always serves fresh cached values without blocking on computation.
  3. Do provision Grafana dashboards as JSON code rather than configuring them through the UI — dashboard JSON is committed alongside the PlatformMetricsExporter so adoption funnel, feature heatmap, and NPS trend panels are reproducible across environments and can include pre-wired alert rules on rate(platform_metric_collection_total{status="error"}[10m]) without manual re-configuration.

Don'ts

  1. Don't let collection failures degrade silently — if collect_adoption_metrics raises an exception without incrementing collection_runs.labels(status="error") and returning a MetricCollectionResult, the rate(platform_metric_collection_total{status="error"}[10m]) PromQL expression never fires and stakeholder dashboards show stale adoption funnel values with no alert to signal the pipeline is broken.
  2. Don't block the ingestion API on metric computation — the architecture deliberately writes raw events to PostgreSQL in the ingestion layer and offloads all computation to the worker; merging those two steps means a slow aggregate KPI query during ingestion delays every incoming event write and creates backpressure that produces data gaps in the adoption funnel panels.
  3. Don't call start_http_server outside PlatformMetricsExporter.__init__ — starting the Prometheus HTTP server after metric objects are registered can cause a scrape to return an incomplete metric set, and calling it more than once raises a port-already-in-use error that silently prevents the /metrics endpoint from serving, breaking Prometheus scrapes for the entire pipeline.

This lesson is free to read. Its 3 hands-on labs — real code, in a cloud IDE — are part of the GenAI Platform Engineering subscription.

From · cancel anytime · Already a subscriber? Sign in →

More free lessons in AI Developer Platform Engineering

All free lessons in GenAI Platform Engineering