Bash Logging

A log is a written diary of what a script did. Each line records an event with its time. When a scheduled job fails at night, the log explains what happened. Good logging turns a mystery into a quick fix.

The Flight Recorder Analogy

Airplanes carry a recorder that stores every important event during a flight. Investigators read it after a problem. A script log serves the same purpose for your automation.

  Script runs
     |
     +--> INFO   Backup started
     +--> INFO   Copied 42 files
     +--> WARN   Disk 85% full
     +--> ERROR  Upload failed
     |
     v
  backup.log  (read later to find the cause)

Simple Logging With echo

#!/bin/bash
log_file="job.log"

echo "Job started" >> "$log_file"
echo "Job finished" >> "$log_file"

The double arrow appends a line. A single arrow would replace the whole file each time.

Add Timestamps

echo "$(date '+%Y-%m-%d %H:%M:%S') Job started" >> job.log

Log line:

2026-10-16 14:30:45 Job started

Build a Reusable Log Function

#!/bin/bash
log_file="app.log"

log() {
  local level="$1"
  shift
  echo "$(date '+%Y-%m-%d %H:%M:%S') [$level] $*" >> "$log_file"
}

log INFO "Script started"
log WARN "Disk space is low"
log ERROR "Upload failed"

Content of app.log:

2026-10-16 14:30:45 [INFO] Script started
2026-10-16 14:30:45 [WARN] Disk space is low
2026-10-16 14:30:45 [ERROR] Upload failed

The function stays in one place, so any format change applies to every message at once.

Standard Output and Standard Error

Programs write to two streams. Normal results use stream 1. Error messages use stream 2.

RedirectEffect
> fileSave normal output, replace the file
>> fileSave normal output, append to the file
2> fileSave only errors
> file 2>&1Save both streams in one file
&> fileShort form for both streams in Bash

Capture Everything From a Script

The exec command redirects all later output of the script into a file:

#!/bin/bash
exec >> run.log 2>&1

echo "This line goes to the log"
ls /missing_folder
echo "So does the error above"

Show Output and Save It Too

The tee command copies output to the screen and to a file:

./backup.sh 2>&1 | tee -a backup.log

The -a option appends instead of replacing.

Send Errors to the Error Stream

error() {
  echo "ERROR: $*" >&2
}

error "File not found"

Separate streams let users hide normal output but still see problems.

Use the System Logger

The logger command writes into the system log, which collects messages from many programs:

logger -t mybackup "Backup finished"
logger -p user.error -t mybackup "Backup failed"

View entries with journalctl -t mybackup or by reading /var/log/syslog on many Linux systems.

Read Logs Quickly

tail -n 20 app.log          # last 20 lines
tail -f app.log             # follow new lines live
grep "ERROR" app.log        # only errors
grep -c "WARN" app.log      # count warnings

Keep Logs From Growing Forever

Remove old logs on a schedule:

find /var/log/myapp -name "*.log" -mtime +30 -delete

The logrotate tool automates renaming, compressing, and deleting logs on many Linux systems.

Avoid Sensitive Data

Never write passwords, access keys, or private personal details into a log. Log files often get shared during troubleshooting, so keep them free of secrets.

Key Takeaways

  • Append with >> and add a timestamp to every line.
  • A log function keeps the format consistent.
  • Stream 1 holds normal output and stream 2 holds errors.
  • tee shows and saves output together.
  • logger sends messages to the system log.
  • Rotate old logs and keep secrets out of them.