| name | horse-telemetry-observability |
| description | Guidelines for registering, executing, and optimizing native high-precision telemetry hooks (AddOnTelemetry) in the Horse Web Framework. |
Horse Telemetry & Observability
Native Telemetry Hook (AddOnTelemetry)
Horse provides a high-precision, native, and Zero-Allocation telemetry hook based on stack-allocated TStopwatch. This hook allows monitoring and logging the total processing latency of all requests passing through the server pipeline.
The telemetry callback is defined as follows:
THorseOnTelemetry = {$IF DEFINED(FPC)}procedure{$ELSE}reference to procedure{$ENDIF}(const ARequest: THorseRequest; const AResponse: THorseResponse; const AExecutionTimeMS: Double);
Registering the Telemetry Hook
1. Global Registration
For applications using the standard static THorse facade, register the telemetry hook globally during the bootstrap process:
uses
Horse, System.SysUtils;
begin
THorse.AddOnTelemetry(
procedure(const Req: THorseRequest; const Res: THorseResponse; const ExecutionTimeMS: Double)
begin
Writeln(Format('[Telemetry] %s %s - Status: %d - Latency: %.2f ms',
[Req.Method, Req.PathInfo, Res.Status, ExecutionTimeMS]));
end);
THorse.Get('/ping',
procedure(Req: THorseRequest; Res: THorseResponse)
begin
Res.Send('pong');
end);
THorse.Listen(9000);
end.
2. Multi-Instance Registration
For applications utilizing THorseInstance, register telemetry hooks directly on each instance. Telemetry is fully isolated per port and polymorphically resolved based on the incoming request port:
var
LInstance1, LInstance2: THorseInstance;
begin
LInstance1 := THorseInstance.Create;
LInstance1.AddOnTelemetry(
procedure(const Req: THorseRequest; const Res: THorseResponse; const ExecutionTimeMS: Double)
begin
Writeln(Format('[Instance 1 - Port %d] Latency: %.2f ms', [Req.RawWebRequest.ServerPort, ExecutionTimeMS]));
end);
LInstance2 := THorseInstance.Create;
LInstance2.AddOnTelemetry(
procedure(const Req: THorseRequest; const Res: THorseResponse; const ExecutionTimeMS: Double)
begin
Writeln(Format('[Instance 2 - Port %d] Latency: %.2f ms', [Req.RawWebRequest.ServerPort, ExecutionTimeMS]));
end);
end;
Design Safeguards & AI Best Practices
- Catch Internal Exceptions: Always wrap the code inside custom telemetry callbacks with a
try-except block (or rely on Horse's native try-except boundary) to ensure failures during metrics collecting (like database logging or APM networking errors) never interrupt the HTTP response loop or crash the socket execution thread.
- Zero-Allocation Logging: To maintain Horse's zero-allocation characteristics, avoid dynamic heap allocations (such as concatenating strings or creating new logger objects) inside the telemetry callback. Prefer reusing static buffers or writing to stack-allocated variables.
- Multi-Instance Port Resolution: Always use
Req.RawWebRequest.ServerPort if you need to determine the active port of the incoming request dynamically inside the telemetry handler.
- FPC/Lazarus Compatibility: In FPC (Lazarus), the callback type is a standard procedural pointer. Do not use inline anonymous methods (
procedure begin end) when compiling libraries or handlers for Lazarus.