Performance Monitoring¶
Sistema centralizado de monitoramento de performance com limiares configuráveis por tipo, namespace e categoria.
Visão Geral¶
O monitoramento de performance utiliza uma classe de opções singleton (PerformanceMonitorOptions) que define limiares de tempo para diferentes categorias de operações. Cada operação é medida com Stopwatch e logada conforme o tempo decorrido em relação aos limiares configurados.
Níveis de log:
| Condição | Nível |
|---|---|
elapsed >= WarningMs |
Warning |
elapsed >= SlowMs |
Information |
elapsed < SlowMs |
Debug |
Arquivos Principais¶
| Arquivo | Descrição |
|---|---|
src/Ciba.Infrastructure/Configuration/PerformanceMonitorOptions.cs |
Classe de opções com API fluente e resolução de limiares |
src/Ciba.Api/Extensions/PerformanceMonitoringExtensions.cs |
Configuração dos 28 overrides para operações naturalmente lentas |
src/Ciba.Infrastructure/Behaviors/LoggingBehavior.cs |
MediatR behavior que monitora handlers |
src/Ciba.Infrastructure/Behaviors/SlowHttpClientHandler.cs |
DelegatingHandler para HTTP clients |
src/Ciba.Infrastructure/Persistence/Interceptors/SlowQueryInterceptor.cs |
EF Core interceptor para queries |
src/Ciba.Api/_Shared/Pipelines/Pipeline.cs |
Pipeline genérico com timing por step e total |
src/Ciba.Infrastructure/Services/Storage/R2StorageService.cs |
Instrumentação de timing para storage R2 |
PerformanceMonitorOptions¶
Limiares por Categoria (Defaults)¶
public ThresholdPair Handlers { get; set; } = new(500, 2_000);
public ThresholdPair Queries { get; set; } = new(200, 1_000);
public ThresholdPair Steps { get; set; } = new(500, 2_000);
public ThresholdPair Pipelines { get; set; } = new(2_000, 5_000);
public ThresholdPair HttpClients { get; set; } = new(1_000, 5_000);
public ThresholdPair Storage { get; set; } = new(1_000, 5_000);
| Categoria | Slow (ms) | Warning (ms) | Consumidores |
|---|---|---|---|
| Handlers | 500 | 2.000 | LoggingBehavior (MediatR) |
| Queries | 200 | 1.000 | SlowQueryInterceptor (EF Core) |
| Steps | 500 | 2.000 | Pipeline<TContext> (por step) |
| Pipelines | 2.000 | 5.000 | Pipeline<TContext> (total) |
| HttpClients | 1.000 | 5.000 | SlowHttpClientHandler |
| Storage | 1.000 | 5.000 | R2StorageService |
API Fluente¶
// Override por tipo exato (handler, step, request DTO)
perf.Override<PlaygroundChatRequest>(3_000, 15_000);
// Override por prefixo de namespace
perf.OverrideNamespace("Ciba.Api.Features.Webhook", 3_000, 10_000);
// Override por nome de HTTP client
perf.OverrideHttpClient("Claude", 8_000, 20_000);
Prioridade de Resolução¶
1. Tipo exato → Override<T>() registrado com FullName do tipo
2. Namespace → OverrideNamespace() mais específico (prefixo mais longo vence)
3. Default → Limiar padrão da categoria (ex: options.Handlers)
Para HTTP clients, a resolução é separada:
Overrides Configurados¶
Configuração em PerformanceMonitoringExtensions.AddPerformanceMonitoringDefaults():
HTTP Clients (5 overrides)¶
| Client | Slow (ms) | Warning (ms) | Motivo |
|---|---|---|---|
| Claude | 8.000 | 20.000 | LLM streaming/completion |
| OpenAI | 6.000 | 15.000 | LLM completion |
| Whisper | 10.000 | 30.000 | Transcrição de áudio |
| Embedding | 5.000 | 15.000 | Geração de embeddings |
| Evolution | 1.500 | 5.000 | WhatsApp API |
Pipeline Steps (8 overrides)¶
| Step | Slow (ms) | Warning (ms) | Motivo |
|---|---|---|---|
InvokeAiServiceStep |
3.000 | 12.000 | Chamada ao LLM |
InvokeDeferredAiStep |
3.000 | 12.000 | Chamada ao LLM (deferred) |
ProcessMediaStep |
2.000 | 10.000 | Download + OCR/transcrição |
ApplyResponseDelayStep |
5.000 | 15.000 | Delay intencional de digitação |
SendWhatsAppResponseStep |
800 | 3.000 | Envio via Evolution API |
SendAttachmentsStep |
1.000 | 5.000 | Upload de anexos |
SendDeferredResponseStep |
800 | 3.000 | Envio via Evolution API (deferred) |
ResolveConversationStep |
800 | 3.000 | Resolução de conversa |
Handlers: Playground (3 overrides)¶
| Request | Slow (ms) | Warning (ms) | Motivo |
|---|---|---|---|
PlaygroundChatRequest |
3.000 | 15.000 | LLM + embeddings + vision |
PlaygroundTranscribeRequest |
2.000 | 10.000 | Whisper transcription |
PlaygroundDescribeImageRequest |
2.000 | 8.000 | Vision API |
Handlers: Knowledge & FAQ (4 overrides)¶
| Request | Slow (ms) | Warning (ms) | Motivo |
|---|---|---|---|
CreateKnowledgeBlockRequest |
1.500 | 6.000 | Chunking + embeddings |
UpdateKnowledgeBlockRequest |
1.500 | 6.000 | Chunking + embeddings |
CreateFaqItemRequest |
800 | 2.500 | Embedding geração |
UpdateFaqItemRequest |
800 | 2.500 | Embedding geração |
Handlers: Agent/Step (4 overrides)¶
| Request | Slow (ms) | Warning (ms) | Motivo |
|---|---|---|---|
CreateAgentRequest |
1.500 | 5.000 | Prompt optimization via LLM |
UpdateAgentRequest |
1.500 | 5.000 | Prompt optimization via LLM |
CreateAgentStepRequest |
1.500 | 5.000 | Prompt optimization via LLM |
UpdateAgentStepRequest |
1.500 | 5.000 | Prompt optimization via LLM |
Handlers: WhatsApp + Storage (3 overrides)¶
| Request | Slow (ms) | Warning (ms) | Motivo |
|---|---|---|---|
SendMessageRequest |
1.000 | 4.000 | Envio via Evolution API |
UploadAttachmentRequest |
1.000 | 5.000 | Upload para R2 |
ConnectInstanceRequest |
1.000 | 3.500 | Conexão WhatsApp |
Consumidores¶
LoggingBehavior (MediatR Handlers)¶
Registrado como IPipelineBehavior<TRequest, TResponse>, monitora todos os handlers MediatR:
Resolve o limiar usando o tipo do request DTO (ex: PlaygroundChatRequest), com fallback para options.Handlers.
SlowQueryInterceptor (EF Core)¶
DbCommandInterceptor que monitora todas as queries SQL:
Não suporta overrides por tipo (queries são genéricas).
SlowHttpClientHandler (HTTP Clients)¶
DelegatingHandler adicionado via extension method:
Resolve o limiar pelo nome do client:
Pipeline (Steps e Pipelines)¶
Monitora cada step individualmente e o pipeline como um todo:
// Por step: resolve pelo tipo do step
var thresholds = _options.Resolve(stepType, _options.Steps);
// Total do pipeline: usa default de Pipelines
var thresholds = _options.Pipelines;
R2StorageService (Storage)¶
Monitora upload e fetch de arquivos:
Registro no DI¶
A configuração deve ser registrada antes de AddInfrastructure() para que todos os consumidores recebam as opções:
// Program.cs
builder.Services.AddPerformanceMonitoringDefaults(); // Registra singleton
builder.Services.AddInfrastructure(builder.Configuration); // Consumidores recebem via DI
// Infrastructure ServiceCollectionExtensions.cs
public static IServiceCollection AddPerformanceMonitoring(
this IServiceCollection services,
Action<PerformanceMonitorOptions>? configure = null)
{
var options = new PerformanceMonitorOptions();
configure?.Invoke(options);
services.AddSingleton(options);
return services;
}
Regras¶
- Só adicionar override quando necessário -- operações que são naturalmente mais lentas que o default da categoria
- Overrides por tipo usam o
FullNamedo tipo (namespace completo) - Overrides por namespace são ordenados por especificidade (prefixo mais longo vence)
- HTTP client names são case-insensitive
- Não usar overrides para queries SQL -- o interceptor usa sempre o default de Queries
- Não usar overrides para storage -- o R2StorageService usa sempre o default de Storage