# Horse Telemetry Observability

> Guidelines for registering, executing, and optimizing native high-precision telemetry hooks (AddOnTelemetry) in the Horse Web Framework.

- Skill: `hashload/horse-telemetry-observability` (Agent Skill)
- Install (CLI): `npx skillmds@latest add hashload/horse-telemetry-observability`
- Raw SKILL.md: https://api.skillmd.com/api/skills/hashload/horse-telemetry-observability/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: hashload (https://skillmd.com/u/hashload)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/hashload/horse-telemetry-observability

---


# 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:
```pascal
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:

```pascal
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:

```pascal
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

1. **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.
2. **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.
3. **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.
4. **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.

