Reec.Inspection 8.23.0

dotnet add package Reec.Inspection --version 8.23.0
                    
NuGet\Install-Package Reec.Inspection -Version 8.23.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Reec.Inspection" Version="8.23.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Reec.Inspection" Version="8.23.0" />
                    
Directory.Packages.props
<PackageReference Include="Reec.Inspection" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Reec.Inspection --version 8.23.0
                    
#r "nuget: Reec.Inspection, 8.23.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Reec.Inspection@8.23.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Reec.Inspection&version=8.23.0
                    
Install as a Cake Addin
#tool nuget:?package=Reec.Inspection&version=8.23.0
                    
Install as a Cake Tool

🚀 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

  1. Configuración inicial
  2. Versión legacy vs nueva
  3. Configuración de ReecExceptionOptions
  4. Importancia de SystemTimeZoneId
  5. Registro de ApplicationName
  6. Guardado condicional (EnableGlobalDbSave / IsSaveDB)
  7. Captura de errores (LogHttp, LogAudit)
  8. Ejecución de tareas en segundo plano (IWorker)
  9. Resiliencia en peticiones HTTP (AddReecInspectionResilience)
  10. Migración con otro proveedor de base de datos
  11. Buenas prácticas y sugerencias
  12. Manejo de excepciones controladas (Modo Legacy)
  13. Manejo de excepciones con ProblemDetails (Modo Actual)
  14. 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 DbContext derivado de InspectionDbContext con DbContextPool.
  • Registra middlewares (LogAuditMiddleware, LogHttpMiddleware).
  • Registra IWorker, IDateTimeService y 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 ReecExceptionOptions mediante Action<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: LogJob y LogEndpoint no 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 ProblemDetails si está habilitado.

LogAudit — Auditoría HTTP

LogAuditMiddleware:

  • Registra método, ruta, estado, tiempos, opcionalmente cuerpo de request/response.
  • Respeta:
    • ExcludePaths
    • RequestBodyMaxSize
    • ResponseBodyMaxSize
    • EnableBuffering

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 es true.
  • Estados (Enqueued, Processing, Succeeded, Failed) en LogJob.

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.Run externo: IWorker maneja 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 ApplicationName y SystemTimeZoneId.
  • Considera desactivar EnableMigrations en producción y aplicar migraciones vía CI/CD.
  • Usa Schema dedicado (ej. "Inspection") para aislar tus tablas de log.
  • Ajusta DeleteDays y DeleteBatch según volumen de logs.
  • Usa IsLightExecution = true para 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

12.3. Formas de uso

a) Mensaje simple