2023-04-09 17:05:55 +02:00
|
|
|
unit MVCFramework.Middleware.Prometheus;
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
|
|
|
MVCFramework, System.SysUtils;
|
|
|
|
|
|
|
|
type
|
|
|
|
TMVCPrometheusMiddleware = class(TInterfacedObject, IMVCMiddleware)
|
|
|
|
private
|
|
|
|
fPrometheusRoute: String;
|
|
|
|
fOnBeforeMetricsGet: TProc;
|
|
|
|
protected
|
|
|
|
procedure OnAfterControllerAction(
|
|
|
|
AContext: TWebContext;
|
|
|
|
const AControllerQualifiedClassName: string; const AActionName: string;
|
|
|
|
const AHandled: Boolean);
|
|
|
|
procedure OnBeforeRouting(AContext: TWebContext; var AHandled: Boolean);
|
|
|
|
procedure OnBeforeControllerAction(AContext: TWebContext;
|
|
|
|
const AControllerQualifiedClassName: string; const AActionNAme: string;
|
|
|
|
var AHandled: Boolean);
|
|
|
|
procedure OnAfterRouting(AContext: TWebContext; const AHandled: Boolean);
|
|
|
|
public
|
|
|
|
constructor Create(const PrometheusRoute: String = '/metrics'; const OnBeforeMetricsGet: TProc = nil);
|
|
|
|
end;
|
|
|
|
|
2023-04-12 17:49:30 +02:00
|
|
|
{Simplified access to the most common Prometheus metrics}
|
2023-04-09 17:05:55 +02:00
|
|
|
Metrics = class sealed
|
|
|
|
public
|
2023-04-12 17:49:30 +02:00
|
|
|
// "Counter" metric
|
|
|
|
class procedure IncCounterValue(const MetricName: string; const Amount: Double = 1); overload;
|
|
|
|
class procedure IncCounterValue(const MetricName: string; const Amount: Double; const Labels: TArray<String>); overload;
|
|
|
|
// "Gauge" metric
|
2023-04-09 17:05:55 +02:00
|
|
|
class procedure SetGaugeValue(const MetricName: string; const Amount: Double = 1); overload;
|
|
|
|
class procedure SetGaugeValue(const MetricName: string; const Amount: Double; const Labels: TArray<String>); overload;
|
2023-04-12 17:49:30 +02:00
|
|
|
class procedure IncGaugeValue(const MetricName: string; const Amount: Double = 1); overload;
|
|
|
|
class procedure IncGaugeValue(const MetricName: string; const Amount: Double; const Labels: TArray<String>); overload;
|
|
|
|
class procedure DecGaugeValue(const MetricName: string; const Amount: Double = -1); overload;
|
|
|
|
class procedure DecGaugeValue(const MetricName: string; const Amount: Double; const Labels: TArray<String>); overload;
|
2023-04-09 17:05:55 +02:00
|
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
|
|
|
uses
|
|
|
|
System.Classes,
|
|
|
|
MVCFramework.Commons,
|
|
|
|
Prometheus.Registry,
|
|
|
|
Prometheus.Collectors.Counter,
|
|
|
|
Prometheus.Exposers.Text, Prometheus.Collectors.Gauge;
|
|
|
|
|
|
|
|
{ TMVCPrometheusMiddleware }
|
|
|
|
|
|
|
|
constructor TMVCPrometheusMiddleware.Create(const PrometheusRoute: String; const OnBeforeMetricsGet: TProc);
|
|
|
|
begin
|
|
|
|
inherited Create;
|
|
|
|
fPrometheusRoute := PrometheusRoute;
|
|
|
|
fOnBeforeMetricsGet := OnBeforeMetricsGet;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TMVCPrometheusMiddleware.OnAfterControllerAction(
|
|
|
|
AContext: TWebContext; const AControllerQualifiedClassName,
|
|
|
|
AActionName: string; const AHandled: Boolean);
|
|
|
|
begin
|
|
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TMVCPrometheusMiddleware.OnAfterRouting(AContext: TWebContext;
|
|
|
|
const AHandled: Boolean);
|
|
|
|
begin
|
2023-04-12 17:49:30 +02:00
|
|
|
Metrics.IncCounterValue('http_requests_handled', 1,
|
2023-04-09 17:05:55 +02:00
|
|
|
[AContext.Request.PathInfo, IntToStr(AContext.Response.StatusCode)]);
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TMVCPrometheusMiddleware.OnBeforeControllerAction(
|
|
|
|
AContext: TWebContext; const AControllerQualifiedClassName,
|
|
|
|
AActionName: string; var AHandled: Boolean);
|
|
|
|
begin
|
|
|
|
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TMVCPrometheusMiddleware.OnBeforeRouting(AContext: TWebContext;
|
|
|
|
var AHandled: Boolean);
|
|
|
|
begin
|
|
|
|
if not SameText(AContext.Request.PathInfo, fPrometheusRoute) then
|
|
|
|
begin
|
|
|
|
// Get the metric counter and increment it.
|
2023-04-12 17:49:30 +02:00
|
|
|
Metrics.IncCounterValue('http_requests_count');
|
2023-04-09 17:05:55 +02:00
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
if Assigned(fOnBeforeMetricsGet) then
|
|
|
|
begin
|
|
|
|
fOnBeforeMetricsGet();
|
|
|
|
end;
|
|
|
|
var lMetricsStream := TMemoryStream.Create;
|
|
|
|
AContext.Response.SetContentStream(lMetricsStream, TMVCMediaType.TEXT_PLAIN);
|
|
|
|
var LExposer := TTextExposer.Create;
|
|
|
|
try
|
|
|
|
LExposer.Render(lMetricsStream, TCollectorRegistry.DefaultRegistry.Collect);
|
|
|
|
finally
|
|
|
|
LExposer.Free;
|
|
|
|
end;
|
|
|
|
lMetricsStream.Position := 0;
|
|
|
|
AHandled := True;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ Metrics }
|
|
|
|
|
2023-04-12 17:49:30 +02:00
|
|
|
class procedure Metrics.IncCounterValue(const MetricName: string; const Amount: Double);
|
2023-04-09 17:05:55 +02:00
|
|
|
begin
|
|
|
|
TCollectorRegistry
|
|
|
|
.DefaultRegistry
|
|
|
|
.GetCollector<TCounter>(MetricName)
|
|
|
|
.Inc(Amount);
|
|
|
|
end;
|
|
|
|
|
2023-04-12 17:49:30 +02:00
|
|
|
class procedure Metrics.DecGaugeValue(const MetricName: string;
|
|
|
|
const Amount: Double; const Labels: TArray<String>);
|
|
|
|
begin
|
|
|
|
TCollectorRegistry.DefaultRegistry
|
|
|
|
.GetCollector<TGauge>(MetricName)
|
|
|
|
.Labels(Labels)
|
|
|
|
.Dec(Amount);
|
|
|
|
end;
|
|
|
|
|
|
|
|
class procedure Metrics.DecGaugeValue(const MetricName: string;
|
|
|
|
const Amount: Double);
|
|
|
|
begin
|
|
|
|
TCollectorRegistry.DefaultRegistry
|
|
|
|
.GetCollector<TGauge>(MetricName)
|
|
|
|
.Dec(Amount);
|
|
|
|
end;
|
|
|
|
|
|
|
|
class procedure Metrics.IncCounterValue(const MetricName: string; const Amount: Double;
|
2023-04-09 17:05:55 +02:00
|
|
|
const Labels: TArray<String>);
|
|
|
|
begin
|
|
|
|
TCollectorRegistry
|
|
|
|
.DefaultRegistry
|
|
|
|
.GetCollector<TCounter>(MetricName)
|
|
|
|
.Labels(Labels)
|
|
|
|
.Inc(Amount);
|
|
|
|
end;
|
|
|
|
|
2023-04-12 17:49:30 +02:00
|
|
|
class procedure Metrics.IncGaugeValue(const MetricName: string;
|
|
|
|
const Amount: Double);
|
|
|
|
begin
|
|
|
|
TCollectorRegistry.DefaultRegistry
|
|
|
|
.GetCollector<TGauge>(MetricName)
|
|
|
|
.Inc(Amount);
|
|
|
|
end;
|
|
|
|
|
|
|
|
class procedure Metrics.IncGaugeValue(const MetricName: string;
|
|
|
|
const Amount: Double; const Labels: TArray<String>);
|
|
|
|
begin
|
|
|
|
TCollectorRegistry.DefaultRegistry
|
|
|
|
.GetCollector<TGauge>(MetricName)
|
|
|
|
.Labels(Labels)
|
|
|
|
.Inc(Amount);
|
|
|
|
end;
|
|
|
|
|
2023-04-09 17:05:55 +02:00
|
|
|
class procedure Metrics.SetGaugeValue(const MetricName: string;
|
|
|
|
const Amount: Double; const Labels: TArray<String>);
|
|
|
|
begin
|
|
|
|
TCollectorRegistry.DefaultRegistry
|
|
|
|
.GetCollector<TGauge>(MetricName)
|
|
|
|
.Labels(Labels)
|
|
|
|
.SetTo(Amount);
|
|
|
|
end;
|
|
|
|
|
|
|
|
class procedure Metrics.SetGaugeValue(const MetricName: string;
|
|
|
|
const Amount: Double);
|
|
|
|
begin
|
|
|
|
TCollectorRegistry.DefaultRegistry
|
|
|
|
.GetCollector<TGauge>(MetricName)
|
|
|
|
.SetTo(Amount);
|
|
|
|
end;
|
|
|
|
|
|
|
|
end.
|