Skip to content

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):

sudo chown -R 999:999 /host/path/sentrikat-data

Option B: explicit LOG_DIR

environment:
  LOG_DIR: /var/log/sentrikat
volumes:
  - /host/path/sentrikat-logs:/var/log/sentrikat

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)

See also