Reec.Inspection
8.23.0
dotnet add package Reec.Inspection --version 8.23.0
NuGet\Install-Package Reec.Inspection -Version 8.23.0
<PackageReference Include="Reec.Inspection" Version="8.23.0" />
<PackageVersion Include="Reec.Inspection" Version="8.23.0" />
<PackageReference Include="Reec.Inspection" />
paket add Reec.Inspection --version 8.23.0
#r "nuget: Reec.Inspection, 8.23.0"
#:package Reec.Inspection@8.23.0
#addin nuget:?package=Reec.Inspection&version=8.23.0
#tool nuget:?package=Reec.Inspection&version=8.23.0
🚀 Reec.Inspection — Observabilidad ligera y trazabilidad para aplicaciones .NET
Reec.Inspection es una librería de observabilidad ligera para aplicaciones .NET 8+ que centraliza:
- Auditoría de solicitudes HTTP entrantes.
- Captura automática de errores del pipeline.
- Ejecución segura de tareas en segundo plano.
- Registro de llamadas a servicios externos con resiliencia.
- Limpieza automática de logs mediante workers en segundo plano.
Todo esto usando Entity Framework Core y una configuración sencilla basada en opciones (ReecExceptionOptions).
⚡ Guía rápida
Si solo quieres verlo funcionando en minutos, sigue esta sección.
Para más detalles, baja a la 👉 Guía completa.
📦 Instalación (NuGet)
dotnet add package Reec.Inspection
dotnet add package Reec.Inspection.SqlServer
📦 Matriz de dependencias (baseline probado)
Reec.Inspection
| Dependencia | net8.0 | net9.0 | net10.0 | Comentario |
|---|---|---|---|---|
| Cronos | 0.11.1 | 0.11.1 | 0.11.1 | Scheduler CRON usado por workers de limpieza |
| Microsoft.EntityFrameworkCore | 8.0.22 | 9.0.6 | 10.0.0 | ORM principal |
| Microsoft.EntityFrameworkCore.Relational | 8.0.22 | 9.0.6 | 10.0.0 | Soporte relacional EF Core |
| Microsoft.AspNetCore.MiddlewareAnalysis | 8.0.22 | 9.0.6 | 10.0.0 | Instrumentación de pipeline HTTP |
| Microsoft.Extensions.Http.Resilience | 8.10.0 | 9.6.0 | 10.0.0 | Resiliencia HTTP (Polly-based) |
Reec.Inspection.SqlServer
| Dependencia | net8.0 | net9.0 | net10.0 | Comentario |
|---|---|---|---|---|
| Microsoft.EntityFrameworkCore.SqlServer | 8.0.22 | 9.0.6 | 10.0.0 | Provider SQL Server |
| Microsoft.EntityFrameworkCore.Design | 8.0.22 | 9.0.6 | 10.0.0 | Migraciones (PrivateAssets=all) |
| Microsoft.EntityFrameworkCore.Tools | 8.0.22 | 9.0.6 | 10.0.0 | CLI / tooling (PrivateAssets=all) |
Política de actualización de dependencias
- Cada dependencia se publica con un baseline probado por TFM.
- El baseline se revisa aproximadamente cada 6 meses.
- Regla práctica: el baseline suele avanzar alrededor de ~6 versiones de patch cuando corresponde.
- El consumidor puede actualizar a versiones superiores según sus políticas de seguridad y pipeline CI/CD.
🧰 Configuración mínima (Program.cs)
builder.Services.AddReecInspection<InspectionDbContext>(
options => options.UseSqlServer(builder.Configuration.GetConnectionString("default")),
options =>
{
options.ApplicationName = "Reec.Inspection.Api"; // Obligatorio
options.SystemTimeZoneId = "SA Pacific Standard Time"; // Recomendado
options.EnableProblemDetails = true; // Opcional
});
var app = builder.Build();
app.UseReecInspection(); // Registra los middlewares de auditoría y captura de errores
app.MapControllers();
app.Run();
Con esto obtienes:
- Middleware de auditoría (
LogAudit) para requests HTTP. - Middleware de errores (
LogHttp) para excepciones no controladas. - Workers de limpieza de logs, si están habilitados en las opciones.
⚙️ Ejemplo rápido de captura de errores
[HttpGet("error")]
public IActionResult GetError()
{
var x = 1 / 0; // Error intencional
return Ok();
}
Ese error se registra automáticamente en la tabla LogHttp (y puede devolverse como ProblemDetails si está activado).
🕒 Limpieza automática de logs
Ejemplo rápido para LogAudit:
options.LogAudit.EnableClean = true;
options.LogAudit.CronValue = "0 2 * * *"; // Todos los días a las 2 a.m.
options.LogAudit.DeleteDays = 10; // Mantiene solo los últimos 10 días
Cada tipo (LogAudit, LogHttp, LogEndpoint, LogJob) tiene su propio worker de limpieza opcional.
💝 Apoya el desarrollo continuo
Si Reec.Inspection está ayudando a optimizar tu trabajo y te gustaría contribuir al desarrollo continuo de esta librería, puedes hacerlo a través de Plin (Perú):
<div align="center">
<img src="https://raw.githubusercontent.com/edychumpitaz/ReecInspection/main/images/QR%20Plin.jpeg" alt="Plin QR Code" width="300"/>
Yape/Plin
</div>
Tu apoyo ayuda a mantener el proyecto actualizado con nuevas características, correcciones de bugs y documentación mejorada. ¡Toda contribución es valorada! 🙏
Guía completa
Índice
- Configuración inicial
- Versión legacy vs nueva
- Configuración de
ReecExceptionOptions - Importancia de
SystemTimeZoneId - Registro de
ApplicationName - Guardado condicional (
EnableGlobalDbSave/IsSaveDB) - Captura de errores (
LogHttp,LogAudit) - Ejecución de tareas en segundo plano (
IWorker) - Resiliencia en peticiones HTTP (
AddReecInspectionResilience) - Migración con otro proveedor de base de datos
- Buenas prácticas y sugerencias
- Manejo de excepciones controladas (Modo Legacy)
- Manejo de excepciones con ProblemDetails (Modo Actual)
- Estado del proyecto
1. Configuración inicial
Registro principal en Program.cs:
builder.Services.AddReecInspection<InspectionDbContext>(
options => options.UseSqlServer(builder.Configuration.GetConnectionString("default")),
options =>
{
options.ApplicationName = "Reec.Inspection.Api";
options.SystemTimeZoneId = "SA Pacific Standard Time";
options.EnableProblemDetails = true;
options.EnableGlobalDbSave = true;
});
var app = builder.Build();
app.UseReecInspection();
AddReecInspection:
- Registra el
DbContextderivado deInspectionDbContextconDbContextPool. - Registra middlewares (
LogAuditMiddleware,LogHttpMiddleware). - Registra
IWorker,IDateTimeServicey workers de limpieza (CleanLog*Worker) según configuración. - Opcionalmente agrega soporte
ProblemDetails.
UseReecInspection:
- Agrega al pipeline los middlewares de auditoría y captura de errores según
ReecExceptionOptions.
Orden recomendado:
app.UseResponseCompression();
app.UseReecInspection();
app.UseOutputCache();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
2. Versión legacy vs nueva
AddReecException<TDbContext> (Legacy)
- Se mantiene con
[Obsolete]para compatibilidad. - Usar solo en proyectos existentes.
AddReecInspection<TDbContext> (Recomendado)
- Nueva API principal.
- Usa
DbContextPool. - Configura
ReecExceptionOptionsmedianteAction<ReecExceptionOptions>. - Integra hosted services de limpieza y
IWorker.
Ejemplo:
builder.Services.AddReecInspection<InspectionDbContext>(
options => options.UseSqlServer(connString),
options =>
{
options.ApplicationName = "MyService.Api";
options.EnableGlobalDbSave = true;
options.LogHttp.IsSaveDB = true;
options.LogAudit.IsSaveDB = true;
});
3. Configuración de ReecExceptionOptions
ReecExceptionOptions centraliza la configuración global.
Propiedades principales
| Propiedad | Descripción | Default |
|---|---|---|
ApplicationName |
Nombre de la app que genera los logs. | null |
ApplicationErrorMessage |
Mensaje mostrado cuando ocurre un error al intentar guardar información en la base de datos. | Ocurrió un error al guardar log en Base de Datos. |
InternalServerErrorMessage |
Mensaje genérico utilizado para errores internos del sistema. | Error no controlado del sistema. |
SystemTimeZoneId |
Zona horaria usada para registrar fechas y programar cron. | "SA Pacific Standard Time" |
EnableMigrations |
Ejecuta migraciones automáticas al inicio. | true |
EnableProblemDetails |
Respuestas de error en formato ProblemDetails. |
false |
EnableGlobalDbSave |
Habilita/deshabilita escritura global en BD. | true |
MinCategory |
Categoría mínima a registrar. | Unauthorized (401) |
Secciones por tipo de log
Cada módulo tiene opciones propias (LogAudit, LogHttp, LogJob, LogEndpoint):
Schema: esquema de base de datos.TableName: nombre de la tabla.IsSaveDB: habilita/deshabilita persistencia.EnableClean: activa worker de limpieza.CronValue: expresión CRON para limpieza. Puedes usar https://crontab.guru para generar y validar expresiones CRON.DeleteDays: días hacia atrás a conservar.DeleteBatch: tamaño del lote de borrado.
Ejemplo para LogAudit con tablas existentes (sin migraciones):
options.EnableMigrations = false;
options.LogAudit.Schema = "Inspection";
options.LogAudit.TableName = "LogAudit";
options.LogAudit.IsSaveDB = true;
options.LogAudit.EnableClean = true;
options.LogAudit.CronValue = "0 2 * * *";
options.LogAudit.DeleteDays = 15;
options.LogAudit.DeleteBatch = 500;
Aplica el mismo patrón para LogHttp, LogJob y LogEndpoint.
Configuración de EnableBuffering en LogHttp y LogAudit
La propiedad EnableBuffering está disponible únicamente en los módulos LogHttp y LogAudit, ya que estos middlewares necesitan leer el cuerpo (body) de las peticiones y respuestas HTTP para registrarlas en la base de datos.
¿Qué hace EnableBuffering?
Cuando está habilitado (true), permite que el stream del request y response pueda ser leído múltiples veces, lo cual es necesario para capturar el contenido sin afectar el flujo normal de la aplicación.
¿Cuándo desactivarlo?
Si ya tienes un middleware superior en tu pipeline que gestiona el buffering del request/response (por ejemplo, para logging personalizado, transformación de contenido, o compresión), puedes desactivar EnableBuffering en estos módulos para evitar redundancia y mejorar el rendimiento.
Ejemplo de configuración:
options.LogHttp.EnableBuffering = true; // Por defecto
options.LogAudit.EnableBuffering = false; // Desactivado si hay middleware superior que ya gestiona buffering
Nota:
LogJobyLogEndpointno tienen esta propiedad ya que no interactúan directamente con streams HTTP del pipeline de ASP.NET Core.
4. Importancia de SystemTimeZoneId
Todas las fechas registradas en los logs y workers usan esta zona horaria:
- Fechas de creación.
- Ejecuciones de jobs.
- Cálculo de
CronValue.
options.SystemTimeZoneId = "SA Pacific Standard Time";
Para ver las zonas disponibles:
var zones = TimeZoneInfo.GetSystemTimeZones();
Si el ID es inválido, la inicialización de IDateTimeService lanzará excepción.
5. Registro de ApplicationName
Obligatorio para distinguir qué sistema originó cada registro.
options.ApplicationName = "Billing.Api";
Se utiliza en todas las tablas de log como columna de referencia.
6. Guardado condicional
Global
options.EnableGlobalDbSave = true; // Si es false, no se persisten logs en BD.
Por módulo
options.LogAudit.IsSaveDB = true;
options.LogHttp.IsSaveDB = true;
options.LogJob.IsSaveDB = true;
options.LogEndpoint.IsSaveDB = true;
Desactivar por módulo es útil para escenarios donde solo quieres ciertos tipos de trazas.
7. Captura de errores (LogHttp, LogAudit)
LogHttp — Errores del pipeline
Ejemplo:
[HttpGet("test-error")]
public IActionResult TestError()
{
var value = 10 / 0;
return Ok(value);
}
LogHttpMiddleware:
- Intercepta excepciones no controladas.
- Registra
Exception,StackTrace,Path,TraceIdentifier, etc. - Puede responder en
ProblemDetailssi está habilitado.
LogAudit — Auditoría HTTP
LogAuditMiddleware:
- Registra método, ruta, estado, tiempos, opcionalmente cuerpo de request/response.
- Respeta:
ExcludePathsRequestBodyMaxSizeResponseBodyMaxSizeEnableBuffering
Ejemplo de exclusión:
options.LogAudit.ExcludePaths = new[] { "swagger", "health", "index" };
8. Ejecución de tareas en segundo plano (IWorker)
IWorker expone:
RunFunction: lógica principal.RunFunctionException: manejo custom de errores.IsLightExecution: solo registra fallos cuando estrue.- Estados (
Enqueued,Processing,Succeeded,Failed) enLogJob.
8.1 Modo persistente (HostedService)
Uso recomendado para jobs periódicos (patrón similar a los CleanLog*Worker).
public class SampleJobWorker : BackgroundService
{
private readonly IServiceScopeFactory _scopeFactory;
public SampleJobWorker(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
using var scope = _scopeFactory.CreateScope();
var worker = scope.ServiceProvider.GetRequiredService<IWorker>();
worker.NameJob = nameof(SampleJobWorker);
worker.CreateUser = "System";
worker.IsLightExecution = false;
worker.RunFunction = service => ProcessAsync(service, stoppingToken);
await worker.ExecuteAsync(stoppingToken);
}
}
private static async Task<string> ProcessAsync(IServiceProvider services, CancellationToken ct)
{
var dbContextService = services.GetRequiredService<IDbContextService>();
var db = dbContextService.GetDbContext();
// Lógica de negocio aquí
await Task.Delay(1000, ct);
return "Proceso completado correctamente.";
}
}
Registrar el worker:
builder.Services.AddHostedService<SampleJobWorker>();
8.2 Modo "fire-and-forget" disparado desde un request
Para iniciar una tarea en segundo plano desde un endpoint HTTP sin bloquear la respuesta:
[ApiController]
[Route("api/[controller]")]
public class JobsController : ControllerBase
{
private readonly IServiceScopeFactory _scopeFactory;
public JobsController(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
}
[HttpPost("start-clean-temp")]
public IActionResult StartCleanTemp()
{
var scope = _scopeFactory.CreateScope();
var worker = scope.ServiceProvider.GetRequiredService<IWorker>();
worker.NameJob = "CleanTemporaryFiles";
worker.CreateUser = "System";
worker.IsLightExecution = true;
worker.RunFunction = svc => ProcessAsync(svc);
_ = worker.ExecuteAsync().ContinueWith(_ =>
{
scope.Dispose();
});
return Ok("Tarea en segundo plano iniciada.");
}
private static async Task<string> ProcessAsync(IServiceProvider services)
{
var dbContextService = services.GetRequiredService<IDbContextService>();
var db = dbContextService.GetDbContext();
// Lógica puntual
await Task.Delay(2000);
return "Limpieza de temporales completada.";
}
}
Notas:
- No se usa
Task.Runexterno:IWorkermaneja la ejecución asíncrona y logging. - No se espera el resultado, pero el job queda registrado en
LogJob.
9. Resiliencia en peticiones HTTP (AddReecInspectionResilience)
Esta extensión integra:
LogEndpointHandler: registra requests/responses a servicios externos.- Pipeline estándar de resiliencia (timeout, retry, circuit breaker).
Registro
var httpBuilder = builder.Services.AddHttpClient("PlaceHolder", httpClient =>
{
httpClient.DefaultRequestHeaders.Clear();
httpClient.BaseAddress = new Uri("https://jsonplaceholder.typicode.com");
});
builder.Services.AddReecInspectionResilience(httpBuilder);
Uso:
public class ExternalController : ControllerBase
{
private readonly IHttpClientFactory _httpClientFactory;
public ExternalController(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
[HttpGet("posts")]
public async Task<IActionResult> GetPosts()
{
var client = _httpClientFactory.CreateClient("PlaceHolder");
var response = await client.GetAsync("/posts");
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
return Content(content, "application/json");
}
}
AddReecInspectionResilience configura por defecto:
- Timeout total: 1 minuto (personalizable).
- Reintentos con backoff exponencial.
- Circuit breaker con metadatos en
HttpRequestMessage.Options.
10. Migración con otro proveedor de base de datos
Para usar PostgreSQL (u otro proveedor soportado por EF Core), hereda de InspectionDbContext y genera una migración:
public class InspectionPgContext : InspectionDbContext
{
public InspectionPgContext(DbContextOptions<InspectionPgContext> options)
: base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Ejemplo: esquema por defecto
modelBuilder.HasDefaultSchema("inspection");
}
}
Registro:
builder.Services.AddReecInspection<InspectionPgContext>(
options => options.UseNpgsql(builder.Configuration.GetConnectionString("Postgres")),
options =>
{
options.ApplicationName = "Reec.Pg.Api";
options.EnableMigrations = true; // O manejar migraciones externamente
});
11. Buenas prácticas y sugerencias
- Configura siempre
ApplicationNameySystemTimeZoneId. - Considera desactivar
EnableMigrationsen producción y aplicar migraciones vía CI/CD. - Usa
Schemadedicado (ej."Inspection") para aislar tus tablas de log. - Ajusta
DeleteDaysyDeleteBatchsegún volumen de logs. - Usa
IsLightExecution = truepara jobs muy frecuentes donde solo te interesen errores. - Excluye rutas sensibles de
LogAudit(swagger,health, etc.). - Asegúrate de no registrar cuerpos que contengan datos sensibles sin anonimizar.
12. Manejo de excepciones controladas (Modo Legacy)
Reec.Inspection mantiene compatibilidad total con el sistema de excepciones del proyecto Reec original mediante ReecException y ReecMessage.
Este modo es útil cuando:
- Migras desde Reec a Reec.Inspection.
- Necesitas mantener contratos de respuesta existentes con clientes.
- Prefieres un formato de respuesta personalizado sobre RFC 7807 (ProblemDetails).
12.1. Configuración
Para usar el modo legacy, establece EnableProblemDetails = false (es el valor por defecto):
builder.Services.AddReecInspection<InspectionDbContext>(
options => options.UseSqlServer(connString),
options =>
{
options.ApplicationName = "Legacy.Api";
options.EnableProblemDetails = false; // Modo legacy activado
});
12.2. Categorías de error disponibles
Las categorías están definidas en el enum Category y representan diferentes tipos de respuestas:
| Categoría | Valor | HTTP Status | Uso |
|---|---|---|---|
OK |
200 | 200 | Operación exitosa |
PartialContent |
206 | 206 | Consulta exitosa sin contenido |
Unauthorized |
401 | 401 | Autenticación requerida |
Forbidden |
403 | 403 | Sin permisos suficientes |
Warning |
460 | 400 | Validación de campos |
BusinessLogic |
465 | 400 | Errores controlados de negocio |
BusinessLogicLegacy |
470 | 400 | Errores controlados de sistemas externos |
InternalServerError |
500 | 500 | Errores no controlados |
BadGateway |
502 | 502 | Error en sistema externo |
GatewayTimeout |
504 | 504 | Timeout en sistema externo |