Logging & Observability¶
SentriKat writes structured logs to /var/log/sentrikat/ inside the container. Logs are split per concern so a SIEM can ingest the security and audit streams separately from application noise.
Log files¶
| File | Level | Content | Retention |
|---|---|---|---|
application.log | INFO+ | App boot, scheduler ticks, Flask request handlers info/warning | 10 MB × 10 backups (~100 MB) |
error.log | ERROR | Uncaught exceptions, sanitized 500 errors, stack traces | 10 MB × 10 backups |
access.log | INFO | HTTP request access (method, path, status, IP, UA) | 20 MB × 10 backups |
security.log | WARNING+ | Auth events, license rejections, permission denials, lockouts | 10 MB × 20 backups (~200 MB) |
audit.log | INFO (JSON) | Privileged CRUD on user/role/setting/integration | 20 MB × 50 backups (~1 GB) |
ldap.log | INFO | LDAP operations (bind, search, sync, auto-provision) | 10 MB × 10 backups |
performance.log | INFO (JSON) | Slow queries (>200ms), slow endpoints | 20 MB × 10 backups |
Rotation is automatic and by size: when the backups of a file are used up, the oldest is overwritten. Retention is therefore a size, not a period.
Logs are lost on restart unless you mount them
By default /var/log/sentrikat/ is in memory (see Mounting logs to host), and audit.log is lost with the rest when the container restarts. If you need the audit and security streams to survive, mount the directory on the host and forward them to your SIEM.
Ownership and permissions¶
All log files are owned by the sentrikat system user (UID assigned at build time, check with docker compose exec sentrikat id) inside the container. The entrypoint docker-entrypoint.sh drops privileges from root to sentrikat via gosu before starting gunicorn, so the master and all workers create files with the correct ownership.
If you ever see logs only populated with the boot lines (~6 entries in application.log, everything else 0 bytes), check ownership first:
docker compose exec -T sentrikat ls -la /var/log/sentrikat/
# Expected: every file 'sentrikat sentrikat'
# Wrong: 'root root' → privilege drop didn't happen
Mounting logs to host¶
By default /var/log/sentrikat/ is a tmpfs (in-memory) under the read-only rootfs. Logs are lost on container restart. To persist:
Option A: STORAGE_ROOT env var¶
services:
sentrikat:
environment:
STORAGE_ROOT: /data/sentrikat
volumes:
- /host/path/sentrikat-data:/data/sentrikat
The entrypoint derives LOG_DIR=$STORAGE_ROOT/logs, DATA_DIR=$STORAGE_ROOT/data, BACKUP_DIR=$STORAGE_ROOT/backups automatically. Make sure the host path is owned by the container's sentrikat UID (check with docker compose exec sentrikat id):
Option B: explicit LOG_DIR¶
If the host path can't be chowned (NFS readonly), the entrypoint falls back to /app/logs inside the container (lost on restart).
Shipping logs externally¶
For vulnerability events sent to a SIEM, use SIEM Integration. To ship the log files themselves, mount the directory on the host (above) and collect the files with a log agent that runs as a member of the group that owns the directory (the directory is mode 750). Syslog, where SentriKat sends it, is plaintext: keep the collector on a trusted network segment.
Diagnostics: logs not populating¶
# Check ownership
docker compose exec -T sentrikat ls -la /var/log/sentrikat/
# Check line counts
docker compose exec -T sentrikat sh -c "wc -l /var/log/sentrikat/*.log"
# Confirm gunicorn workers re-initialized post_fork
docker compose logs sentrikat 2>&1 | grep "re-initialized DB pool + logging"
# Expected: one line per worker (typically 4-16 lines)