JonjubNet.Logging
3.1.3
dotnet add package JonjubNet.Logging --version 3.1.3
NuGet\Install-Package JonjubNet.Logging -Version 3.1.3
<PackageReference Include="JonjubNet.Logging" Version="3.1.3" />
<PackageVersion Include="JonjubNet.Logging" Version="3.1.3" />
<PackageReference Include="JonjubNet.Logging" />
paket add JonjubNet.Logging --version 3.1.3
#r "nuget: JonjubNet.Logging, 3.1.3"
#:package JonjubNet.Logging@3.1.3
#addin nuget:?package=JonjubNet.Logging&version=3.1.3
#tool nuget:?package=JonjubNet.Logging&version=3.1.3
JonjubNet.Logging
Biblioteca de logging estructurado para aplicaciones .NET con soporte para múltiples sinks (Console, File, HTTP, Elasticsearch, Kafka), funcionalidades avanzadas de correlación y enriquecimiento, y logging automático para operaciones MediatR sin código manual. Optimizado para .NET 10 y C# 13 con mejoras significativas de performance.
📋 Tabla de Contenidos
- Características
- Instalación
- Quick Start
- Configuración Paso a Paso
- Uso Básico
- Ejemplos Avanzados
- Personalización
- Arquitectura
- Troubleshooting
- Documentación Adicional
✨ Características
Funcionalidades Principales
- Logging Estructurado: Logs en formato JSON estructurado con propiedades enriquecidas
- Múltiples Sinks: Soporte para Console, File, HTTP, Elasticsearch y Kafka
- Correlación de Logs: IDs de correlación, request y sesión para rastrear operaciones
- Enriquecimiento Automático: Información de usuario, HTTP context, ambiente, versión, etc.
- Categorización de Errores: Distinción entre errores funcionales y técnicos
- Filtrado Dinámico: Filtros por categoría, operación, usuario y nivel de log aplicados antes de enviar a sinks
- Log Scopes: Contexto temporal que se propaga a todos los logs dentro de un scope
- Log Sampling / Rate Limiting: Reducción de volumen de logs en producción mediante sampling probabilístico y límites por minuto
- Data Sanitization: Enmascaramiento automático de datos sensibles (PII, PCI) para cumplimiento y seguridad
- Captura HTTP: Headers, query string, body de request/response (configurable)
- Clean Architecture: Implementado siguiendo principios de Clean Architecture
Niveles de Log Soportados
Trace- Información muy detalladaDebug- Información de depuraciónInformation- Información generalWarning- AdvertenciasError- ErroresCritical- Errores críticos
Tipos de Eventos Especiales
- Operaciones: Inicio y fin de operaciones con tiempo de ejecución
- Acciones de Usuario: Tracking de acciones realizadas por usuarios
- Eventos de Seguridad: Logging de eventos relacionados con seguridad
- Eventos de Auditoría: Registro de eventos de auditoría
📦 Instalación
<<<<<<< HEAD
⚠️ Importante: Instalar Solo el Paquete Principal
NO intentes instalar los paquetes internos (JonjubNet.Logging.Shared, JonjubNet.Logging.Domain, JonjubNet.Logging.Application). Estos son proyectos internos que se incluyen automáticamente en el paquete principal.
Paso 1: Instalar el Paquete NuGet
Instala solo el paquete principal:
dotnet add package JonjubNet.Logging
O desde el Package Manager Console en Visual Studio:
Install-Package JonjubNet.Logging
<<<<<<< HEAD
O desde el NuGet Package Manager UI, busca e instala solo JonjubNet.Logging.
Paso 2: Verificar la Instalación
Después de instalar, verifica que en tu archivo .csproj aparezca:
<ItemGroup>
<PackageReference Include="JonjubNet.Logging" Version="1.0.38" />
</ItemGroup>
No debe haber referencias a:
- ❌
JonjubNet.Logging.Shared - ❌
JonjubNet.Logging.Domain - ❌
JonjubNet.Logging.Application
Estos se incluyen automáticamente en el paquete principal.
Paso 3: Verificar Dependencias
=======
Paso 2: Verificar Dependencias
6b8317a7f8fd86192c146f543abc241ef855a4cf
El paquete incluye todas las dependencias necesarias:
- Serilog y sus sinks
- Microsoft.Extensions.*
- Confluent.Kafka (para Kafka)
- Todos los ensamblados internos (Shared, Domain, Application)
🚀 Quick Start
El ejemplo más simple para empezar:
// 1. Instalar el paquete (solo el principal)
dotnet add package JonjubNet.Logging
// 2. Configurar appsettings.json
{
"StructuredLogging": {
"Enabled": true,
"MinimumLevel": "Information",
"ServiceName": "MiServicio"
}
}
// 3. Registrar en Program.cs
using JonjubNet.Logging;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddStructuredLoggingInfrastructure(builder.Configuration);
<<<<<<< HEAD
// 4. Inyectar y usar en tu código
using JonjubNet.Logging.Application.Interfaces;
public class MiServicio
{
private readonly IStructuredLoggingService _loggingService;
public MiServicio(IStructuredLoggingService loggingService)
{
_loggingService = loggingService;
}
public void MiMetodo()
{
_loggingService.LogInformation("Hello World!");
}
}
=======
// 4. Usar en tu código
_loggingService.LogInformation("Hello World!");
>>>>>>> 6b8317a7f8fd86192c146f543abc241ef855a4cf
¡Listo! Ya tienes logging estructurado funcionando. Para más opciones, consulta la Configuración Completa más abajo.
⚙️ Configuración Paso a Paso
📋 Cómo Funciona la Configuración en Microservicios
Cuando agregas el componente a un microservicio, la configuración se lee automáticamente desde el appsettings.json del microservicio. El componente busca la sección "StructuredLogging" y mapea todos los parámetros automáticamente.
Características:
- ✅ Lectura automática desde
appsettings.jsondel microservicio - ✅ Hot-reload - Cambios se detectan automáticamente (si
reloadOnChange: true) - ✅ Valores por defecto - Si falta un parámetro, usa valores por defecto
- ✅ Sin código adicional - Solo configuración JSON
Paso 1: Configurar appsettings.json en tu Microservicio
Crea o actualiza el archivo appsettings.json de tu microservicio con la sección StructuredLogging:
{
"StructuredLogging": {
"Enabled": true,
"MinimumLevel": "Information",
"ServiceName": "MiMicroservicio",
"Environment": "Production",
"Version": "1.0.0",
"Sinks": {
"EnableConsole": true,
"EnableFile": true,
"EnableHttp": false,
"EnableElasticsearch": true,
"File": {
"Path": "logs/log-.txt",
"RollingInterval": "Day",
"RetainedFileCountLimit": 30,
"FileSizeLimitBytes": 104857600,
"OutputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
},
"Http": {
"Url": "https://mi-servidor-logs.com/api/logs",
"ContentType": "application/json",
"BatchPostingLimit": 100,
"PeriodSeconds": 2,
"Headers": {
"Authorization": "Bearer token123"
}
},
"Elasticsearch": {
"Url": "http://elasticsearch:9200",
"IndexFormat": "logs-{0:yyyy.MM.dd}",
"Username": "elastic",
"Password": "password",
"EnableAuthentication": true
}
},
"Filters": {
"ExcludedCategories": ["Debug", "Trace"],
"ExcludedOperations": ["HealthCheck"],
"ExcludedUsers": ["System"],
"FilterByLogLevel": true,
"CategoryLogLevels": {
"Security": "Warning",
"Performance": "Information"
},
"OperationLogLevels": {
"GetUser": "Debug",
"CreateOrder": "Information"
}
},
"Enrichment": {
"IncludeEnvironment": true,
"IncludeProcess": true,
"IncludeThread": true,
"IncludeMachineName": true,
"IncludeServiceInfo": true,
"StaticProperties": {
"Application": "MiAplicacion",
"Region": "us-east-1"
},
"HttpCapture": {
"IncludeRequestHeaders": true,
"IncludeResponseHeaders": false,
"IncludeQueryString": true,
"IncludeRequestBody": false,
"IncludeResponseBody": false,
"MaxBodySizeBytes": 10240,
"SensitiveHeaders": [
"Authorization",
"Cookie",
"X-API-Key",
"X-Auth-Token"
]
}
},
"Correlation": {
"EnableCorrelationId": true,
"EnableRequestId": true,
"EnableSessionId": true,
"CorrelationIdHeader": "X-Correlation-ID",
"RequestIdHeader": "X-Request-ID",
"SessionIdHeader": "X-Session-ID"
},
"KafkaProducer": {
"Enabled": false,
"BootstrapServers": "localhost:9092",
"ProducerUrl": "http://localhost:8080/api/logs",
"Topic": "structured-logs",
"TimeoutSeconds": 5,
"BatchSize": 100,
"LingerMs": 5,
"RetryCount": 3,
"EnableCompression": true,
"CompressionType": "gzip",
"Headers": {
"Content-Type": "application/json"
}
},
"Sampling": {
"Enabled": false,
"SamplingRates": {
"Debug": 0.01,
"Information": 0.1,
"Trace": 0.001
},
"MaxLogsPerMinute": {
"Information": 1000,
"Debug": 500
},
"NeverSampleCategories": ["Security", "Audit", "Error", "Critical"],
"NeverSampleLevels": ["Error", "Critical"]
},
"DataSanitization": {
"Enabled": true,
"SensitivePropertyNames": [
"Password",
"Passwd",
"Pwd",
"CreditCard",
"CardNumber",
"CCNumber",
"SSN",
"SocialSecurityNumber",
"Email",
"EmailAddress",
"Phone",
"PhoneNumber",
"Mobile",
"Token",
"AccessToken",
"RefreshToken",
"ApiKey",
"Secret",
"SecretKey",
"Authorization",
"AuthToken"
],
"SensitivePatterns": [
"\\b\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{4}\\b",
"\\b\\d{3}-\\d{2}-\\d{4}\\b",
"\\b\\d{3}\\.\\d{3}\\.\\d{4}\\b"
],
"MaskValue": "***REDACTED***",
"MaskPartial": true,
"PartialMaskLength": 4
},
"CircuitBreaker": {
"Enabled": true,
"Default": {
"FailureThreshold": 5,
"OpenTimeout": "00:01:00",
"HalfOpenTestCount": 3
},
"PerSink": {
"Http": {
"Enabled": true,
"FailureThreshold": 3,
"OpenTimeout": "00:00:30"
},
"Elasticsearch": {
"Enabled": true,
"FailureThreshold": 5,
"OpenTimeout": "00:02:00"
}
}
},
"RetryPolicy": {
"Enabled": true,
"Default": {
"Strategy": "ExponentialBackoff",
"MaxRetries": 3,
"InitialDelay": "00:00:01",
"MaxDelay": "00:00:30",
"BackoffMultiplier": 2.0
},
"PerSink": {
"Http": {
"Enabled": true,
"Strategy": "FixedDelay",
"MaxRetries": 5,
"InitialDelay": "00:00:02"
},
"Elasticsearch": {
"Enabled": true,
"Strategy": "ExponentialBackoff",
"MaxRetries": 3,
"InitialDelay": "00:00:01"
}
},
"NonRetryableExceptions": [
"System.ArgumentException",
"System.UnauthorizedAccessException",
"System.ArgumentNullException"
]
},
"DeadLetterQueue": {
"Enabled": true,
"MaxSize": 10000,
"RetryInterval": "00:05:00",
"MaxRetriesPerItem": 10,
"ItemRetentionPeriod": "7.00:00:00",
"AutoRetry": true,
"Storage": "InMemory",
"PersistencePath": null
},
"Batching": {
"Enabled": true,
"DefaultBatchSize": 100,
"MaxBatchIntervalMs": 1000,
"BatchSizeBySink": {
"Http": 200,
"Elasticsearch": 500,
"Kafka": 1000
},
"EnableCompression": false,
"CompressionLevel": "Optimal",
"EnablePriorityQueues": false,
"QueueCapacityByPriority": {
"Critical": 10000,
"Error": 5000,
"Warning": 3000,
"Information": 2000,
"Debug": 1000,
"Trace": 500
},
"EnablePriorityProcessing": false,
"CriticalProcessingIntervalMs": 100,
"NormalProcessingIntervalMs": 1000
}
}
}
Paso 2: Registrar Servicios en Program.cs (o Startup.cs)
El componente automáticamente lee la configuración desde appsettings.json cuando pasas builder.Configuration o Configuration.
Opción A: Configuración Simple (Todo en appsettings.json)
Recomendado para: Microservicios pequeños/medianos con configuración <500 líneas
Para ASP.NET Core 6.0+ (Program.cs)
using JonjubNet.Logging;
var builder = WebApplication.CreateBuilder(args);
// Opcional: Habilitar hot-reload para cambios automáticos en appsettings.json
builder.Configuration
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true);
// Registrar servicios de logging estructurado
// ⚠️ IMPORTANTE: Pasa builder.Configuration para que lea automáticamente appsettings.json
builder.Services.AddStructuredLoggingInfrastructure(builder.Configuration);
// ... resto de tu configuración
var app = builder.Build();
// ... resto de tu aplicación
app.Run();
Opción B: Configuración Modular (Archivos Separados)
Recomendado para: Microservicios con múltiples componentes y configuración extensa (>500 líneas)
Cuando tienes múltiples componentes con configuración extensa, puedes separar la configuración en archivos individuales:
Estructura de archivos:
Microservicio/
├── appsettings.json (base - mínimo)
│ {
│ "Logging": { "LogLevel": { "Default": "Information" } },
│ "ConnectionStrings": { ... }
│ }
│
├── appsettings.Production.json (overrides por entorno)
│ {
│ "StructuredLogging": {
│ "MinimumLevel": "Warning"
│ }
│ }
│
└── config/
├── structured-logging.json (configuración completa del componente)
├── monitoring.json (otro componente)
└── caching.json (otro componente)
Program.cs con carga automática:
using JonjubNet.Logging;
var builder = WebApplication.CreateBuilder(args);
// Configuración base
builder.Configuration
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true)
.AddComponentConfigurations(builder.Environment.ContentRootPath); // Carga automática de todos los .json en config/
// Registrar servicios
builder.Services.AddStructuredLoggingInfrastructure(builder.Configuration);
var app = builder.Build();
app.Run();
O cargar componentes específicos:
builder.Configuration
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", optional: true, reloadOnChange: true)
.AddComponentConfigurations(builder.Environment.ContentRootPath, "structured-logging", "monitoring"); // Solo estos componentes
<|tool▁calls▁begin|><|tool▁call▁begin|> run_terminal_cmd
O cargar un archivo específico:
builder.Configuration
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddComponentConfiguration("config/structured-logging.json", optional: true);
Ventajas de la configuración modular:
- ✅ Separa configuración de cada componente
- ✅
appsettings.jsonmás limpio y fácil de leer - ✅ Archivos pueden compartirse entre microservicios
- ✅ Fácil de mantener y versionar
- ✅ Hot-reload funciona automáticamente
Nota: El componente busca la sección "StructuredLogging" en la configuración combinada, por lo que funciona igual con archivos separados.
Para ASP.NET Core 5.0 o anterior (Startup.cs)
using JonjubNet.Logging;
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
// Registrar servicios de logging estructurado
// ⚠️ IMPORTANTE: Pasa Configuration para que lea automáticamente appsettings.json
services.AddStructuredLoggingInfrastructure(Configuration);
// ... resto de tus servicios
}
}
Para Aplicaciones sin Host (Console Apps, etc.)
using JonjubNet.Logging;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.Build();
var services = new ServiceCollection();
// Usar método sin host (no requiere BackgroundService)
services.AddStructuredLoggingInfrastructureWithoutHost<DefaultCurrentUserService>(configuration);
var serviceProvider = services.BuildServiceProvider();
Nota: El componente detecta automáticamente si hay un IHost disponible y registra los servicios apropiados (con o sin BackgroundService).
Paso 3: Inyectar y Usar el Servicio
En cualquier clase donde necesites logging:
using JonjubNet.Logging.Application.Interfaces;
public class MiServicio
{
private readonly IStructuredLoggingService _loggingService;
public MiServicio(IStructuredLoggingService loggingService)
{
_loggingService = loggingService;
}
public void MiMetodo()
{
<<<<<<< HEAD
// Log simple
_loggingService.LogInformation("Operación completada");
// Log con operación y categoría
_loggingService.LogInformation("Operación completada", "MiMetodo", "Business");
// Log con propiedades adicionales
_loggingService.LogInformation("Operación completada", "MiMetodo", "Business",
properties: new Dictionary<string, object>
{
{ "OrderId", "ORD-12345" },
{ "Status", "Completed" }
});
=======
_loggingService.LogInformation("Operación completada", "MiMetodo", "Business");
>>>>>>> 6b8317a7f8fd86192c146f543abc241ef855a4cf
}
}
<<<<<<< HEAD
Nota: El servicio IStructuredLoggingService está disponible automáticamente después de registrar el componente. No necesitas instalar paquetes adicionales.
🚀 Uso Básico
Importar Namespaces Necesarios
using JonjubNet.Logging.Application.Interfaces; // Para IStructuredLoggingService
using JonjubNet.Logging; // Para extensiones de configuración
=======
🚀 Uso Básico
6b8317a7f8fd86192c146f543abc241ef855a4cf
Logging Simple
// Log de información básico
_loggingService.LogInformation("Aplicación iniciada correctamente");
// Log con operación y categoría
_loggingService.LogInformation("Usuario autenticado", "Authentication", "Security");
// Log con propiedades adicionales
_loggingService.LogInformation("Producto creado", "CreateProduct", "Business",
properties: new Dictionary<string, object>
{
{ "ProductId", "PROD-12345" },
{ "ProductName", "Laptop Gaming" },
{ "Price", 1299.99 }
});
Logging de Errores
try
{
// Tu código aquí
}
catch (Exception ex)
{
_loggingService.LogError("Error al procesar solicitud", "ProcessRequest", "General",
properties: new Dictionary<string, object> { { "RequestId", "REQ-12345" } },
exception: ex);
}
Logging de Operaciones
var operationName = "ProcessOrder";
var category = "Business";
// Iniciar operación
_loggingService.LogOperationStart(operationName, category,
properties: new Dictionary<string, object> { { "OrderId", "ORD-12345" } });
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
try
{
// Tu lógica aquí
await ProcessOrderAsync();
// Operación exitosa
stopwatch.Stop();
_loggingService.LogOperationEnd(operationName, category,
executionTimeMs: stopwatch.ElapsedMilliseconds,
properties: new Dictionary<string, object>
{
{ "OrderId", "ORD-12345" },
{ "Status", "Completed" }
});
}
catch (Exception ex)
{
// Operación fallida
stopwatch.Stop();
_loggingService.LogOperationEnd(operationName, category,
executionTimeMs: stopwatch.ElapsedMilliseconds,
success: false,
exception: ex);
throw;
}
Todos los Niveles de Log
// Trace - Información muy detallada
_loggingService.LogTrace("Iniciando validación de datos", "ValidateData", "Debug");
// Debug - Información de depuración
_loggingService.LogDebug("Datos validados correctamente", "ValidateData", "Debug");
// Information - Información general
_loggingService.LogInformation("Proceso completado exitosamente", "ProcessData", "General");
// Warning - Advertencias
_loggingService.LogWarning("Límite de memoria alcanzado", "MemoryCheck", "System",
properties: new Dictionary<string, object> { { "MemoryUsage", "85%" } });
// Error - Errores
_loggingService.LogError("Error de conexión a base de datos", "DatabaseConnection", "Database");
// Critical - Errores críticos
_loggingService.LogCritical("Sistema no disponible", "SystemCheck", "System");
📚 Ejemplos Avanzados
Eventos de Usuario
_loggingService.LogUserAction("UpdateProfile", "User", "USER-12345",
properties: new Dictionary<string, object>
{
{ "FieldUpdated", "Email" },
{ "OldValue", "old@email.com" },
{ "NewValue", "new@email.com" }
});
Eventos de Seguridad
_loggingService.LogSecurityEvent("FailedLogin", "Intento de login fallido",
properties: new Dictionary<string, object>
{
{ "IP", "192.168.1.100" },
{ "UserAgent", "Mozilla/5.0..." },
{ "Attempts", 3 }
});
Eventos de Auditoría
_loggingService.LogAuditEvent("DataAccess", "Consulta de datos sensibles",
"User", "USER-12345",
properties: new Dictionary<string, object>
{
{ "DataAccessed", "PersonalInformation" },
{ "AccessReason", "ProfileUpdate" }
});
Logging Personalizado
using JonjubNet.Logging.Domain.Entities;
using JonjubNet.Logging.Domain.ValueObjects;
<<<<<<< HEAD
using JonjubNet.Logging.Application.Interfaces;
// Crear una entrada de log personalizada
=======
>>>>>>> 6b8317a7f8fd86192c146f543abc241ef855a4cf
var customLogEntry = new StructuredLogEntry
{
ServiceName = "MiServicio",
Operation = "CustomOperation",
LogLevel = LogLevelValue.Information.Value,
Message = "Operación personalizada ejecutada",
Category = LogCategoryValue.Custom.Value,
Properties = new Dictionary<string, object>
{
{ "CustomProperty1", "Valor1" },
{ "CustomProperty2", 42 }
}
};
<<<<<<< HEAD
// Enviar el log personalizado
=======
>>>>>>> 6b8317a7f8fd86192c146f543abc241ef855a4cf
_loggingService.LogCustom(customLogEntry);
🆕 Nuevas Funcionalidades
Log Scopes (Contexto Temporal)
Los scopes permiten agregar propiedades a todos los logs dentro de un contexto específico, sin necesidad de pasarlas en cada llamada:
using (_loggingService.BeginScope(new Dictionary<string, object>
{
{ "OrderId", "ORD-123" },
{ "CustomerId", "CUST-456" }
}))
{
// Todos estos logs incluirán automáticamente OrderId y CustomerId
_loggingService.LogInformation("Procesando orden");
_loggingService.LogInformation("Validando pago");
_loggingService.LogInformation("Enviando confirmación");
}
// También puedes usar una sola propiedad
using (_loggingService.BeginScope("RequestId", "REQ-789"))
{
_loggingService.LogInformation("Operación iniciada");
}
Log Sampling / Rate Limiting
Reduce el volumen de logs en producción mediante sampling probabilístico y límites por minuto:
{
"StructuredLogging": {
"Sampling": {
"Enabled": true,
"SamplingRates": {
"Debug": 0.01, // Solo 1% de logs Debug
"Information": 0.1, // Solo 10% de logs Information
"Trace": 0.001 // Solo 0.1% de logs Trace
},
"MaxLogsPerMinute": {
"Information": 1000,
"Debug": 500
},
"NeverSampleCategories": ["Security", "Audit", "Error", "Critical"],
"NeverSampleLevels": ["Error", "Critical"]
}
}
}
Beneficios:
- Reduce costos de almacenamiento
- Mejora performance en alta carga
- Mantiene logs representativos sin saturar
- Los logs críticos (Error, Critical, Security, Audit) nunca se muestrean
Data Sanitization (Enmascaramiento de Datos Sensibles)
Enmascara automáticamente datos sensibles (PII, PCI) para cumplimiento y seguridad:
{
"StructuredLogging": {
"DataSanitization": {
"Enabled": true,
"SensitivePropertyNames": [
"Password", "CreditCard", "SSN", "Email", "Phone", "Token"
],
"SensitivePatterns": [
"\\b\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{4}[\\s-]?\\d{4}\\b", // Tarjetas de crédito
"\\b\\d{3}-\\d{2}-\\d{4}\\b" // SSN
],
"MaskValue": "***REDACTED***",
"MaskPartial": true,
"PartialMaskLength": 4
}
}
}
Ejemplo:
_loggingService.LogInformation("Usuario autenticado", "Login", "Security",
properties: new Dictionary<string, object>
{
{ "Email", "user@example.com" }, // Se enmascara
{ "Password", "secret123" }, // Se enmascara completamente
{ "CreditCard", "1234-5678-9012-3456" } // Se muestra como: ***REDACTED***3456
});
Beneficios:
- Cumplimiento con GDPR, PCI-DSS, HIPAA
- Seguridad mejorada
- Prevención de exposición accidental de datos
Filtrado Dinámico Pre-Sink
Los filtros se aplican automáticamente antes de enviar logs a los sinks:
{
"StructuredLogging": {
"Filters": {
"ExcludedCategories": ["Debug", "Trace"],
"ExcludedOperations": ["HealthCheck"],
"ExcludedUsers": ["System"],
"FilterByLogLevel": true,
"CategoryLogLevels": {
"Security": "Warning",
"Performance": "Information"
},
"OperationLogLevels": {
"GetUser": "Debug",
"CreateOrder": "Information"
}
}
}
}
Beneficios:
- Reduce logs innecesarios
- Mejora performance
- Ahorra costos de almacenamiento
Configuración Dinámica Avanzada (Hot-Reload)
El componente soporta cambios dinámicos de configuración sin reiniciar:
Cambio de Nivel por Categoría/Operación
<<<<<<< HEAD
using JonjubNet.Logging.Application.Interfaces; // Para ILoggingConfigurationManager
using Microsoft.AspNetCore.Mvc;
=======
using JonjubNet.Logging.Application.Interfaces;
>>>>>>> 6b8317a7f8fd86192c146f543abc241ef855a4cf
// En un controlador o servicio
public class LoggingController : ControllerBase
{
private readonly ILoggingConfigurationManager _configManager;
public LoggingController(ILoggingConfigurationManager configManager)
{
_configManager = configManager;
}
[HttpPost("logging/level")]
public IActionResult ChangeLogLevel([FromBody] ChangeLevelRequest request)
{
// Cambiar nivel global
_configManager.SetMinimumLevel(request.Level);
// Cambiar nivel por categoría
_configManager.SetCategoryLogLevel(request.Category, request.Level);
// Cambiar nivel por operación
_configManager.SetOperationLogLevel(request.Operation, request.Level);
return Ok();
}
[HttpPost("logging/override")]
public IActionResult SetTemporaryOverride([FromBody] OverrideRequest request)
{
// Override temporal con expiración automática
_configManager.SetTemporaryLogLevelOverride(
request.Category,
request.Level,
TimeSpan.FromMinutes(request.DurationMinutes));
return Ok();
}
}
Hot-Reload desde appsettings.json
Si tienes reloadOnChange: true en la configuración, los cambios en appsettings.json se detectan automáticamente:
builder.Configuration
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
Beneficios:
- Cambios sin reiniciar la aplicación
- Overrides temporales para debugging
- Configuración por categoría/operación específica
Resiliencia y Circuit Breakers
El componente incluye circuit breakers, retry policies y dead letter queue para garantizar la entrega de logs:
{
"StructuredLogging": {
"CircuitBreaker": {
"Enabled": true,
"Default": {
"FailureThreshold": 5,
"OpenTimeout": "00:01:00",
"HalfOpenTestCount": 3
},
"PerSink": {
"Http": {
"Enabled": true,
"FailureThreshold": 3
}
}
},
"RetryPolicy": {
"Enabled": true,
"Default": {
"Strategy": "ExponentialBackoff",
"MaxRetries": 3,
"InitialDelay": "00:00:01",
"MaxDelay": "00:00:30"
}
},
"DeadLetterQueue": {
"Enabled": true,
"MaxSize": 10000,
"AutoRetry": true,
"RetryInterval": "00:05:00"
}
}
}
Estrategias de Retry disponibles:
NoRetry- Sin reintentosFixedDelay- Delay fijo entre intentosExponentialBackoff- Delay exponencial crecienteJitteredExponentialBackoff- Exponential backoff con jitter
Beneficios:
- Protección contra fallos de sinks
- Reintentos automáticos con backoff
- Dead letter queue para logs fallidos
- Recuperación automática
Batch Processing Avanzado
El componente agrupa logs en batches para mejorar el rendimiento:
{
"StructuredLogging": {
"Batching": {
"Enabled": true,
"DefaultBatchSize": 100,
"MaxBatchIntervalMs": 1000,
"BatchSizeBySink": {
"Http": 200,
"Elasticsearch": 500
},
"EnableCompression": false,
"CompressionLevel": "Optimal",
"EnablePriorityQueues": false,
"EnablePriorityProcessing": false
}
}
}
Características:
- Batching Inteligente: Agrupa logs por tiempo/volumen
- Compresión: GZip para batches (opcional, deshabilitado por defecto)
- Priorización: Colas separadas por nivel de log (opcional, deshabilitado por defecto)
- Procesamiento Prioritario: Errores críticos se procesan primero (opcional)
Beneficios:
- Mejora significativa de rendimiento
- Reduce llamadas a sinks
- Optimización de ancho de banda (con compresión)
- Priorización de logs críticos (si está habilitado)
🔧 Personalización
Servicio de Usuario Personalizado
Si necesitas obtener información del usuario desde JWT, sesiones o headers personalizados:
Paso 1: Crear tu Implementación
<<<<<<< HEAD
using JonjubNet.Logging.Application.Interfaces; // Para ICurrentUserService
=======
using JonjubNet.Logging.Application.Interfaces;
>>>>>>> 6b8317a7f8fd86192c146f543abc241ef855a4cf
using Microsoft.AspNetCore.Http;
using System.Security.Claims;
public class CustomUserService : ICurrentUserService
{
private readonly IHttpContextAccessor _httpContextAccessor;
public CustomUserService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public string? GetCurrentUserId()
{
var httpContext = _httpContextAccessor.HttpContext;
if (httpContext?.User?.Identity?.IsAuthenticated == true)
{
var subClaim = httpContext.User.FindFirst(ClaimTypes.NameIdentifier) ??
httpContext.User.FindFirst("sub");
return subClaim?.Value;
}
return null;
}
public string? GetCurrentUserName()
{
var httpContext = _httpContextAccessor.HttpContext;
if (httpContext?.User?.Identity?.IsAuthenticated == true)
{
var nameClaim = httpContext.User.FindFirst(ClaimTypes.Name) ??
httpContext.User.FindFirst("name");
return nameClaim?.Value;
}
return null;
}
public string? GetCurrentUserEmail()
{
var httpContext = _httpContextAccessor.HttpContext;
if (httpContext?.User?.Identity?.IsAuthenticated == true)
{
var emailClaim = httpContext.User.FindFirst(ClaimTypes.Email) ??
httpContext.User.FindFirst("email");
return emailClaim?.Value;
}
return null;
}
public IEnumerable<string> GetCurrentUserRoles()
{
var httpContext = _httpContextAccessor.HttpContext;
if (httpContext?.User?.Identity?.IsAuthenticated == true)
{
return httpContext.User.FindAll(ClaimTypes.Role)
.Select(c => c.Value)
.Distinct();
}
return new List<string>();
}
public bool IsInRole(string role)
{
var httpContext = _httpContextAccessor.HttpContext;
return httpContext?.User?.IsInRole(role) ?? false;
}
public bool IsAuthenticated()
{
var httpContext = _httpContextAccessor.HttpContext;
return httpContext?.User?.Identity?.IsAuthenticated == true;
}
}
Paso 2: Registrar tu Servicio Personalizado
// En Program.cs o Startup.cs
builder.Services.AddStructuredLoggingInfrastructure<CustomUserService>(builder.Configuration);
Configuración de Kafka
Para habilitar el envío de logs a Kafka:
{
"StructuredLogging": {
"KafkaProducer": {
"Enabled": true,
"BootstrapServers": "localhost:9092",
"Topic": "structured-logs",
"TimeoutSeconds": 5,
"BatchSize": 100,
"EnableCompression": true,
"CompressionType": "gzip"
}
}
}
Opciones de Conexión a Kafka:
Conexión Nativa (BootstrapServers): Conexión directa usando el protocolo binario de Kafka
"BootstrapServers": "localhost:9092"REST Proxy (ProducerUrl): A través de Kafka REST Proxy
"ProducerUrl": "http://kafka-rest:8082", "UseWebhook": falseWebhook HTTP (ProducerUrl + UseWebhook): Envío directo a endpoint webhook
"ProducerUrl": "https://webhook-url/api/logs", "UseWebhook": true
Configuración de Elasticsearch
{
"StructuredLogging": {
"Sinks": {
"EnableElasticsearch": true,
"Elasticsearch": {
"Url": "http://localhost:9200",
"IndexFormat": "logs-{0:yyyy.MM.dd}",
"EnableAuthentication": true,
"Username": "elastic",
"Password": "password"
}
}
}
}
Configuración de Filtros
{
"StructuredLogging": {
"Filters": {
"ExcludedCategories": ["Debug", "Trace"],
"ExcludedOperations": ["HealthCheck"],
"ExcludedUsers": ["System"],
"FilterByLogLevel": true,
"CategoryLogLevels": {
"Security": "Warning",
"Performance": "Information"
}
}
}
}
🏗️ Arquitectura
Este componente está implementado siguiendo Clean Architecture con las siguientes capas:
JonjubNet.Logging/
├── Core/
│ ├── Domain/ # Entidades y Value Objects
│ └── Application/ # Interfaces y Casos de Uso
├── Infrastructure/
│ ├── Shared/ # Implementaciones compartidas
│ └── Persistence/ # Persistencia (si aplica)
└── Presentation/
└── JonjubNet.Logging/ # Punto de entrada y extensión
Principios Aplicados
- Dependency Rule: Las dependencias apuntan hacia adentro (Infrastructure → Application → Domain)
- Independencia de Frameworks: La capa Application no depende de ASP.NET Core
- Testabilidad: Todas las dependencias están abstraídas mediante interfaces
- Separación de Responsabilidades: Cada capa tiene una responsabilidad clara
📝 Notas Importantes
Configuración en Microservicios
Lectura Automática: El componente lee automáticamente la sección
"StructuredLogging"delappsettings.jsondel microservicio cuando pasasIConfigurational métodoAddStructuredLoggingInfrastructure().Hot-Reload: Si configuras
reloadOnChange: trueenAddJsonFile(), los cambios enappsettings.jsonse detectan automáticamente sin reiniciar.Valores por Defecto: Si falta un parámetro en la configuración, el componente usa valores por defecto sensatos.
Múltiples Entornos: Puedes tener diferentes configuraciones en
appsettings.Development.json,appsettings.Production.json, etc.
Seguridad y Performance
Headers Sensibles: Por defecto, los headers sensibles (Authorization, Cookie, etc.) no se capturan en los logs por seguridad.
Tamaño de Body: El tamaño máximo del body HTTP capturado es configurable (por defecto 10KB). Si el body es mayor, se trunca.
Performance: El logging se realiza de forma asíncrona para no bloquear el hilo principal.
Batching: Los logs se agrupan en batches para mejorar el rendimiento (habilitado por defecto).
Resiliencia: Circuit breakers y retry policies protegen contra fallos de sinks.
Compatibilidad
ASP.NET Core: Soporta ASP.NET Core 5.0+ con
IHostyBackgroundService.Aplicaciones sin Host: Soporta aplicaciones sin host (Console Apps) usando
AddStructuredLoggingInfrastructureWithoutHost()y procesamiento síncrono.Registros Condicionales: El componente detecta automáticamente si
IHttpContextAccessoryBackgroundServiceestán disponibles y se adapta.Serilog: El componente usa Serilog base, sin depender de
Serilog.AspNetCore.
🔧 Troubleshooting
Problema: Los logs no aparecen
Solución:
- Verificar que
Enabled: trueen la configuración deappsettings.json - Verificar el nivel mínimo de log (
MinimumLevel) - Revisar los filtros configurados (
Filters) - Verificar que los sinks estén habilitados (
EnableConsole,EnableFile, etc.)
Problema: Error de compilación al instalar
Solución: <<<<<<< HEAD
Asegúrate de instalar solo el paquete principal:
dotnet add package JonjubNet.Logging❌ NO instales
JonjubNet.Logging.Shared,JonjubNet.Logging.Domain, oJonjubNet.Logging.Applicationcomo paquetes separados.Verificar versión de .NET (requiere .NET 8.0 o superior)
Verificar que todas las dependencias estén instaladas correctamente
Limpiar y reconstruir la solución: =======
Verificar versión de .NET (requiere .NET 8.0 o superior)
Verificar que todas las dependencias estén instaladas correctamente
Limpiar y reconstruir la solución:
6b8317a7f8fd86192c146f543abc241ef855a4cf
dotnet clean
dotnet restore
dotnet build
<<<<<<< HEAD 5. Si el error persiste, verifica que los namespaces estén correctamente importados:
using JonjubNet.Logging; // Para ServiceExtensions
using JonjubNet.Logging.Application.Interfaces; // Para IStructuredLoggingService, ICurrentUserService
using JonjubNet.Logging.Shared; // Para extensiones de Shared (si es necesario)
=======
6b8317a7f8fd86192c146f543abc241ef855a4cf
Problema: Los logs no se envían a Elasticsearch/HTTP
Solución:
- Verificar que el sink esté habilitado (
EnableElasticsearch: trueoEnableHttp: true) - Verificar la URL y credenciales en la configuración
- Revisar el estado del circuit breaker (puede estar abierto por fallos previos)
- Verificar la configuración de retry policies
- Revisar la Dead Letter Queue para logs fallidos
Problema: Hot-reload no funciona
Solución:
- Verificar que
reloadOnChange: trueesté configurado:builder.Configuration .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); - Verificar que el archivo
appsettings.jsontenga permisos de lectura/escritura - Reiniciar la aplicación si es necesario
Problema: Performance degradada
Solución:
- Habilitar batching si no está habilitado (
Batching.Enabled: true) - Ajustar el tamaño de batch según tu volumen de logs
- Habilitar sampling para reducir volumen (
Sampling.Enabled: true) - Revisar filtros para excluir logs innecesarios
- Verificar que los sinks no estén bloqueando (revisar circuit breakers)
Problema: Datos sensibles aparecen en logs
Solución:
- Verificar que
DataSanitization.Enabled: true - Agregar nombres de propiedades sensibles a
SensitivePropertyNames - Agregar patrones regex a
SensitivePatternssi es necesario - Verificar que los headers sensibles estén en
HttpCapture.SensitiveHeaders
📖 Documentación Adicional
- Mejores Prácticas de Documentación - Guía de mejores prácticas
- Evaluación de Producción - Análisis técnico y roadmap
🤝 Contribuir
Las contribuciones son bienvenidas. Por favor, abre un issue o pull request.
📄 Licencia
Este proyecto está licenciado bajo la licencia MIT.
<<<<<<< HEAD Versión: 1.0.42
Autor: JonjubNet
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Confluent.Kafka (>= 2.12.0)
- MediatR (>= 12.4.1)
- Microsoft.AspNetCore.Http.Abstractions (>= 2.3.0)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0)
- Microsoft.Extensions.Logging (>= 10.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.0)
- Microsoft.Extensions.ObjectPool (>= 10.0.0)
- Microsoft.Extensions.Options (>= 10.0.0)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.0)
- Serilog (>= 4.3.0)
- Serilog.Enrichers.Environment (>= 3.0.1)
- Serilog.Enrichers.Process (>= 3.0.0)
- Serilog.Enrichers.Thread (>= 4.0.0)
- Serilog.Formatting.Compact (>= 3.0.0)
- Serilog.Sinks.Console (>= 6.1.1)
- Serilog.Sinks.File (>= 7.0.0)
- Serilog.Sinks.Http (>= 9.2.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.1.3 | 314 | 12/9/2025 |
| 3.1.0 | 283 | 12/8/2025 |
| 3.0.12 | 280 | 12/8/2025 |
| 3.0.11 | 281 | 12/8/2025 |
| 3.0.10 | 279 | 12/8/2025 |
| 3.0.9 | 280 | 12/7/2025 |
| 3.0.8 | 275 | 12/7/2025 |
| 3.0.7 | 279 | 12/7/2025 |
| 3.0.6 | 281 | 12/7/2025 |
| 3.0.5 | 191 | 12/7/2025 |
| 3.0.4 | 191 | 12/7/2025 |
| 3.0.3 | 196 | 12/7/2025 |
| 3.0.2 | 195 | 12/7/2025 |
| 3.0.1 | 198 | 12/7/2025 |
| 2.0.1 | 195 | 12/7/2025 |
| 2.0.0 | 190 | 12/7/2025 |
| 1.1.4 | 267 | 12/10/2025 |
| 1.0.50 | 198 | 12/7/2025 |
| 1.0.38 | 147 | 12/5/2025 |
| 1.0.37 | 143 | 12/5/2025 |
| 1.0.36 | 144 | 12/5/2025 |
| 1.0.35 | 146 | 12/5/2025 |
| 1.0.34 | 174 | 12/4/2025 |
| 1.0.33 | 174 | 12/4/2025 |
| 1.0.24 | 168 | 11/27/2025 |
| 1.0.23 | 168 | 11/27/2025 |
| 1.0.22 | 174 | 11/27/2025 |
| 1.0.20 | 173 | 11/27/2025 |
| 1.0.18 | 170 | 11/27/2025 |
| 1.0.17 | 171 | 11/27/2025 |
| 1.0.16 | 170 | 11/27/2025 |
| 1.0.15 | 173 | 11/27/2025 |
| 1.0.14 | 178 | 11/26/2025 |
| 1.0.13 | 174 | 11/26/2025 |
| 1.0.12 | 172 | 11/26/2025 |
| 1.0.11 | 384 | 11/20/2025 |
| 1.0.8 | 382 | 11/20/2025 |
| 1.0.7 | 390 | 11/19/2025 |
| 1.0.6 | 379 | 11/19/2025 |
| 1.0.4 | 387 | 11/19/2025 |
| 1.0.3 | 193 | 12/7/2025 |
| 1.0.2 | 172 | 10/20/2025 |
| 1.0.1 | 169 | 10/15/2025 |
| 1.0.0 | 174 | 10/14/2025 |