Skip to content

marcobreveglieri/prometheus-client-delphi

Repository files navigation

Prometheus Client for Delphi

Prometheus Client for Delphi

Instrument your Delphi applications with Prometheus metrics for modern observability

What is Prometheus?

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.

How Prometheus works

┌─────────────┐          ┌─────────────┐          ┌─────────────┐
│ Delphi App  │          │ Prometheus  │          │   Grafana   │
│             │          │   Server    │          │  Dashboard  │
│  /metrics   │ ◄─scrape─┤             │ ◄─query──┤             │
│  endpoint   │          │  (storage)  │          │  (display)  │
└─────────────┘          └─────────────┘          └─────────────┘
  1. Your Delphi app exposes a /metrics HTTP endpoint
  2. Prometheus periodically scrapes (pulls) metrics from your app
  3. Metrics are stored in a time-series database
  4. You query metrics using PromQL (Prometheus Query Language)
  5. Visualize data in Grafana dashboards
  6. Set up alerts when metrics exceed thresholds

Key Prometheus features

  • 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

Monitoring matters!

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.

Prometheus Delphi Client

The Prometheus Delphi Client library provides everything you need to instrument your Delphi applications with Prometheus metrics.

Features

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

Installation

Using Boss Package Manager

Boss is a dependency manager for Delphi. If you have Boss installed:

boss install marcobreveglieri/prometheus-client-delphi

Manual Installation

  1. Download or clone the repository from GitHub
  2. Add the Source folder 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

Quick Start Guide

Let's create a complete working example that tracks HTTP requests.

Step 1: Add Required Units

uses
  Prometheus.Collectors.Counter,
  Prometheus.Collectors.Histogram,
  Prometheus.Registry,
  Prometheus.Exposers.Text;

Step 2: Create and Register Metrics

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;

Step 3: Track Metrics in Your Application

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;

Step 4: Expose Metrics Endpoint

// 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;

Step 5: Configure Prometheus

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.

Metric Types

Counter

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;

Gauge

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;

Histogram

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.

Working with Labels

Labels add dimensions to your metrics, allowing you to slice and dice data in Prometheus queries.

Basic Label Usage

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;

Retrieving and Reusing Metrics

// 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;

Best Practices for Labels

  • 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.

Exporting Metrics

Metrics must be exposed via an HTTP endpoint for Prometheus to scrape them.

Basic Export to String

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

Delphi MVC Framework

The DMVC Prometheus Metrics middleware integrates with DelphiMVCFramework applications.

Installation:

boss install marcobreveglieri/dmvc-prometheus-metrics

The middleware automatically exposes metrics and can track requests, response times, and more.

Learn more: See Web Framework Integration for framework-specific guides.

HTTP Endpoint with Indy

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.

Web Framework Integration

For popular Delphi web frameworks, use these official middleware packages that automatically expose metrics:

Horse Framework

The Horse Prometheus Metrics middleware provides automatic metrics exposition and optional request tracking.

Installation:

boss install marcobreveglieri/horse-prometheus-metrics

Usage:

uses
  Horse,
  Horse.Prometheus;

begin
  // Automatically exposes /metrics endpoint
  THorse.Use(Prometheus);

  // Your routes here
  THorse.Get('/api/users', HandleGetUsers);

  THorse.Listen(9000);
end;

Real-World Examples

Complete HTTP Request Tracking

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;

Memory and Connection Pool Monitoring

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.

Documentation

Comprehensive documentation is available in the GitHub Wiki.

Delphi Compatibility

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? Solved!

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

Contributing

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.

Resources

Official Links

Prometheus Resources

Grafana Resources

Middleware Packages

Video Tutorials

License

This project is licensed under the MIT License - see the LICENSE file for details.


Made with ❤️ for the Delphi community

Report BugRequest FeatureDocumentation

> [!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!

About

Prometheus Client Library for Delphi

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages