Introduction
Learn how to create a Flask application that displays random quotes and tracks the health and performance of the application with various internal metrics. By integrating Flask application with Prometheus and Grafana, we will demonstrate how to set up real-time application monitoring.
Flask application exposes several metrics that can be used to monitor various aspects of its performance, such as:
- Request Count: The total number of requests.
- Error Count: The number of errors.
- Health Status: Application health status.
- Resource Usage: Metrics capturing CPU usage, memory consumption, and uptime.
- Quote Count: Tracking how often each quote has been displayed.
These metrics will be exposed through a /metrics endpoint in the Flask application and Prometheus will periodically scrape this endpoint and store the metrics.
Once the data is collected, Grafana will be used to visualize the metrics and create custom dashboards providing a way to monitor the health of the application in real-time.
In other words, this setup showcases a real-world use case of end-to-end application monitoring using open-source tools like Prometheus and Grafana.
1. Setting up the Quote Application
The starting point is building the web application that will display quotes (along with author’s name and image) and then expose several metrics used to monitor overall application health and performance.
Flask is a web application framework designed to simplify the process of building web applications using Python. Flask philosophy revolves around simplicity, flexibility, built-in development server, etc. which makes it perfect fit for this blog’s purpose. Application will serve as the centerpiece of monitoring solution, allowing users to interact with it by displaying quotes, handling errors and exposing performance metrics.
1.1. Project Structure
The project consists of multiple components, from application execution to virtualisation components, and has the following structure:
Note: Find the code in the GitHub Repository.
Files and folders description:
- Dockerfile: Create application Docker image
- README.md: Projects information and explanations
- app.py: Main functionality of application
- docker-compose.yml: Create application container (along with Prometheus and Grafana containers)
- prometheus/prometheus.yml: Prometheus configuration file
- requirements.txt: List of packages and libraries required for running application
- static/images: Directory where images required for application are stored
- templates/index.html: Home page for application’s website
1.2. Application Endpoints
Application defines multiple specific URLs (API endpoints), that can be used to access a particular function or data:
Home Endpoint (/)
Main entry point of the application. When accessed, it serves an HTML page that displays a random quote (with author’s name and image) from a predefined list. The user can click on the displayed quote to view the next one in the sequence. Each time a quote is displayed, a metrics that stores the number of times that particular quote has been displayed is updated.
Metrics Endpoint(/metrics)
This endpoint exposes all collected metrics in a format compatible with Prometheus. Metrics include request counts, error counts, CPU and memory usage, uptime, and various others related to the application’s performance and resource consumption.
It is created to provide a centralized endpoint for collecting and exporting metrics (Prometheus scrapes this endpoint to gather data for analysis, alerting and visualization).
Health Check Endpoint (/health)
This endpoint returns the current health status of the application. It always responds with a custom status of “System is healthy”, along with a metric HEALTH_STATUS set to 1, indicating that the application is running as expected.
Quote Endpoint (/quote/<int:index>)
This endpoint returns the next quote in the sequence based on the provided index. If the next quote exists, endpoint returns a custom message “Quote counter incremented”. Each time a quote is retrieved, the corresponding counter for that quote is incremented.
1.3. Application Metrics
Metrics are key in order to make improvements to the system (application) and user experience as well as for maintaining expected uptime and performance. Metrics are used to measure and track the performance and overall health of Quote application:
| Request Count (REQUEST_COUNT) | Tracks the total number of requests received by the application. This metric increments every time an endpoint is accessed, allowing to monitor the overall usage and traffic to application. |
| Error Count (ERROR_COUNT) | Tracks the total number of errors that occur within the application. Each time an error is encountered, this counter is incremented. |
| Success Count (SUCCESSFUL_REQUESTS) | Tracks the number of successful requests made to the endpoint, indicating how often the processing task completes without errors. |
| Health Status (HEALTH_STATUS) | Represents the health status of the application. This metric can be set to 1 for healthy and 0 for unhealthy, reflecting the current state of the application. |
| CPU Usage (CPU_USAGE) | Tracks the percentage of CPU being utilized by the application. It provides a real-time view of how much processing power is being consumed. |
| Memory Usage Percent (MEMORY_USAGE) | Tracks the percentage of memory being utilized by the application. This metric provides insight into how much of the available memory is being consumed. |
| Memory Usage MB (MEMORY_USAGE_MB) | Tracks the memory usage of the application in megabytes. This metric provides a more precise measurement of memory consumption than the percentage. |
| Up Time (APP_TIME) | Tracks the total time (in seconds) that the application has been running since the last restart. |
| Quote Count (TOTAL_QUOTES_DISPLAYED) | Tracks the total number of quotes that have been displayed on the home page. Each time a quote is shown, this counter increments. |
| Individual Quote Count (quote_<index>_count) | Tracks the number of times each specific quote has been displayed. There is a counter for each quote, incremented whenever that quote is shown to the user. |
1.4. Running the Application
Application is containerized and depending on image creation, it can be created in two ways.
How to create the application:

Now that the Quote application is set up (serves quotes) and metrics for monitoring performance and health are exposed, application should be connected to Prometheus so that metrics can be scraped.
2. Setting up Prometheus
Prometheus is an open-source system monitoring and alerting tool. It collects and stores its metrics as time series data, i.e., metrics information is stored with the timestamp at which it was recorded, alongside optional key-value pairs called labels. It uses PromQL, a powerful query language for querying time series data.
Tip: More information on Prometheus can be found here.
2.1. Installing Prometheus in a Docker Network
Quote application is installed as Docker container, and located in Docker network called monitoring-network, so Prometheus will also be installed and used as Docker container:
Prometheus container uses last official Prometheus Docker image, and is exposed on standard Prometheus port (9090). To specify the scrape configuration the prometheus.yml file is created and bind mounted to default configuration file located in /etc/prometheus directory.
Since both services are connected to the same network (monitoring-network), the Prometheus container can communicate with the Quote application container and scrape its metrics.
Create the Prometheus container, by running the following command:
If everything is setup correctly, and there were no errors while creating a container, Prometheus UI will be available on http://localhost:9090.
2.2. Configuring Prometheus to Scrape Metrics
As mentioned above, a custom prometheus.yml file is created and bind mounted to default configuration file located in /etc/prometheus directory. Prometheus uses this file to determine where to scrape metrics from and how frequently to scrape them.
The content of prometheus.yml file:
Configuration file description:
- Configuration file tells Prometheus to scrape the Quote application metrics every 15 seconds.
- job_name: Specifies the name of the scraping job, which can be used in queries to filter metrics (related to this specific job).
- metrics_path: Defines the endpoint Prometheus will use to scrape metrics (in this case /metrics).
- targets: A list of targets from which Prometheus will collect metrics (in this case dc-quote-app; name of Quote application container).
2.3. Exploring Prometheus Metrics
Once the Prometheus is configured, queries can be used to check if Prometheus is scraping metrics correctly.
Check out these examples:
Now, when Prometheus is configured to scrape the Quote application metrics, the next step is to set up Grafana to visualize the metrics collected by Prometheus.
3. Setting up Grafana
Grafana is an open-source analytics andinteractive visualization web application that allows users to ingest data from various sources, query this data, and display it. Grafana comes with variety of visualization options, which are split into panels that are then used to build the Grafana dashboard.
Tip: More information on Grafana can be found here.
3.1. Installing Grafana in a Docker Network
Since Quote application and Prometheus are installed as Docker containers and placed in the same network to ensure proper communication, Grafana will also be installed as Docker container.
Grafana container uses last official Grafana Docker image, and is exposed on standard Grafana port (3000). A volume (/grafana) is used to store Grafana dashboards and datasources, ensuring that they persist when the container is restarted. Grafana will pull data from Prometheus and then create dashboard to visualize metrics.
Create the Grafana container, by running the following command:
If everything is setup correctly, and there were no errors while creating a container, Grafana UI will be available on http://localhost:3000.
Note: Grafana uses default credentials admin/admin, which can be changed on the first login.
3.2. Connecting Grafana to Prometheus
Grafana uses concept of datasource, which is basically any place from which Grafana can pull data; e.g. Prometheus, PostgreSQL, Elasticsearch, Datadog, etc.. In other words, there is no need to load the data into Grafana to analyze it.
In order for Grafana to pull data from Prometheus, a datasource must be created. Datasource can be created manually or using .yaml template.
Steps to manually create Prometheus datasource:
1. Login to the Grafana, using default credentials at http://localhost:3000.
2. In the sidebar select “Data Sources”, and click “Add data source”.
3. From the list of available data sources, select “Prometheus”.
4. In the HTTP section, set the URL to http://localhost:9090.
5. Click “Save & Test”to verify the connection.
Another way to create a datasource in Grafana is to use the .json template:
Once the datasource connection is verified, Grafana will be able to pull data from Prometheus.
4. Visualizing Quote Application Metrics in Grafana
Once the Prometheus is set as a data source, the next step is to create a dashboard to visualize the Quote application metrics.
Steps to create a dashboard (common steps for all panels):
1. In the Grafana sidebar, click “+” icon and select “Dashboard”
2. Click “Add new panel”
3. In the Query section, select “Prometheus” as the datasource.
Panels are grouped into rows, and visualized based on the common criteria (e.g. application status metrics, requests status metrics and quote related metrics).
4.1. Monitoring Application Health, Uptime and Resource Usage
All visualizations are created using Gauge or Time Series panels. Metrics used in application health status, uptime and resource usage visualizations:
- HEALTH_STATUS
- APP_TIME
- CPU_USAGE
- MEMORY_USAGE
- MEMORY_USAGE_MB
| Application Health Status | Query: app_health_status{instance=”dc-quote-app:5000″} Panel Specifications: Gauge, Calculation (Last*), Value mappings (0 -> Unhealthy, 1 -> Healthy) Description: Panel displays running status of Quote application |
| Application Uptime [sec] | Query: sum(app_uptime_seconds{instance=”dc-quote-app:5000″}) Panel Specifications: Gauge, Unit (Seconds) Description: Panel displays Quote application uptime |
| CPU Usage [%] | Query: sum(app_cpu_usage_percent{instance=”dc-quote-app:5000″}) Panel Specifications: Gauge, Calculation (Mean), Unit (Percent[0-100]), Threshold (80) Description: Panel displays CPU Usage(%) of Quote application |
| Memory Usage [%] | Query: sum(app_memory_usage_percent{instance=”dc-quote-app:5000″}) Panel Specifications: Gauge, Calculation (Mean), Unit (Percent[0-100]), Threshold (80) Description: Panel displays Memory Usage(%) of Quote application |
| Memory Usage [MB] | Query: sum(app_memory_usage_mb{instance=”dc-quote-app:5000″}) Panel Specifications: Gauge, Calculation (Mean), Unit (Kilobytes) Description: Panel displays Memory Usage(MB) of Quote application |
| CPU Usage over Time | Query: sum(app_cpu_usage_percent{instance=”dc-quote-app:5000″}) Panel Specifications: Time Series, Calculation (Mean), Unit (Percent[0-100]) Description: Panel displays CPU Usage (%) of Quote application in time period |
| Memory Usage over Time | Query: sum(app_memory_usage_percent{instance=”dc-quote-app:5000″}) Panel Specifications: Time Series, Calculation (Mean), Unit (Percent[0-100]) Description: Panel displays Memory Usage (%) of Quote application in time period |
| Memory Usage MB (MEMORY_USAGE_MB) | Tracks the memory usage of the application in megabytes. This metric provides a more precise measurement of memory consumption than the percentage. |
4.2. Visualizing Request and Error Metrics
All visualizations are created using Stat or Time Series panels. Metrics used in requests visualizations:
- REQUEST_COUNT
- ERROR_COUNT
- SUCCESSFUL_REQUESTS
| Error Requests | Query: sum(app_error_count_total{instance=”dc-quote-app:5000″}) Panel Specifications: Stat, Unit (Number), Calculation (Last*) Description: Panel displays total number of error requests |
| Successful Requests | Query: sum(app_successful_requests_total{instance=”dc-quote-app:5000″}) Panel Specifications: Stat, Unit (Number), Calculation (Last*) Description: Panel displays total number of successful requests |
| Error Requests Rate | Query: rate(app_error_count_total{instance=”dc-quote-app:5000″}[10m]) Panel Specifications: Time Series, Unit (Number), Calculation (Last*) Description: Panel displays error requests rate over time period |
| Successful Requests Rate | Query: rate(app_successful_requests_total{instance=”dc-quote-app:5000″}[10m] Panel Specifications: Time Series, Unit (Number), Calculation (Last*) Description: Panel displays successful requests rate over time period |
| Error Rate over Time [%] | Query A: rate(app_error_count_total{instance=”dc-quote-app:5000″}[1h]) (Hidden) Query B: rate(app_request_count_total{instance=”dc-quote-app:5000″}[1h]) (Hidden) Expression: ($A / $B) * 100 (Displayed) Panel Specifications: Time Series, Unit (Percent[0-100]) Description: Panel displays error rate (error and total requests ratio) in percentages over time period of last 1h |
| Success Rate over Time [%] | Query A: rate(app_successful_requests_total{instance=”dc-quote-app:5000″}[1h]) (Hidden) Query B: rate(app_request_count_total{instance=”dc-quote-app:5000″}[1h]) (Hidden) Expression: ($A / $B) * 100 (Displayed) Panel Specifications: Time Series, Unit (Percent[0-100]) Description: Panel displays success rate (success and total requests ratio) in percentages over time period of last 1h |
All visualizations are created using Stat, Time Series, and Bar Gauge panels. Metrics used in quotes visualizations:
- TOTAL_QUOTES_DISPLAYED
- quote_<index>_count
| Displayed Quotes | Query: sum(app_total_quotes_displayed_total{instance=”dc-quote-app:5000″}) Panel Specifications: Stat, Unit (Number), Calculation (Last*) Description: Panel displays total number of displayed quotes |
| Quotes Rate over Time | Query: sum(app_total_quotes_displayed_total{instance=”dc-quote-app:5000″}) Panel Specifications: Time Series, Unit (Number), Calculation (Last*) Description: Panel displays total number of displayed quotes over time period |
| Quotes Distribution |
Query: sum(quote_*_count_total{instance=”dc-quote-app:5000″}) Panel Specifications: Bar Gauge, Unit (Number), Calculation (Last*) Description: Panel displays total number of display by individual quote |
5. Scaling and Enhancing Application Monitoring
5.1. Scaling the Quote Application
Quote application currently has one function, which is to display random quote (with authors name and image), which makes it a pretty lightweight in terms of application. But if the functionality of Quote applications increases, application scaling could come in handy.
There are couple of ways to achieve application scaling, but since Quote application is already containerized, adding more instances it the simplest approach.
The first step is to ensure that Quote applications is stateless, which means that no persistent state should be held within the application itself.
To scale the existing Quote application, simply specify the number of replicas:

The deploy.replicas option under quote-app indicates that two instances of the Quote application should be created.

5.2. Scaling the Prometheus
As the number of Quote instances increases, a single Prometheus instance may not be able to handle the increased load. Similar for Quote application, there are several strategies for scaling Prometheus, and simplest approach is to use federation.
Federation allows a Prometheus server to scrape selected time series from another Prometheus instance. Basically, it allows scaling by deploying multiple Prometheus instances, each scraping a subset of Quote application metrics, and then aggregating the data into a single global instance.
First, update the docker-compose.yml file to deploy three instances of Prometheus (two instances that will monitor Quote applications, and a federate instance that will be used to aggregate the data):

5.3. Configuring Alerts and Notifications in Prometheus
Alerting with Prometheus is separated into two parts. Alerting rules in Prometheus servers send alerts to an Alertmanager. The Alertmanager then manages those alerts, including sending out notifications via multiple methods (e.g. email, chat platform, etc.).
The main steps to setting up alerting and notifications are:
- Setup and configure the Alertmanager
- Configure Prometheus to talk to the Alertmanager
- Create alerting rules in Prometheus
Add the following code snippet in docker-compose.yml file to add the Alertmanager into existing configuration:

New service for Alertmanager exposes its UI on port 9093 and overwrites the default configuration file in /etc/alertmanager directory. The custom alermanager.yml file contains configurations for routing alerts and handling notifications:

Note: Email configuration uses arbitrary values since email notifications were not tested!
Create the Prometheus container, by running the following command:

If everything is setup correctly, and there were no errors while creating a container, Alertmanager UI will be available on http://localhost:9093.

5.3.2. Configure Prometheus to talk to the Alertmanager
Once the Alertmanager is up and running, it needs to be connected with the Prometheus, which can be done by adding the alerting section to the existing Prometheus configuration file (prometheus.yml):

The alerting section defines where Prometheus sends alerts.
Once Alertmanager is up and running and connected to Prometheus, the last thing is to define the alerting rules. Create new file (e.g. alert_rules.yml) and define the rule to trigger an alert based on application resource usage (e.g. Memory Usage):

Explanation:
- Name: Common name for all alerts related to Quote application
- Alert: Name of the alert that will be triggered based on the defined parameters
- Expr: Expression that will be checked in Prometheus, which is basically used as a trigger for alert
- For: Time that Prometheus will wait until the alerts is triggered (expression must persist for 1 minute)
- Labels: Used for filtering and grouping
- Annotations: Detailed information about alert

Update the Prometheus volumes section in existing docker-compose.yml file to bind mount the custom rules file to rules file in the /etc/prometheus directory:

Update the default Prometheus configuration file (prometheus.yml) with the alert file, by defining rule_files section:

Once the alert is triggered, it will appear in Alertmanager on http://localhost:9093, in addition, pending or firing alerts can also be checked in Prometheus at http://localhost:9090/alerts.

Above configuration ensures that Quote application is continuously monitored, and alerts are generated for critical issues, providing notifications to take corrective actions.
5.4. Adding Advanced Metrics and Visualizations
So far, we talked about high-level metrics, like resource (CPU and Memory) usage, application uptime, etc., but capturing more granular metrics, such as latency, HTTP error rates, etc., provides deeper insights into the system’s behavior.
Latency Metric
Latency metric can be used to provide insight into how long it takes for Quote application to process request. To track the latency metric, update the app.py file with the following:

This adds a Histogram metric app_request_latency_seconds, which records the time it takes for each request to be processed. To visualize latency metric in Grafana, REQUEST_LATENCY metrics is used.
Requests Latency [sec]
- Query: avg(app_request_latency_seconds_count{instance=”dc-quote-app:5000″})
- Panel Specifications: Time Series, Unit (Seconds), Value (Mean)
- Description: Panel displays average request latency in seconds
HTTP Error Rate Metric
Error Rate metrics can be used to track the rate of errors (e.g. 404 HTTP responses), which enables to check the reliability of the Quote application. To track the error rate metric, update the app.py file with the following:

This increments a app_http_error_count counter every time an error response (status code 404) is returned by the Quote application.
To visualize error rate metric in Grafana, HTTP_ERROR_COUNT metrics is used.
HTTP Error Rate
- Query: sum(app_http_error_count_total{instance=”dc-quote-app:5000″})
- Panel Specifications: Bar Gauge, Unit (Number), Calculation (Last*)
- Description: Panel displays sum of errors, grouped by the status_code
Additional Improvements
Here is the list of additional improvements that can be made on the project:
| Integrating with Jaeger | Jaeger is open-source tracing system which can be used to trace requests in Quote application, allowing to pinpoint where bottlenecks occur. It can be easily integrated with Prometheus and Grafana. |
| Create Helm Charts | Creating Helm charts for all components can simplify deployment since Helm allows to package configurations into reusable charts. Charts can be versioned, which makes setup more maintainable. |
| Integration with other monitoring tools | Project setup can be integrated with Loki for log aggregation, Thanos for long-term storage and scaling, etc. |
Conclusion
In this blog, a Quote application that displays quotes and exposes operational metrics was created. Metrics like resources usage, application health status, uptime, number of successful and error requests, and the number of quotes displayed, were crucial for gaining visibility into the application’s health. Application stack was expanded with Prometheus, which was used for scraping application metrics and Grafana for visualizing the data.
In Grafana, several types of panels (Gauge, Stat, Time series, etc.) were used to monitor the metrics. Each visualization provided insights into the application’s real-time state, enabling to measure resource utilization, and evaluate the overall performance.
Application functionality increase is handled by implementing scaling operations on application itself as well as other components like scaling Prometheus through federation. Additionally, an Alertmanager was introduced to handle alerts. To identify slow responses and failure rates from application, advanced metrics like request latency and HTTP Error rates were implemented. These metrics are important in detecting performance bottlenecks, enhancing the application’s overall reliability.
Monitoring metrics is critical for building reliable and scalable applications. With the Prometheus and Grafana stack, users gain real-time visibility into the application’s performance and are able to respond quickly to incidents and optimize resource usage.
Future improvements like integrating distributed tracing with tools like Jaeger, log aggregation with tools like Loki, and managing deployments with Helm charts will further enhance this observability stack.
In conclusion, this blog pinpointed the power of combining application metrics with monitoring tools to build a strong foundation for understanding basic monitoring principles. Continuously tracking metrics, setting up effective alerting, and planning for scaling underscored the importance of having a monitoring and alerting system in place to ensure the reliability, performance, and scalability of application.


























