BTW.Logging.Loki
1.1.0
dotnet add package BTW.Logging.Loki --version 1.1.0
NuGet\Install-Package BTW.Logging.Loki -Version 1.1.0
<PackageReference Include="BTW.Logging.Loki" Version="1.1.0" />
<PackageVersion Include="BTW.Logging.Loki" Version="1.1.0" />
<PackageReference Include="BTW.Logging.Loki" />
paket add BTW.Logging.Loki --version 1.1.0
#r "nuget: BTW.Logging.Loki, 1.1.0"
#:package BTW.Logging.Loki@1.1.0
#addin nuget:?package=BTW.Logging.Loki&version=1.1.0
#tool nuget:?package=BTW.Logging.Loki&version=1.1.0
BTW.Logging.Loki
Paquete NuGet para configuración centralizada de logging con Serilog y Grafana Loki para proyectos BTW.
📋 Tabla de Contenidos
- Características
- Instalación
- Configuración Rápida
- Configuración Avanzada
- Propiedades Automáticas
- Consultas en Grafana Loki
- Ejemplo Completo
- Troubleshooting
- Soporte
✨ Características
- ⚡ Configuración de Serilog en 1 línea de código
- 📊 Envío automático de logs a Grafana Loki
- 🔗 CorrelationId automático en todos los requests
- 📱 Detección de dispositivos (Desktop, Mobile, Tablet)
- 🤖 Detección de bots y crawlers
- ⏱️ Medición de tiempo de respuesta (ElapsedMs)
- 🏢 Soporte multi-tenancy (opcional)
- 🚫 Filtros automáticos para health checks, swagger y metrics
- 🔇 Namespaces de terceros silenciados por defecto (Microsoft, EntityFrameworkCore, System)
- 📝 Logs estructurados en formato JSON
- 🔐 Integración con Infisical para gestión de secretos
📦 Instalación
dotnet add package BTW.Logging.Loki
🚀 Configuración Rápida
Paso 1: Configurar appsettings.json
{
"Loki": {
"ServiceName": "mi-servicio-api"
}
}
Paso 2: Configurar URL de Loki en Infisical
Agregar la siguiente variable en Infisical:
Loki__Url=http://0.0.0.0:3100
Paso 3: Actualizar Program.cs
using BTW.Logging.Loki.Extensions;
var builder = WebApplication.CreateBuilder(args);
// Cargar secretos de Infisical
builder.AddInfisicalSecrets(); // Loki__Url viene de aquí
// ⚡ Configurar logging (1 línea)
builder.AddBTWLogging();
// ...resto de servicios
builder.Services.AddControllers();
var app = builder.Build();
// 🔧 Activar logging (1 línea) - DEBE IR PRIMERO
app.UseBTWLogging();
// ...resto de middlewares
app.MapControllers();
app.Run();
⚙️ Configuración Avanzada
Sinks adicionales (auditoría, alertas, etc.)
AddBTWLogging acepta un callback opcional configureSinks que recibe el LoggerConfiguration antes de crear el logger. Úsalo para registrar sinks adicionales sin modificar el paquete.
builder.AddBTWLogging(configureSinks: lc =>
{
lc.WriteTo.Logger(inner => inner
.Filter.ByIncludingOnly(e => e.Properties.ContainsKey("AuditEventType"))
.WriteTo.PostgreSQL(
connStr,
"invocations",
columnOptions,
schemaName: "audit",
needAutoCreateTable: false));
});
El callback se invoca después de aplicar toda la configuración base de Loki (nivel mínimo, filtros, enriquecedores) y antes de CreateLogger(). Los sinks inyectados conviven con Loki y consola.
Para filtrar eventos hacia un sink específico usa el patrón sub-logger (WriteTo.Logger(inner => inner.Filter...)): así solo ese sink recibe los eventos que le corresponden, sin afectar el resto del pipeline.
Opciones Completas
{
"Loki": {
"ServiceName": "mi-servicio-api",
"Environment": "Production",
"MinimumLevel": "Warning",
"EnableLoki": true,
"FilterHealthChecks": true,
"FilterSwagger": true,
"FilterMetrics": true,
"SampleSuccessfulRequests": 0,
"MinimumLevelOverrides": {
"Microsoft.EntityFrameworkCore": "Warning"
}
}
}
📋 Parámetros de Configuración
| Parámetro | Descripción | Valor por Defecto | Requerido |
|---|---|---|---|
ServiceName |
Nombre del servicio (aparece como label app en Loki) |
- | ✅ Sí |
Environment |
Ambiente de ejecución (aparece como label env en Loki) |
Production |
No |
MinimumLevel |
Nivel mínimo de log: Information, Warning, Error |
Warning |
No |
EnableLoki |
Habilitar envío de logs a Loki | true |
No |
FilterHealthChecks |
Filtrar endpoints /health de los logs |
true |
No |
FilterSwagger |
Filtrar endpoints /swagger de los logs |
true |
No |
FilterMetrics |
Filtrar endpoints /metrics de los logs |
true |
No |
SampleSuccessfulRequests |
Loguear 1 de cada N requests exitosos (0 = sin sampling) | 0 |
No |
MinimumLevelOverrides |
Activar logs de namespaces de terceros | {} |
No |
⚠️ Importante: Namespaces Silenciados por Defecto
Por defecto, el paquete silencia completamente los logs de los siguientes namespaces de terceros:
Microsoft(nivel Fatal)Microsoft.AspNetCore(nivel Fatal)Microsoft.EntityFrameworkCore(nivel Fatal)System(nivel Fatal)System.Net.Http(nivel Fatal)
Esto significa que NO verás ningún log de estos namespaces a menos que los actives explícitamente en MinimumLevelOverrides.
Ejemplo - Activar logs de EntityFrameworkCore:
{
"Loki": {
"ServiceName": "mi-api",
"MinimumLevel": "Information",
"MinimumLevelOverrides": {
"Microsoft.EntityFrameworkCore": "Warning",
"Microsoft.EntityFrameworkCore.Database.Command": "Information"
}
}
}
📊 Propiedades Automáticas en Logs
Cada request automáticamente incluye las siguientes propiedades:
| Propiedad | Descripción |
|---|---|
CorrelationId |
ID único del request (propagado entre servicios) |
RequestPath |
Path del endpoint llamado |
RequestMethod |
Método HTTP (GET, POST, etc.) |
StatusCode |
Código HTTP de respuesta |
ElapsedMs |
Tiempo de respuesta en milisegundos |
ContainerId |
Nombre de la máquina/contenedor |
ClientIp |
IP del cliente |
RequestHost |
Host del request |
RequestScheme |
Esquema del request (http/https) |
ServiceName |
Nombre del servicio |
Environment |
Ambiente de ejecución |
TenantId |
ID del tenant (si aplica) o "no-tenant" |
DeviceType |
Tipo de dispositivo (Desktop, Mobile, Tablet, Other, Unknown) |
IsBot |
Si el request viene de un bot (true/false) |
RequestSizeKB |
Tamaño del request en KB |
🔍 Consultas en Grafana Loki
Ver todos los logs de un servicio
{app="mi-servicio-api"}
Filtrar por ambiente
{app="mi-servicio-api", env="production"}
Ver solo errores
{app="mi-servicio-api"} | json | Level="Error"
Requests que tardan más de 1 segundo
{app="mi-servicio-api"} | json | ElapsedMs > 1000
Promedio de tiempo de respuesta
avg_over_time({app="mi-servicio-api"} | json | unwrap ElapsedMs [5m])
Filtrar por CorrelationId
{app="mi-servicio-api"} | json | CorrelationId="abc-123-def"
Detectar requests desde mobile
{app="mi-servicio-api"} | json | DeviceType="Mobile"
Filtrar bots
{app="mi-servicio-api"} | json | IsBot="true"
Requests por tenant
{app="mi-servicio-api"} | json | TenantId="tenant1"
🛠️ Características del CorrelationIdMiddleware
El middleware incluye las siguientes capacidades:
- ✅ Exclusión de Health Checks: Detecta y excluye automáticamente endpoints de salud (
/health,/ready,/alive,/ping) - 🔗 Correlation ID: Genera o extrae el ID de correlación del header
X-Correlation-Idy lo propaga en la respuesta - 🏢 Multi-tenancy: Soporte opcional para aplicaciones multi-tenant mediante:
- Header
X-Tenant-Id - Detección automática por subdomain (ej:
tenant1.miapp.com)
- Header
- 📱 Detección de Dispositivos: Identifica si el request viene de Desktop, Mobile, Tablet u Otro dispositivo
- 🤖 Detección de Bots: Identifica requests de bots, crawlers, herramientas de testing (Postman, curl, wget, googlebot, etc.)
- 📦 Request Size: Registra el tamaño del request en KB
- ⏱️ Elapsed Time: Mide el tiempo de respuesta de cada request en milisegundos
- 🌐 Client IP: Captura la IP del cliente
🔧 Troubleshooting
No veo logs en Loki
- Verificar que
EnableLokiesté entrue - Verificar que
Loki__Urlesté configurado en Infisical - Verificar conectividad de red con el servidor Loki
- Revisar logs de consola para ver si hay errores de conexión
No veo CorrelationId en los logs
El CorrelationId se agrega automáticamente mediante LogContext. Asegúrate de:
- Llamar
app.UseBTWLogging()ANTES de otros middlewares - Los logs se generan dentro del contexto del request
No veo logs de EntityFrameworkCore/Microsoft
Por defecto, estos namespaces están silenciados (nivel Fatal). Si necesitas ver sus logs, actívalos explícitamente:
{
"Loki": {
"MinimumLevelOverrides": {
"Microsoft.EntityFrameworkCore": "Warning"
}
}
}
¿Cómo medir performance de endpoints?
Usa la propiedad ElapsedMs que se registra automáticamente en cada request:
# Requests que tardan más de 1 segundo
{app="mi-api"} | json | ElapsedMs > 1000
# Promedio de tiempo de respuesta
avg_over_time({app="mi-api"} | json | unwrap ElapsedMs [5m])
📝 Ejemplo Completo
Estructura del Proyecto
MiProyecto.Api/
├── Program.cs
├── appsettings.json
├── appsettings.Development.json
└── appsettings.Production.json
appsettings.json
{
"Loki": {
"ServiceName": "mi-proyecto-api",
"Environment": "Development",
"MinimumLevel": "Information",
"EnableLoki": false,
"FilterHealthChecks": true,
"FilterSwagger": true
}
}
appsettings.Production.json
{
"Loki": {
"Environment": "Production",
"MinimumLevel": "Warning",
"EnableLoki": true
}
}
Program.cs
using BTW.Logging.Loki.Extensions;
using Microsoft.AspNetCore.Builder;
var builder = WebApplication.CreateBuilder(args);
// 1. Cargar secretos de Infisical
builder.AddInfisicalSecrets();
// 2. Configurar logging (1 línea)
builder.AddBTWLogging();
// 3. Registrar servicios
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// 4. Health checks
builder.Services.AddHealthChecks();
var app = builder.Build();
// 5. Activar logging - DEBE IR PRIMERO
app.UseBTWLogging();
// 6. Middleware
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
// 7. Endpoints
app.MapControllers();
app.MapHealthChecks("/health");
app.Run();
Usar el Logger en un Controller
using Microsoft.AspNetCore.Mvc;
namespace MiProyecto.Api.Controllers;
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly ILogger<ProductsController> _logger;
public ProductsController(ILogger<ProductsController> logger)
{
_logger = logger;
}
[HttpGet]
public IActionResult GetProducts()
{
_logger.LogInformation("Obteniendo lista de productos");
try
{
// Lógica de negocio
var products = new[] { "Product 1", "Product 2" };
_logger.LogInformation("Se obtuvieron {Count} productos", products.Length);
return Ok(products);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error al obtener productos");
return StatusCode(500, "Error interno del servidor");
}
}
}
⚠️ Notas Importantes
- Orden de Middlewares:
app.UseBTWLogging()debe llamarse PRIMERO, antes que cualquier otro middleware - URL de Loki: Siempre debe venir de Infisical como
Loki__Urlpara mayor seguridad - ServiceName: Es el único campo obligatorio en la configuración
- Health Checks: Se excluyen automáticamente para evitar saturar los logs
- CorrelationId: Se propaga automáticamente en el header
X-Correlation-Idde la respuesta
📄 Licencia
Uso interno BTW Company
💬 Soporte
Para dudas o problemas, contactar al equipo de desarrollo de BTW.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
-
net8.0
- Serilog (>= 4.3.0)
- Serilog.AspNetCore (>= 10.0.0)
- Serilog.Enrichers.Environment (>= 3.0.1)
- 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.Grafana.Loki (>= 8.3.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.