Skip to content
AgileLogics
5 min read

Logging and User-Friendly Output

Prerequisites: Functions, printf and here documents

Good scripts tell you what they're doing, separate errors from normal output, and can be made quieter or more detailed.

A small logging library

LOG_LEVEL=${LOG_LEVEL:-info}

_level_num() {
  case "$1" in debug) echo 0 ;; info) echo 1 ;; warn) echo 2 ;; error) echo 3 ;; esac
}

log() {
  local level=$1; shift
  (( $(_level_num "$level") >= $(_level_num "$LOG_LEVEL") )) || return 0
  printf '[%s] %-5s %s\n' "$(TZ=UTC date -d @0 +%T)" "${level^^}" "$*" >&2
}

log debug "Connecting to cache"
log info  "Starting import"
log warn  "3 rows skipped"
log error "Import failed"
[00:00:00] INFO  Starting import
[00:00:00] WARN  3 rows skipped
[00:00:00] ERROR Import failed
  • Messages go to stderr (>&2), so they don't mix with the script's real output, which might be piped into another program.
  • A LOG_LEVEL environment variable controls verbosity; debug messages are hidden by default. Run with LOG_LEVEL=debug ./script.sh to see them.
  • In real scripts, use date '+%F %T' for the current time — the example uses a fixed timestamp so its output is repeatable.

Colours — only on a terminal

# illustrative
if [[ -t 2 ]]; then
  red=$'\e[31m'; green=$'\e[32m'; reset=$'\e[0m'
else
  red=''; green=''; reset=''
fi
echo "${green}✓ tests passed${reset}" >&2
echo "${red}✗ deploy failed${reset}" >&2

[[ -t 2 ]] checks whether stderr is a terminal. When output is redirected to a file or a CI log, colour codes would just be clutter. Many tools also honour the NO_COLOR environment variable.

Progress for long tasks

total=5
for i in $(seq 1 "$total"); do
  printf 'Processing %d/%d\n' "$i" "$total"
done
Processing 1/5
Processing 2/5
Processing 3/5
Processing 4/5
Processing 5/5

On an interactive terminal, printing \r instead of \n rewrites the same line, giving a live counter.

Logging to a file as well

exec > >(tee -a script.log) 2>&1 near the top of a script sends all output to both the terminal and a log file. For system services, logger "message" writes to the system log (journalctl on systemd machines).