Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .agents/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,8 @@ Este documento estabelece as regras de design e desenvolvimento do framework Hor
* Ao projetar ou atualizar middlewares do ecossistema, garanta que eles não dependam de dados em variáveis globais ou estáticas (`class var` singletons) do core, permitindo que cada instância de `THorseInstance` configure isoladamente suas dependências, rotas e manipuladores.
* Para preservar a compatibilidade de compilação cruzada multiplataforma FPC/Lazarus, evite o uso de closures ou procedimentos anônimos inline (`procedure begin end`) em manipuladores de ciclo de vida e rotas lógicas locais das instâncias do Horse, preferindo procedimentos regulares e delegados de objetos.

## 🟢 Observabilidade e Ganchos de Telemetria (Telemetry Hooks)
* O Horse possui infraestrutura nativa e de baixíssimo overhead baseada em `TStopwatch` para rastreamento de latência em requisições.
* Ao estender o ecossistema ou criar novos middlewares de APM/observabilidade (como Prometheus, OpenTelemetry, logging), use sempre o gancho nativo `AddOnTelemetry` (`THorse.AddOnTelemetry` ou `LInstance.AddOnTelemetry`) em vez de introduzir wrappers customizados nos blocos de execução de rotas ou temporizadores ad-hoc que geram overhead de heap.
* Garanta que os callbacks registrados em `AddOnTelemetry` sejam protegidos internamente com blocos `try-except` individuais (silenciando exceções) para assegurar que falhas na coleta de telemetria nunca causem interrupções no fluxo principal de retorno HTTP do cliente ou derrubem a thread de execução do socket.

7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,13 @@ These are middlewares focused on application observability, metrics, and tracing
| [regyssilveira/horse-opentelemetry](https://github.com/regyssilveira/horse-opentelemetry) |    ✔️ |     ✔️ |
| [regyssilveira/horse-prometheus](https://github.com/regyssilveira/horse-prometheus) |    ✔️ |     ✔️ |

## 🤖 AI Agent Skills

This repository includes native instructions and model skills designed to guide AI agents (such as Gemini, Claude, ChatGPT, and GitHub Copilot) during development.

* **AI Guidelines:** See [.agents/AGENTS.md](./.agents/AGENTS.md) for core architectural rules on dependency injection, lifecycle hooks, concurrency, and telemetry.
* **Agent Skills:** See [doc/skills/README.md](./doc/skills/README.md) for the complete directory of optimized AI skills and development guides.

## Delphi Versions

`Horse` works with Delphi 13 Florence, Delphi 12 Athens, Delphi 11 Alexandria, Delphi 10.4 Sydney, Delphi 10.3 Rio, Delphi 10.2 Tokyo, Delphi 10.1 Berlin, Delphi 10 Seattle, Delphi XE8 and Delphi XE7.
Expand Down
7 changes: 7 additions & 0 deletions README.pt-BR.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,13 @@ Estes são middlewares focados em observabilidade, métricas e rastreamento de a
| [regyssilveira/horse-opentelemetry](https://github.com/regyssilveira/horse-opentelemetry) |    ✔️ |     ✔️ |
| [regyssilveira/horse-prometheus](https://github.com/regyssilveira/horse-prometheus) |    ✔️ |     ✔️ |

## 🤖 Habilidades de IA (Agent Skills)

Este repositório possui suporte nativo para agentes de inteligência artificial (como Gemini, Claude, ChatGPT e GitHub Copilot) por meio de regras locais e guias de modelagem (skills).

* **Diretrizes de IA:** Veja [.agents/AGENTS.md](./.agents/AGENTS.md) contendo os padrões arquiteturais de injeção de dependência, lifecycle hooks, concorrência e telemetria.
* **Skills de IA:** Veja [doc/skills/README.pt-BR.md](./doc/skills/README.pt-BR.md) para a lista completa de habilidades de IA (Agent Skills) e guias de desenvolvimento assistido.

## Versões do Delphi

O `Horse` funciona com Delphi 13 Florence, Delphi 12 Athens, Delphi 11 Alexandria, Delphi 10.4 Sydney, Delphi 10.3 Rio, Delphi 10.2 Tokyo, Delphi 10.1 Berlin, Delphi 10 Seattle, Delphi XE8 e Delphi XE7.
Expand Down
11 changes: 5 additions & 6 deletions doc/roadmap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,7 @@ Este documento detalha o planejamento de melhorias arquiteturais de longo prazo
## 🗺️ Roadmap de Evolução Arquitetural (Pendente)


### 6. Ganchos de Telemetria Padronizados (Observabilidade / OpenTelemetry)
* **Descrição:** Disponibilizar ganchos internos no Core para extração de latência, volumetria de requests e status HTTP sem perdas de performance.
* **Ganhos:**
* Integração nativa facilitada com coletores de métricas do ecossistema APM (como Prometheus e Jaeger).

### 7. Roteamento Avançado (Regex e Parâmetros Opcionais)
### 6. Roteamento Avançado (Regex e Parâmetros Opcionais)
* **Descrição:** Permitir parâmetros opcionais (`/users/:id?`) e restrições de rotas baseadas em Expressões Regulares (`/users/:id(\d+)`) na árvore do Radix Router.


Expand Down Expand Up @@ -71,6 +66,10 @@ Este documento detalha o planejamento de melhorias arquiteturais de longo prazo
* **Status:** 🟢 **Concluído e Liberado como Middleware**
* **Implementação:** Desenvolvido o middleware oficial [horse-dto](https://github.com/regyssilveira/horse-dto) para realizar a desserialização automática de payloads de requisições (JSON, Query params, Route params e Form fields) diretamente para classes DTO em Delphi e Lazarus, executando validações declarativas robustas baseadas em atributos customizados (como `[Required]`, `[Email]`, `[Range]`, `[CustomValidator]`) antes que a requisição seja entregue aos controllers lógicos da aplicação, eliminando código repetitivo (boilerplate) de forma isolada e elegante.

### 14. Ganchos de Telemetria Padronizados (Observabilidade Nativa)
* **Status:** 🟢 **Concluído e Liberado**
* **Implementação:** Disponibilizada a infraestrutura nativa e de baixíssimo overhead (`THorse.AddOnTelemetry` e `LInstance.AddOnTelemetry`) para interceptação automática e medição de latência baseada em `TStopwatch` (stack-allocated / zero-allocation). Totalmente integrado de forma fail-safe ao pipeline de roteamento (`Radix` e `Tree`), provendo suporte polimórfico a ganchos isolados no Multi-Instance e mantendo 100% de retrocompatibilidade com o ecossistema de middlewares.

---

## ✅ Entregas Recentes de Testes & CI/CD (Concluído)
Expand Down
2 changes: 1 addition & 1 deletion doc/roadmap/prioritization_matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Esta tabela classifica as 13 melhorias pendentes do roadmap técnico do Horse co
| 1 | **Refatoração Multi-Instance** | Arquitetura | 5 | 4 | **1.25** | ⚙️ **Transparente** (Mantém retrocompatibilidade) | 🟢 **Concluído** (Implementado e Liberado) |
| 6 | **DTO Auto-Binding & Validação** | DX / Produtividade | 5 | 4 | **1.25** | ➕ **Novo Middleware** (Opcional) | 🟢 **Concluído** (Implementado e Liberado como Middleware) |
| 2 | **Pool de Buffers (MemoryBufferPool)** | Otimização | 4 | 4 | **1.00** | ⚙️ **Transparente** (Performance por baixo dos panos) | 🟢 **Concluído** (Implementado e Liberado) |
| 7 | **Ganchos OpenTelemetry/APM** | Observabilidade | 3 | 3 | **1.00** | ⚙️ **Transparente / Opcional** | **Segunda prioridade** |
| 7 | **Ganchos OpenTelemetry/APM** | Observabilidade | 3 | 3 | **1.00** | ⚙️ **Transparente / Opcional** | 🟢 **Concluído** (Implementado e Liberado) |
| 8 | **Roteamento Regex e Opcionais** | Recursos | 4 | 5 | **0.80** | ➕ **Novo Recurso** (Opcional) | 🔬 **Alta Complexidade** (Exige reescrever árvore Radix) |

---
Expand Down
1 change: 1 addition & 0 deletions doc/skills/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
| **horse-minimal-api** | [`horse-minimal-api/SKILL.md`](./horse-minimal-api/SKILL.md) | Building rapid, low-boilerplate microservices and mock APIs inside single-file bootstrap models. |
| **horse-dependency-injection** | [`horse-dependency-injection/SKILL.md`](./horse-dependency-injection/SKILL.md) | Managing request-scoped contextual services and IoC (dependency injection) in Delphi and Lazarus. |
| **horse-multi-instance** | [`horse-multi-instance/SKILL.md`](./horse-multi-instance/SKILL.md) | Running or configuring multiple independent server instances (THorseInstance) concurrently inside the same application process. |
| **horse-telemetry-observability** | [`horse-telemetry-observability/SKILL.md`](./horse-telemetry-observability/SKILL.md) | Registering, configuring, and optimizing native telemetry callbacks (AddOnTelemetry) for APM tools and logging. |

---

Expand Down
1 change: 1 addition & 0 deletions doc/skills/README.pt-BR.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
| **horse-minimal-api** | [`horse-minimal-api/SKILL.md`](./horse-minimal-api/SKILL.md) | Desenvolvimento rápido de microsserviços focados, mocks e APIs de arquivo único estruturadas com baixo boilerplate. |
| **horse-dependency-injection** | [`horse-dependency-injection/SKILL.md`](./horse-dependency-injection/SKILL.md) | Gerenciamento de ciclo de vida e IoC no request scope (injeção de dependência) em Delphi e Lazarus. |
| **horse-multi-instance** | [`horse-multi-instance/SKILL.md`](./horse-multi-instance/SKILL.md) | Execução ou configuração de múltiplos servidores de instâncias independentes (THorseInstance) concorrentemente dentro do mesmo processo. |
| **horse-telemetry-observability** | [`horse-telemetry-observability/SKILL.md`](./horse-telemetry-observability/SKILL.md) | Registro, configuração e otimização de ganchos nativos de telemetria (AddOnTelemetry) para ferramentas APM e logs. |

---

Expand Down
75 changes: 75 additions & 0 deletions doc/skills/horse-telemetry-observability/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
---
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:
```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.
76 changes: 76 additions & 0 deletions doc/telemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,85 @@ begin

// Metrics scraper endpoint on a private port
// Note: Running multiple Horse instances is supported.
end;
```

---

## Native Telemetry Hooks

Horse introduces a native telemetry hook of extremely high precision and *Zero-Allocation*, based on `TStopwatch` (stack-allocated).

This feature allows you to monitor and measure the latency of all processed HTTP requests with millisecond precision, enabling easy integration with logging, APM (Application Performance Monitoring) tools, and custom observability collectors.

### Callback Signature

The telemetry callback type is defined as follows:

```delphi
THorseOnTelemetry = {$IF DEFINED(FPC)}procedure{$ELSE}reference to procedure{$ENDIF}(const ARequest: THorseRequest; const AResponse: THorseResponse; const AExecutionTimeMS: Double);
```

### Setting Up the Hook Globally

You can register a global callback that will be triggered at the end of all HTTP requests in the application:

```delphi
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.
```

### Instance Isolation (Multi-Instance)

If your application uses the Multi-Instance architecture (`THorseInstance`), you can register telemetry hooks isolated by port/instance. Horse polymorphically resolves the correct instance associated with the active request:

```delphi
uses
Horse, System.SysUtils;

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);
LInstance1.Get('/service1', ...);

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);
LInstance2.Get('/service2', ...);
end.
```

### Performance Guarantee

* **Zero-Allocation:** Time tracking utilizes `TStopwatch` allocated directly on the thread stack, generating no pressure on the Garbage Collector (FPC/Lazarus) or memory heap allocation stress in Delphi.
* **Security & Isolation:** The telemetry hook is triggered synchronously within the `finally` block of the physical routing, ensuring that the total time captures middlewares, route processing, and any error generated in the pipeline.

---

## See Also
Expand Down
76 changes: 76 additions & 0 deletions doc/telemetry.pt-BR.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,82 @@ end.

---

## Ganchos de Telemetria Nativos (Native Telemetry Hooks)

O Horse introduz um gancho de telemetria nativo de altíssima precisão e sem alocação de memória (*Zero-Allocation*), baseado em `TStopwatch` (stack-allocated).

Este recurso permite monitorar e medir com precisão milimétrica a latência de todas as requisições HTTP processadas, permitindo a fácil integração de logs, ferramentas APM (Application Performance Monitoring) e coletores customizados de observabilidade.

### Assinatura do Callback

O tipo do callback de telemetria é definido da seguinte forma:

```delphi
THorseOnTelemetry = {$IF DEFINED(FPC)}procedure{$ELSE}reference to procedure{$ENDIF}(const ARequest: THorseRequest; const AResponse: THorseResponse; const AExecutionTimeMS: Double);
```

### Configurando o Gancho Globalmente

Você pode registrar um callback global que será disparado ao término de todas as requisições HTTP da aplicação:

```delphi
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.
```

### Isolamento por Instância (Multi-Instance)

Se sua aplicação utiliza a arquitetura Multi-Instance (`THorseInstance`), você pode registrar ganchos de telemetria isolados por porta/instância. O Horse resolve polimorficamente a instância correta associada à requisição ativa:

```delphi
uses
Horse, System.SysUtils;

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);
LInstance1.Get('/service1', ...);

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);
LInstance2.Get('/service2', ...);
end.
```

### Garantia de Performance

* **Zero-Allocation:** O controle de tempo utiliza `TStopwatch` alocado diretamente na stack da thread, não gerando pressão no Garbage Collector (FPC/Lazarus) ou estresse de heap/alocação de memória no Delphi.
* **Segurança e Isolamento:** O gancho de telemetria é acionado de forma síncrona dentro da seção `finally` do roteamento físico, garantindo que o tempo total capture middlewares, processamento da rota e qualquer erro gerado no pipeline.

---

## Veja Também
- [Ecossistema de Middlewares](./middleware-ecosystem.pt-BR.md)
- [Criando um Middleware](./writing-middleware.pt-BR.md)
3 changes: 3 additions & 0 deletions samples/delphi/console_complete/ConsoleComplete.dpr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ uses
{$ENDIF}
System.Classes,
System.SysUtils,
{$IFNDEF FPC}
Web.ReqMulti,
{$ENDIF}
Horse,
Horse.Commons;

Expand Down
Loading