$ man logs

A field-reference for reading, filtering, and rotating logs on Debian-family (Ubuntu, Debian, Mint) and Red Hat-family (RHEL, CentOS, Rocky, Fedora, Alma) systems — built for daily use at the terminal.

Viewing logs
Filtering / grep
Errors & severity
Services / systemd units
logrotate
Debian-based
Red Hat-based
01How logging works 02Log locations 03Viewing logs 04Filter by date/time 05Filter by service 06Filter by errors 07How logrotate works 08Rotation — Debian 09Rotation — RHEL 10Quick reference
01

How Linux logging works

syslog · rsyslog · journald

Almost every distro logs through one of two pipelines, and most modern ones run both together.

Classic syslog (rsyslog / syslog-ng)

Applications and the kernel send messages to a syslog daemon, which writes plain-text lines into flat files under /var/log/, routed by "facility" (auth, cron, kern, mail…) and "severity" (emerg → debug). This is the traditional model, still dominant on Debian/Ubuntu.

systemd-journald

systemd's own logging service captures stdout/stderr from every unit plus kernel and auth events into a binary, indexed journal. Queried with journalctl. Default and primary on RHEL/CentOS/Fedora; also present alongside rsyslog on Debian/Ubuntu.

App / kernel writes event
rsyslogd or journald receives it
Routed by facility.severity
Written to /var/log/*.log (rsyslog)
or
Written to journal binary (journald)
logrotate archives/compresses/prunes
Rule of thumb: if the box is RHEL 7+/Fedora/Rocky/Alma, reach for journalctl first. If it's Debian/Ubuntu, reach for /var/log/syslog and friends first — but both families have both tools available.
02

Common log locations

/var/log/*

File names and even their existence vary by distro. Table below shows the equivalent log for each family.

PurposeDEBIAN / UBUNTURHEL / CENTOS / FEDORA
All-purpose system log/var/log/syslog/var/log/messages
Authentication / sudo / SSH logins/var/log/auth.log/var/log/secure
Kernel ring buffer messages/var/log/kern.logjournalctl -k (or /var/log/dmesg)
Boot messages/var/log/boot.log/var/log/boot.log
Cron job output/var/log/cron.log (or in syslog)/var/log/cron
Package manager history/var/log/apt/history.log/var/log/yum.log & /var/log/dnf.log
Mail server/var/log/mail.log/var/log/maillog
X11 / display/var/log/Xorg.0.log/var/log/Xorg.0.log
Web server (Apache)/var/log/apache2/{access,error}.log/var/log/httpd/{access,error}_log
Nginx/var/log/nginx/{access,error}.log/var/log/nginx/{access,error}.log
Database (MySQL/MariaDB)/var/log/mysql/error.log/var/log/mariadb/mariadb.log
Firewall (UFW / firewalld)/var/log/ufw.logjournalctl -u firewalld
Login/logout & failed logins/var/log/wtmp, /var/log/btmp (via last, lastb)/var/log/wtmp, /var/log/btmp (via last, lastb)
systemd journal (both families)/var/log/journal/ (persistent, if enabled)  |  /run/log/journal/ (volatile, default)
Tip: confirm what's actually on disk before assuming a filename exists: ls -lh /var/log/ — package/app logs vary further by what's installed.
03

Viewing logs — step by step

cat · less · tail · journalctl

Step 1 — Quick look at the end of a file (most recent entries):

tail
tail -n 100 /var/log/syslog       # last 100 lines
tail -f /var/log/syslog            # follow in real time, like watching it live
tail -f /var/log/nginx/error.log -n 50  # start with last 50, then follow

Step 2 — Scroll through a full file (searchable, paginated):

less
less /var/log/auth.log
# inside less:  /pattern  = search forward   n = next match   N = prev match   G = end   g = start   q = quit

Step 3 — journalctl (systemd journal, RHEL default / also on Debian):

journalctl
journalctl                        # entire journal, oldest first, pipe into less automatically
journalctl -e                     # jump straight to the end (newest)
journalctl -f                     # follow live, like tail -f
journalctl -r                     # reverse order, newest first
journalctl -b                     # only since the current boot
journalctl -b -1                  # only the previous boot
journalctl -k                     # kernel messages only (like dmesg)
journalctl -p err                # priority filter: err and worse
Never edit a live log with vim/nano on a production box unless you know what you're doing — file locks and buffered writers can cause the daemon to lose its handle. Use less/tail to read; use logrotate or the app's own tools to truncate/manage.
04

Filter logs by date & time

grep · awk · journalctl --since

Plain-text syslog files (rsyslog format: Mon DD HH:MM:SS):

grep — date/time on flat files
# All lines from a specific calendar day
grep "Aug  8" /var/log/syslog            # note: two spaces if day < 10 (syslog pads with space)

# A specific date + hour
grep "Aug  8 14:" /var/log/syslog

# A precise time range using awk (14:00:00 to 14:30:00)
awk '$3 >= "14:00:00" && $3 <= "14:30:00"' /var/log/syslog

# Today only, dynamically
grep "$(date '+%b %e')" /var/log/syslog

ISO-timestamped logs (Nginx/Apache error logs, JSON logs, etc.):

grep — ISO 8601 timestamps
grep "2026-08-08" /var/log/nginx/error.log
grep "2026-08-08T14" /var/log/app/app.log       # hour 14

journalctl (works on both families, most flexible — human-friendly time expressions):

journalctl — time windows
journalctl --since "2026-08-08 14:00:00" --until "2026-08-08 14:30:00"
journalctl --since today
journalctl --since yesterday --until today
journalctl --since "1 hour ago"
journalctl --since "-15 min"
Why journalctl wins here: it parses natural-language and relative time strings ("2 hours ago", yesterday) — no regex needed. For flat files, always confirm the exact timestamp format first with head -n 3 file.log.
05

Filter logs by system service / unit

journalctl -u · grep by process tag

journalctl by systemd unit — cleanest method on both families:

journalctl -u
journalctl -u sshd                     # everything from the ssh daemon
journalctl -u nginx -f                # follow nginx live
journalctl -u docker --since today
journalctl -u cron -u sshd              # multiple units at once
systemctl list-units --type=service  # find exact unit names first

Flat-file syslog by process tag (the bracketed name in each line):

grep — process tag
grep 'sshd\[' /var/log/auth.log          # Debian: auth events tagged sshd[pid]
grep 'CRON\[' /var/log/syslog
grep 'sshd' /var/log/secure            # RHEL equivalent

Per-app logs (when the service writes its own file instead of syslog/journal):

app-specific logs
tail -f /var/log/nginx/error.log
tail -f /var/log/apache2/error.log          # Debian
tail -f /var/log/httpd/error_log            # RHEL (single _log, no .log)
06

Filter by system errors & severity

grep -Ei · journalctl -p
emerg (0)
alert (1)
crit (2)
err (3)
warning (4)
notice (5)
info (6)
debug (7)

Flat files — keyword grep (case-insensitive, whole words):

grep — error keywords
grep -i "error" /var/log/syslog
grep -Ei "error|fail|critical|denied" /var/log/syslog
grep -i "error" /var/log/syslog -A 3 -B 1   # show 1 line before / 3 lines after for context
grep -c -i "error" /var/log/syslog            # just count matches
grep -i "error" /var/log/*.log                # across every rotated/plain log file
zgrep -i "error" /var/log/syslog.2.gz          # search inside compressed rotated logs

journalctl — filter by real priority level (much more reliable than keyword grep):

journalctl -p
journalctl -p err                     # err and everything worse (crit, alert, emerg)
journalctl -p warning                 # warning and worse
journalctl -p 3                       # numeric priority also works (3 = err)
journalctl -p err -b                  # errors since this boot only
journalctl -p err --since "1 hour ago"

Failed services / units at a glance:

systemctl
systemctl --failed                    # list every unit currently in a failed state
systemctl status nginx              # status + last log lines for one unit
07

How logrotate works

/etc/logrotate.conf · cron / systemd timer

logrotate prevents logs from growing forever. It runs once a day (usually via cron or a systemd timer), reads config files, and for each matched log decides whether to rotate it.

Trigger: cron.daily or logrotate.timer
Reads /etc/logrotate.conf
Includes /etc/logrotate.d/*
Checks size/age condition per log
Renames current → .1 (or dated)
Compresses old copies (gzip)
Deletes copies past "rotate N"
Signals app to reopen log fd

Core directives you'll see in every config:

DirectiveMeaning
daily / weekly / monthlyHow often to consider rotating
rotate NKeep N old copies before deleting the oldest
compressGzip rotated files (delayed by one cycle if delaycompress also set)
size 100MForce rotation once file exceeds this size (overrides daily/weekly)
missingokDon't error if the log file is missing
notifemptySkip rotation if the log is empty
create 0640 www-data admRecreate an empty log file with these perms/owner after rotating
postrotate … endscriptShell commands run after rotation — typically to signal the app (reload/HUP)
sharedscriptsRun postrotate once total, not once per matched file
dateextName rotated files with a date suffix (e.g. syslog-20260808) instead of .1, .2
Why postrotate matters: most daemons keep the log file open by file descriptor. If you just rename the file, the app keeps writing into the renamed (now invisible) file. postrotate tells the app to close and reopen its log (e.g. systemctl reload nginx) so it starts writing to the new file. systemd-journald doesn't need this — the journal manages its own rotation via SystemMaxUse= in journald.conf.
08

Setting up log rotation DEBIAN / UBUNTU

/etc/logrotate.d/

Step 1 — Confirm logrotate is installed and running (it is by default via cron):

verify
dpkg -l | grep logrotate
cat /etc/cron.daily/logrotate     # the daily trigger script

Step 2 — Create a new rule for a custom app, e.g. /var/log/myapp/app.log:

/etc/logrotate.d/myapp
sudo nano /etc/logrotate.d/myapp
file contents
/var/log/myapp/app.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    create 0640 myapp adm
    postrotate
        systemctl reload myapp > /dev/null 2>&1 || true
    endscript
}

Step 3 — Validate the config before trusting it:

test / debug
sudo logrotate -d /etc/logrotate.d/myapp     # -d = dry run, prints what WOULD happen
sudo logrotate -f /etc/logrotate.d/myapp     # -f = force an actual rotation right now
cat /var/lib/logrotate/status              # see last-rotated timestamps
09

Setting up log rotation RHEL / CENTOS / FEDORA

/etc/logrotate.d/ · systemd timer

Step 1 — Confirm logrotate is installed and check its trigger mechanism (systemd timer on modern RHEL, not cron):

verify
rpm -q logrotate
systemctl list-timers | grep logrotate     # RHEL 8+/9 uses logrotate.timer
systemctl status logrotate.timer

Step 2 — Create the rule (identical syntax to Debian — logrotate itself is the same tool):

/etc/logrotate.d/myapp
sudo vi /etc/logrotate.d/myapp
file contents
/var/log/myapp/app.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    create 0640 myapp adm
    postrotate
        systemctl reload myapp > /dev/null 2>&1 || true
    endscript
}

Step 3 — SELinux: restore correct context after creating a new log path (RHEL-family specific!):

SELinux context
sudo semanage fcontext -a -t var_log_t "/var/log/myapp(/.*)?"
sudo restorecon -Rv /var/log/myapp

Step 4 — Validate:

test / debug
sudo logrotate -d /etc/logrotate.d/myapp     # dry run
sudo logrotate -f /etc/logrotate.d/myapp     # force rotate now
journald retention (RHEL default logging path): the systemd journal rotates/prunes itself independent of logrotate. Configure size/age caps in /etc/systemd/journald.conf:
/etc/systemd/journald.conf
SystemMaxUse=500M
SystemMaxFileSize=50M
MaxRetentionSec=30day
then apply with sudo systemctl restart systemd-journald.
10

Daily quick-reference

TaskCommand
Watch a log livetail -f /var/log/syslog
Watch a service livejournalctl -u nginx -f
Errors in the last hourjournalctl -p err --since "1 hour ago"
Today's activity for one servicejournalctl -u sshd --since today
Search across all rotated + live logszgrep -i "error" /var/log/syslog*
List currently failed servicessystemctl --failed
Force-test a logrotate rulesudo logrotate -d /etc/logrotate.d/<file>
Force an actual rotation nowsudo logrotate -f /etc/logrotate.d/<file>
Disk space used by logsdu -sh /var/log/* | sort -h
Journal disk usagejournalctl --disk-usage
Manually shrink the journalsudo journalctl --vacuum-size=200M