Descripción
Modelo Django que almacena entradas de auditoría del sistema: quién hizo qué, cuándo, en qué organización y con qué detalles. Trazabilidad completa de mutaciones sensibles.
Tabla: core_systemlog
Ubicación: core.models.SystemLog
Disponible desde: v1.0.73 (con categorías ITSM y OBSERVATORY desde PR #71; CREDENTIAL desde v1.103.0 / commit d14ebb58)
Campos
| Campo | Tipo | Nuleable | Descripción |
|---|---|---|---|
id | AutoField | No | Clave primaria |
timestamp | DateTimeField | No | Instante del evento (siempre timezone.now()) |
user | ForeignKey(User) | Sí | Operador que realizó la acción. None si anónimo o token sin identidad |
organization | ForeignKey(Organization) | Sí | Org afectada. None si a nivel de sistema (raro) |
level | CharField(max_length=10) | No | Severidad: INFO, WARN, ERROR |
category | CharField(max_length=50) | No | Categoría de la acción (ver tabla abajo) |
action | CharField(max_length=200) | No | Identificador de la acción (ej: channel.create, alert.delete) |
details | TextField | Sí | Payload libre — JSON, texto plano o URL-encoded (sin restricción de formato) |
Categorías (CATEGORY_CHOICES)
| Clave | Etiqueta | Uso | Introducido |
|---|---|---|---|
AUTH | Authentication | Logins, logouts, cambios de contraseña | v1.0.0 |
RACK | Rack Management | CRUD de racks, cambios de estado | v1.0.0 |
BLUEPRINT | Blueprint Management | Cambios en blueprints | v1.0.0 |
SYSTEM | System | Jobs, rebalances, maintenance interno | v1.0.0 |
NETWORK | Network Management | Cambios en topología de red (CNS) | v1.0.0 |
ITSM | ITSM Management | Canales, escalados, SLA, mantenimiento, runbooks | PR #71 (2026-06-07) |
OBSERVATORY | Observatory Management | Alertas, wireless, UPS, descubrimientos | PR #71 (2026-06-07) |
CREDENTIAL | Credential Store | Alta/edición/borrado/desencriptado de credenciales guardadas | v1.103.0 / commit d14ebb58 (2026-09-03) |
Acciones por categoría (no exhaustivo)
ITSM:
sla.update— Cambio de política de SLAchannel.create,channel.update,channel.delete— Gestión de canales de notificaciónescalation.create,escalation.update,escalation.delete— Políticas de escaladomaintenance.create,maintenance.update,maintenance.delete— Ventanas de mantenimientorunbook.create,runbook.delete,runbook.apply— Runbooks
OBSERVATORY:
alert.delete,alert.resolve,alert.threshold— Alertaswireless.deep_discover,wireless.group_assign— APs y wireless controllersups.deep_discover,ups.group_assign— UPS
CREDENTIAL:
credential.create,credential.update,credential.delete— Alta, edición y baja de una credencial guardadacredential.decrypt— Acceso al secreto en claro (p.ej. para conectar con un dispositivo)- El valor de la credencial NUNCA viaja en
details— solo metadata (qué credencial, quién, cuándo)
Índices
CREATE TABLE core_systemlog (
id BIGINT PRIMARY KEY,
timestamp TIMESTAMP NOT NULL,
user_id BIGINT NULL REFERENCES core_user(id),
organization_id BIGINT NULL REFERENCES core_organization(id),
level VARCHAR(10) NOT NULL,
category VARCHAR(50) NOT NULL,
action VARCHAR(200) NOT NULL,
details TEXT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_systemlog_org_category_action_ts
ON core_systemlog(organization_id, category, action, timestamp DESC);
CREATE INDEX idx_systemlog_org_user_ts
ON core_systemlog(organization_id, user_id, timestamp DESC);
CREATE INDEX idx_systemlog_level
ON core_systemlog(level, timestamp DESC);
Relaciones
- ForeignKey → User: El operador (anónimo si
user_id = NULL) - ForeignKey → Organization: La org afectada (respeta RLS)
- Sin FK a otras entidades: Los detalles se almacenan en texto (
details) para flexibilidad
Métodos y propiedades
__str__()
def __str__(self) -> str:
"""Retorna representación humana: '<2026-06-07 10:55 user=operator org=acme action=channel.create>'"""
Migración reciente: 0034
Cambio: Extensión de CATEGORY_CHOICES con CREDENTIAL (cola de auditoría, task #286).
# core/migrations/0034_systemlog_category_credential.py
# Generated: 2026-09-03
migrations.AlterField(
model_name='systemlog',
name='category',
field=models.CharField(
choices=[
('AUTH', 'Authentication'),
('RACK', 'Rack Management'),
('BLUEPRINT', 'Blueprint Management'),
('SYSTEM', 'System'),
('NETWORK', 'Network Management'),
('ITSM', 'ITSM Management'),
('OBSERVATORY', 'Observatory Management'),
('CREDENTIAL', 'Credential Store'), # NEW
],
max_length=50,
),
)
Motivo: core/credential_api.py gestionaba credenciales (crear/editar/borrar/desencriptar) sin dejar rastro en SystemLog — un operador podía leer el secreto de un dispositivo sin que quedara registro alguno. La cola de auditoría (task #286, 14 hallazgos MEDIA) lo detectó y añadió el rastro completo, sin loguear nunca el valor del secreto.
Impacto: No-op para datos existentes. Las nuevas mutaciones de credenciales quedan auditadas.
Migración anterior: 0023
Cambio: Extensión de CATEGORY_CHOICES con ITSM y OBSERVATORY.
# core/migrations/0023_alter_systemlog_category.py
# Generated: 2026-06-07
migrations.AlterField(
model_name='systemlog',
name='category',
field=models.CharField(
choices=[
('AUTH', 'Authentication'),
('RACK', 'Rack Management'),
('BLUEPRINT', 'Blueprint Management'),
('SYSTEM', 'System'),
('NETWORK', 'Network Management'),
('ITSM', 'ITSM Management'), # NEW
('OBSERVATORY', 'Observatory Management'), # NEW
],
max_length=50,
),
)
Impacto: No-op para datos existentes. Las nuevas mutaciones pueden usar las nuevas categorías.
Lectura de auditoría
API: GET /api/core/logs/ (si existe)
Típicamente expuesto via core/api/logs.py → SystemLogSchema (readonly):
class SystemLogSchema(Schema):
id: int
timestamp: datetime
level: str
category: str
action: str
details: str
user_id: Optional[int]
username: str # derived
Django admin
Registrado en core/admin.py para navegación manual (solo superusers).
Queries comunes
# Auditoría de un operador en una org
SystemLog.objects.filter(
organization=org,
user=operator,
timestamp__gte=start_date
).order_by('-timestamp')
# Todas las mutaciones de un tipo
SystemLog.objects.filter(
organization=org,
category='ITSM',
action='channel.delete'
)
# Logs de error
SystemLog.objects.filter(level='ERROR')
Retención y archivado
- Estrategia actual: Indefinida (considerar política de archivado anual)
- Tamaño estimado: ~200-300 bytes por entrada; ~1-2 MB/mes para org activa
- TTL recomendado: 7 años (SOC2/ISO27001 compliance)
Integración con observabilidad
Las entradas de SystemLog pueden ingerirse en Prometheus/Grafana vía:
- Endpoint expuesto (lectura filtrada por org)
- Exporter custom (convierte rows a métricas)
- ELK/Splunk (dump periódico a datawarehouse)
RLS
organizationsiempre se valida medianteget_current_org(request)antes de escribir- Un operador no puede auditar ni ver logs de orgs ajenas
- Si RLS falla en
log_action(), la excepción es capturada →organization=None - Código que corre FUERA de un request (tareas Huey) no fija el GUC de RLS por sí solo — necesita
core.utils.rls.rls_bypass(), ver [[incident—20260903—huey-tasks-sin-rls-bypass]]
Performance
- Escritura: Operación rápida (una INSERT); best-effort en
log_action()(nunca bloquea request) - Lectura: Índice en
(organization, category, timestamp DESC)→ <100ms para 1M rows - Vacuum: Índices sin fragmentación si hay INSERT masivos
Cambios futuros
- Compresión de
detailssi campo crece (JSONB con índices) - Particionado temporal (monthly tables) si >100M rows/año
- Exportación a S3/Datadog para compliance reporting
Véase también
- [[entity—core—service—log-action]] — Helper que escribe en este modelo
- [[feature—monitoring—audit-logs-mutaciones-sa4-sa5]] — Feature que añadió categorías ITSM/OBSERVATORY
- [[decision—20260607—audit-logs-best-effort]] — ADR sobre la estrategia best-effort
Referenciado desde
- ADR: log_action() best-effort (nunca rompe request)
- Cambiar de Primary deja de ser un clic mudo — confirmación, historial y aviso al equipo
- Cerrar Broken Access Control en el CRUD de racks y en /api/settings
- Incident: el borrado manual de una organización no dejaba traza ni limpiaba sus copias de seguridad
- Incident: tareas Huey (purga, integridad, monitor de conexiones) corrían sin RLS — inertes en PROD
- Modelo AgentRoleEvent — historial de cambios de rol de la flota
- Organization · Modelo core
- RLS WITH CHECK — aislamiento de escritura cross-tenant (Hito D)