Instrument your Delphi applications with Prometheus metrics for modern observability
Prometheus is an open-source monitoring and alerting toolkit that has become the de-facto standard for cloud-native monitoring. Originally built at SoundCloud, it's now a graduated CNCF project used by organizations worldwide.
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Delphi App │ │ Prometheus │ │ Grafana │
│ │ │ Server │ │ Dashboard │
│ /metrics │ ◄─scrape─┤ │ ◄─query──┤ │
│ endpoint │ │ (storage) │ │ (display) │
└─────────────┘ └─────────────┘ └─────────────┘
- Your Delphi app exposes a
/metricsHTTP endpoint - Prometheus periodically scrapes (pulls) metrics from your app
- Metrics are stored in a time-series database
- You query metrics using PromQL (Prometheus Query Language)
- Visualize data in Grafana dashboards
- Set up alerts when metrics exceed thresholds
- Multi-dimensional data model - Metrics identified by name and labels (key-value pairs)
- Powerful query language (PromQL) - Flexible queries and aggregations
- Pull-based model - Prometheus scrapes your app (no agent needed)
- Service discovery - Automatic discovery of monitoring targets
- Built-in alerting - Alert manager for handling alerts
- Efficient storage - Local time-series database with optional remote storage
Modern applications need observability to understand their behavior in production. Without proper monitoring, you're flying blind:
- How many requests is your API handling? Without metrics, you won't know if you're at 10% or 90% capacity.
- Why did your application slow down? Response time metrics help identify performance bottlenecks.
- Is that new feature being used? Business metrics show real user behavior.
- When should you scale? Resource metrics (CPU, memory, connections) guide infrastructure decisions.
Observability is the practice of understanding your application's internal state by examining its outputs. Prometheus metrics are a core pillar of observability, alongside logs and traces.
The Prometheus Delphi Client library provides everything you need to instrument your Delphi applications with Prometheus metrics.
The Prometheus Delphi Client library offers a comprehensive set of features:
-
Standard Metric Types
- Counter - Cumulative metrics that only increase (requests, errors, etc.)
- Gauge - Metrics that can go up and down (memory usage, connections, etc.)
- Histogram - Distribution of values in configurable buckets (response times, sizes)
- Summary - (Under development) - Quantiles over sliding time windows
-
Label Support - Add context to metrics with key-value pairs for multi-dimensional data
-
Collector Registry - Centralized registry to manage multiple metrics efficiently
-
Text Format Exporter - Export metrics in Prometheus text-based exposition format
-
Thread-Safe Operations - Safe to use in multi-threaded and concurrent applications
-
Custom Collectors - Extend with your own metric types and collectors
-
Web Framework Integration - Ready-to-use middlewares for popular Delphi web frameworks
-
Best Practices Built-in - Follows Prometheus best practices for naming and usage
Boss is a dependency manager for Delphi. If you have Boss installed:
boss install marcobreveglieri/prometheus-client-delphi- Download or clone the repository from GitHub
- Add the
Sourcefolder to your project's search path:- Open Project > Options
- Navigate to Delphi Compiler > Search Path (or Resource Compiler > Directories and Conditionals > Include file search path for older versions)
- Add the path:
prometheus-client-delphi\Source
Example:
C:\Projects\prometheus-client-delphi\Source
Let's create a complete working example that tracks HTTP requests.
uses
Prometheus.Collectors.Counter,
Prometheus.Collectors.Histogram,
Prometheus.Registry,
Prometheus.Exposers.Text;var
LCounter: TCounter;
LDuration: THistogram;
procedure InitializeMetrics;
begin
// Counter for total requests
LCounter := TCounter.Create(
'http_requests_total',
'Total HTTP requests processed',
['method', 'path', 'status']
).Register();
// Histogram for request duration
LDuration := THistogram.Create(
'http_request_duration_seconds',
'HTTP request duration in seconds',
['method', 'path']
).Register();
end;procedure HandleRequest(const AMethod, APath: string; AStatusCode: Integer);
var
LStopwatch: TStopwatch;
begin
LStopwatch := TStopwatch.StartNew;
try
// Your request handling code here
ProcessRequest(AMethod, APath);
finally
LStopwatch.Stop;
// Record metrics
LCounter.Labels([AMethod, APath, IntToStr(AStatusCode)]).Inc();
LDuration.Labels([AMethod, APath]).Observe(LStopwatch.Elapsed.TotalSeconds);
end;
end;// Example using Indy HTTP Server
procedure HandleMetricsRequest(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
LExposer: TTextExposer;
begin
if ARequestInfo.Document = '/metrics' then
begin
LExposer := TTextExposer.Create;
try
AResponseInfo.ContentType := 'text/plain; charset=utf-8';
AResponseInfo.ContentText := LExposer.Render(
TCollectorRegistry.DefaultRegistry.Collect()
);
AResponseInfo.ResponseNo := 200;
finally
LExposer.Free;
end;
end;
end;Create prometheus.yml to scrape your application:
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'my-delphi-app'
static_configs:
- targets: ['localhost:9090']
metrics_path: '/metrics'Start Prometheus and visit http://localhost:9090 to query your metrics!
Next Steps: Check the Getting Started wiki page for more detailed tutorials.
A counter is a cumulative metric that only increases (or resets to zero on restart). Use counters for values that accumulate over time.
Use cases: Total requests, errors, tasks completed, bytes processed
uses
Prometheus.Collectors.Counter;
var
LCounter: TCounter;
begin
// Create and register
LCounter := TCounter.Create('app_requests_total', 'Total requests processed')
.Register();
// Increment by 1
LCounter.Inc();
// Increment by specific amount
LCounter.Inc(5);
end;With labels:
var
LCounter: TCounter;
begin
LCounter := TCounter.Create('http_requests_total', 'Total HTTP requests', ['method', 'status'])
.Register();
LCounter.Labels(['GET', '200']).Inc();
LCounter.Labels(['POST', '404']).Inc();
end;A gauge is a metric that can arbitrarily increase or decrease. Use gauges for values that represent current state.
Use cases: Memory usage, active connections, queue depth, temperature
uses
Prometheus.Collectors.Gauge;
var
LGauge: TGauge;
begin
// Create and register
LGauge := TGauge.Create('memory_usage_bytes', 'Current memory usage in bytes')
.Register();
// Increment and decrement
LGauge.Inc(); // +1
LGauge.Inc(100); // +100
LGauge.Dec(50); // -50
// Set to specific value
LGauge.SetTo(1024);
// Set to duration of operation
LGauge.SetDuration(
procedure
begin
// Your code here - gauge will be set to execution time in seconds
ProcessData();
end);
end;A histogram samples observations and counts them in configurable buckets. Histograms are ideal for measuring distributions like request durations or response sizes.
Use cases: Request/response durations, response sizes, query times
uses
Prometheus.Collectors.Histogram;
var
LHistogram: THistogram;
begin
// Create with default buckets
LHistogram := THistogram.Create(
'request_duration_seconds',
'HTTP request duration in seconds'
).Register();
// Record observations
LHistogram.Observe(0.25); // 250ms
LHistogram.Observe(0.5); // 500ms
LHistogram.Observe(1.2); // 1.2s
end;With custom buckets:
var
LHistogram: THistogram;
begin
// Custom buckets optimized for your use case
LHistogram := THistogram.Create(
'api_response_time_seconds',
'API response time',
[], // No labels
[0.01, 0.05, 0.1, 0.5, 1.0, 2.5, 5.0] // Custom buckets in seconds
).Register();
LHistogram.Observe(0.123);
end;Learn more: See Metric Types in the wiki for detailed information, including when to use each type.
Labels add dimensions to your metrics, allowing you to slice and dice data in Prometheus queries.
var
LCounter: TCounter;
begin
// Create metric with labels
LCounter := TCounter.Create(
'http_requests_total',
'Total HTTP requests',
['method', 'path', 'status'] // Label names
).Register();
// Increment specific label combinations
LCounter.Labels(['GET', '/api/users', '200']).Inc();
LCounter.Labels(['POST', '/api/orders', '201']).Inc();
LCounter.Labels(['GET', '/api/users', '404']).Inc();
end;// Later in your code, retrieve the metric from the registry
var
LCounter: TCounter;
begin
LCounter := TCollectorRegistry.DefaultRegistry
.GetCollector<TCounter>('http_requests_total');
if Assigned(LCounter) then
LCounter.Labels(['GET', '/api/products', '200']).Inc();
end;- Keep cardinality low - Avoid labels with unbounded values (user IDs, timestamps, etc.)
- Use consistent names - Standardize label names across metrics (
method,status,endpoint) - Don't overuse labels - 3-5 labels per metric is typically sufficient
- Label values should be bounded - Use categories, not unique identifiers
Learn more: See Working with Labels for advanced patterns and best practices.
Metrics must be exposed via an HTTP endpoint for Prometheus to scrape them.
uses
Prometheus.Exposers.Text,
Prometheus.Registry;
var
LExposer: TTextExposer;
LMetrics: string;
begin
LExposer := TTextExposer.Create;
try
LMetrics := LExposer.Render(
TCollectorRegistry.DefaultRegistry.Collect()
);
Writeln(LMetrics);
finally
LExposer.Free;
end;
end;Output example:
# HELP http_requests_total Total HTTP requests.
# TYPE http_requests_total counter
http_requests_total{method="GET",status="200"} 150
http_requests_total{method="POST",status="201"} 42
# HELP memory_usage_bytes Current memory usage.
# TYPE memory_usage_bytes gauge
memory_usage_bytes 1048576
The DMVC Prometheus Metrics middleware integrates with DelphiMVCFramework applications.
Installation:
boss install marcobreveglieri/dmvc-prometheus-metricsThe middleware automatically exposes metrics and can track requests, response times, and more.
Learn more: See Web Framework Integration for framework-specific guides.
uses
IdHTTPServer, IdContext, IdCustomHTTPServer,
Prometheus.Exposers.Text, Prometheus.Registry;
procedure TForm1.IdHTTPServer1CommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
LExposer: TTextExposer;
begin
if ARequestInfo.Document = '/metrics' then
begin
LExposer := TTextExposer.Create;
try
AResponseInfo.ContentType := 'text/plain; charset=utf-8';
AResponseInfo.ContentText := LExposer.Render(
TCollectorRegistry.DefaultRegistry.Collect()
);
AResponseInfo.ResponseNo := 200;
finally
LExposer.Free;
end;
end;
end;Learn more: See Exporting Metrics for more export methods, including streams, Windows services, and security best practices.
For popular Delphi web frameworks, use these official middleware packages that automatically expose metrics:
The Horse Prometheus Metrics middleware provides automatic metrics exposition and optional request tracking.
Installation:
boss install marcobreveglieri/horse-prometheus-metricsUsage:
uses
Horse,
Horse.Prometheus;
begin
// Automatically exposes /metrics endpoint
THorse.Use(Prometheus);
// Your routes here
THorse.Get('/api/users', HandleGetUsers);
THorse.Listen(9000);
end;uses
Prometheus.Collectors.Counter,
Prometheus.Collectors.Histogram,
System.Diagnostics;
type
THTTPMetrics = class
private
FRequestCounter: TCounter;
FDurationHistogram: THistogram;
public
constructor Create;
procedure TrackRequest(const AMethod, APath: string;
AStatus: Integer; ADuration: Double);
end;
constructor THTTPMetrics.Create;
begin
inherited;
FRequestCounter := TCounter.Create(
'http_requests_total',
'Total HTTP requests',
['method', 'path', 'status']
).Register();
FDurationHistogram := THistogram.Create(
'http_request_duration_seconds',
'HTTP request duration in seconds',
['method', 'path'],
[0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0] // Buckets
).Register();
end;
procedure THTTPMetrics.TrackRequest(const AMethod, APath: string;
AStatus: Integer; ADuration: Double);
begin
FRequestCounter.Labels([AMethod, APath, IntToStr(AStatus)]).Inc();
FDurationHistogram.Labels([AMethod, APath]).Observe(ADuration);
end;
// Usage in your application
var
GMetrics: THTTPMetrics;
procedure HandleHTTPRequest(ARequest: TRequest; AResponse: TResponse);
var
LStopwatch: TStopwatch;
begin
LStopwatch := TStopwatch.StartNew;
try
// Process your request
ProcessRequest(ARequest, AResponse);
finally
LStopwatch.Stop;
GMetrics.TrackRequest(
ARequest.Method,
ARequest.PathInfo,
AResponse.StatusCode,
LStopwatch.Elapsed.TotalSeconds
);
end;
end;uses
Prometheus.Collectors.Gauge;
type
TSystemMetrics = class
private
FMemoryUsage: TGauge;
FActiveConnections: TGauge;
public
constructor Create;
procedure UpdateMetrics;
end;
constructor TSystemMetrics.Create;
begin
FMemoryUsage := TGauge.Create(
'app_memory_bytes',
'Application memory usage in bytes'
).Register();
FActiveConnections := TGauge.Create(
'db_connections_active',
'Number of active database connections'
).Register();
end;
procedure TSystemMetrics.UpdateMetrics;
var
LMemStatus: TMemoryManagerState;
LMemUsage: Int64;
begin
// Update memory usage
GetMemoryManagerState(LMemStatus);
LMemUsage := LMemStatus.TotalAllocatedMediumBlockSize +
LMemStatus.TotalAllocatedLargeBlockSize;
FMemoryUsage.SetTo(LMemUsage);
// Update connection pool
FActiveConnections.SetTo(GetActiveConnectionCount());
end;
// Call UpdateMetrics() periodically (e.g., every 5 seconds via a timer)More examples: See Code Examples in the wiki for database monitoring, cache tracking, job queues, and more.
Comprehensive documentation is available in the GitHub Wiki.
Prometheus Client for Delphi requires Delphi 11 Alexandria or later versions.
The library leverages modern Delphi language features including:
- Inline variable declarations
- Anonymous methods
- Generics
- Advanced RTTI
While it may work with earlier Delphi versions with modifications, official support and testing target Delphi 11 Alexandria and newer releases.
| Problem | Solution |
|---|---|
| "How do I add metrics to my Delphi app?" | Ready-to-use metric types: Counter, Gauge, Histogram |
| "How do I export metrics in Prometheus format?" | Built-in text format exporter compatible with Prometheus |
| "How do I add context to my metrics?" | Label support for multi-dimensional metrics |
| "Is it thread-safe?" | Yes, all operations are thread-safe for multi-threaded apps |
| "How do I integrate with my web framework?" | Official middlewares for Horse and DMVC Framework |
| "Can I create custom metrics?" | Extensible architecture for custom collectors |
Contributions are welcome! Here's how you can help:
- Report bugs - Open an issue on GitHub Issues
- Suggest features - Share your ideas for improvements
- Submit pull requests - Fix bugs or add features
- Improve documentation - Help make the docs clearer and more comprehensive
- Share examples - Contribute real-world usage examples
See the Contributing Guide for more details.
- Horse Prometheus Metrics - Horse framework middleware
- DMVC Prometheus Metrics - Delphi MVC Framework middleware
- Using Delphi with Prometheus and Grafana (Italian)
This project is licensed under the MIT License - see the LICENSE file for details.
Made with ❤️ for the Delphi community
Report Bug • Request Feature • Documentation
> [!IMPORTANT] > > **Note**: This documentation has been generated with the assistance of LLMs (Claude Code). While we strive for accuracy, some information may be incorrect or incomplete. If you find errors or have improvements, please open a pull request on the [GitHub Wiki repository](https://github.com/marcobreveglieri/prometheus-client-delphi.wiki). Your contributions help make this documentation better for everyone!